/*! * @overview Ember - JavaScript Application Framework * @copyright Copyright 2011-2015 Tilde Inc. and contributors * Portions Copyright 2006-2011 Strobe Inc. * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE * @version 1.11.0-beta.1 */ (function() { var enifed, requireModule, eriuqer, requirejs, Ember; var mainContext = this; (function() { Ember = this.Ember = this.Ember || {}; if (typeof Ember === 'undefined') { Ember = {}; }; function UNDEFINED() { } if (typeof Ember.__loader === 'undefined') { var registry = {}; var seen = {}; enifed = function(name, deps, callback) { var value = { }; if (!callback) { value.deps = []; value.callback = deps; } else { value.deps = deps; value.callback = callback; } registry[name] = value; }; requirejs = eriuqer = requireModule = function(name) { var s = seen[name]; if (s !== undefined) { return seen[name]; } if (s === UNDEFINED) { return undefined; } seen[name] = {}; if (!registry[name]) { throw new Error('Could not find module ' + name); } var mod = registry[name]; var deps = mod.deps; var callback = mod.callback; var reified = []; var exports; var length = deps.length; for (var i=0; i 3) { args = new Array(length - 3); for (var i = 3; i < length; i++) { args[i-3] = arguments[i]; } } else { args = undefined; } if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, deferOnce: function(queueName, target, method /* , args */) { if (!method) { method = target; target = null; } if (isString(method)) { method = target[method]; } var stack = this.DEBUG ? new Error() : undefined; var length = arguments.length; var args; if (length > 3) { args = new Array(length - 3); for (var i = 3; i < length; i++) { args[i-3] = arguments[i]; } } else { args = undefined; } if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, setTimeout: function() { var l = arguments.length; var args = new Array(l); for (var x = 0; x < l; x++) { args[x] = arguments[x]; } var length = args.length, method, wait, target, methodOrTarget, methodOrWait, methodOrArgs; if (length === 0) { return; } else if (length === 1) { method = args.shift(); wait = 0; } else if (length === 2) { methodOrTarget = args[0]; methodOrWait = args[1]; if (isFunction(methodOrWait) || isFunction(methodOrTarget[methodOrWait])) { target = args.shift(); method = args.shift(); wait = 0; } else if (isCoercableNumber(methodOrWait)) { method = args.shift(); wait = args.shift(); } else { method = args.shift(); wait = 0; } } else { var last = args[args.length - 1]; if (isCoercableNumber(last)) { wait = args.pop(); } else { wait = 0; } methodOrTarget = args[0]; methodOrArgs = args[1]; if (isFunction(methodOrArgs) || (isString(methodOrArgs) && methodOrTarget !== null && methodOrArgs in methodOrTarget)) { target = args.shift(); method = args.shift(); } else { method = args.shift(); } } var executeAt = now() + parseInt(wait, 10); if (isString(method)) { method = target[method]; } var onError = getOnError(this.options); function fn() { if (onError) { try { method.apply(target, args); } catch (e) { onError(e); } } else { method.apply(target, args); } } // find position to insert var i = searchTimer(executeAt, this._timers); this._timers.splice(i, 0, executeAt, fn); updateLaterTimer(this, executeAt, wait); return fn; }, throttle: function(target, method /* , args, wait, [immediate] */) { var backburner = this; var args = arguments; var immediate = pop.call(args); var wait, throttler, index, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; immediate = true; } else { wait = pop.call(args); } wait = parseInt(wait, 10); index = findThrottler(target, method, this._throttlers); if (index > -1) { return this._throttlers[index]; } // throttled timer = global.setTimeout(function() { if (!immediate) { backburner.run.apply(backburner, args); } var index = findThrottler(target, method, backburner._throttlers); if (index > -1) { backburner._throttlers.splice(index, 1); } }, wait); if (immediate) { this.run.apply(this, args); } throttler = [target, method, timer]; this._throttlers.push(throttler); return throttler; }, debounce: function(target, method /* , args, wait, [immediate] */) { var backburner = this; var args = arguments; var immediate = pop.call(args); var wait, index, debouncee, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; immediate = false; } else { wait = pop.call(args); } wait = parseInt(wait, 10); // Remove debouncee index = findDebouncee(target, method, this._debouncees); if (index > -1) { debouncee = this._debouncees[index]; this._debouncees.splice(index, 1); clearTimeout(debouncee[2]); } timer = global.setTimeout(function() { if (!immediate) { backburner.run.apply(backburner, args); } var index = findDebouncee(target, method, backburner._debouncees); if (index > -1) { backburner._debouncees.splice(index, 1); } }, wait); if (immediate && index === -1) { backburner.run.apply(backburner, args); } debouncee = [ target, method, timer ]; backburner._debouncees.push(debouncee); return debouncee; }, cancelTimers: function() { var clearItems = function(item) { clearTimeout(item[2]); }; each(this._throttlers, clearItems); this._throttlers = []; each(this._debouncees, clearItems); this._debouncees = []; if (this._laterTimer) { clearTimeout(this._laterTimer); this._laterTimer = null; } this._timers = []; if (this._autorun) { clearTimeout(this._autorun); this._autorun = null; } }, hasTimers: function() { return !!this._timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; }, cancel: function(timer) { var timerType = typeof timer; if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); } else if (timerType === 'function') { // we're cancelling a setTimeout for (var i = 0, l = this._timers.length; i < l; i += 2) { if (this._timers[i + 1] === timer) { this._timers.splice(i, 2); // remove the two elements if (i === 0) { if (this._laterTimer) { // Active timer? Then clear timer and reset for future timer clearTimeout(this._laterTimer); this._laterTimer = null; } if (this._timers.length > 0) { // Update to next available timer when available updateLaterTimer(this, this._timers[0], this._timers[0] - now()); } } return true; } } } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce return this._cancelItem(findThrottler, this._throttlers, timer) || this._cancelItem(findDebouncee, this._debouncees, timer); } else { return; // timer was null or not a timer } }, _cancelItem: function(findMethod, array, timer){ var item, index; if (timer.length < 3) { return false; } index = findMethod(timer[0], timer[1], array); if (index > -1) { item = array[index]; if (item[2] === timer[2]) { array.splice(index, 1); clearTimeout(timer[2]); return true; } } return false; } }; Backburner.prototype.schedule = Backburner.prototype.defer; Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; Backburner.prototype.later = Backburner.prototype.setTimeout; if (needsIETryCatchFix) { var originalRun = Backburner.prototype.run; Backburner.prototype.run = wrapInTryCatch(originalRun); var originalEnd = Backburner.prototype.end; Backburner.prototype.end = wrapInTryCatch(originalEnd); } function getOnError(options) { return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); } function createAutorun(backburner) { backburner.begin(); backburner._autorun = global.setTimeout(function() { backburner._autorun = null; backburner.end(); }); } function updateLaterTimer(backburner, executeAt, wait) { var n = now(); if (!backburner._laterTimer || executeAt < backburner._laterTimerExpiresAt || backburner._laterTimerExpiresAt < n) { if (backburner._laterTimer) { // Clear when: // - Already expired // - New timer is earlier clearTimeout(backburner._laterTimer); if (backburner._laterTimerExpiresAt < n) { // If timer was never triggered // Calculate the left-over wait-time wait = Math.max(0, executeAt - n); } } backburner._laterTimer = global.setTimeout(function() { backburner._laterTimer = null; backburner._laterTimerExpiresAt = null; executeTimers(backburner); }, wait); backburner._laterTimerExpiresAt = n + wait; } } function executeTimers(backburner) { var n = now(); var fns, i, l; backburner.run(function() { i = searchTimer(n, backburner._timers); fns = backburner._timers.splice(0, i); for (i = 1, l = fns.length; i < l; i += 2) { backburner.schedule(backburner.options.defaultQueue, null, fns[i]); } }); if (backburner._timers.length) { updateLaterTimer(backburner, backburner._timers[0], backburner._timers[0] - n); } } function findDebouncee(target, method, debouncees) { return findItem(target, method, debouncees); } function findThrottler(target, method, throttlers) { return findItem(target, method, throttlers); } function findItem(target, method, collection) { var item; var index = -1; for (var i = 0, l = collection.length; i < l; i++) { item = collection[i]; if (item[0] === target && item[1] === method) { index = i; break; } } return index; } __exports__["default"] = Backburner; }); enifed("backburner.umd", ["./backburner"], function(__dependency1__) { "use strict"; var Backburner = __dependency1__["default"]; /* global define:true module:true window: true */ if (typeof enifed === 'function' && enifed.amd) { enifed(function() { return Backburner; }); } else if (typeof module !== 'undefined' && module.exports) { module.exports = Backburner; } else if (typeof this !== 'undefined') { this['Backburner'] = Backburner; } }); enifed("backburner/binary-search", ["exports"], function(__exports__) { "use strict"; __exports__["default"] = function binarySearch(time, timers) { var start = 0; var end = timers.length - 2; var middle, l; while (start < end) { // since timers is an array of pairs 'l' will always // be an integer l = (end - start) / 2; // compensate for the index in case even number // of pairs inside timers middle = start + l - (l % 2); if (time >= timers[middle]) { start = middle + 2; } else { end = middle; } } return (time >= timers[start]) ? start + 2 : start; } }); enifed("backburner/deferred-action-queues", ["./utils","./queue","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var each = __dependency1__.each; var Queue = __dependency2__["default"]; function DeferredActionQueues(queueNames, options) { var queues = this.queues = Object.create(null); this.queueNames = queueNames = queueNames || []; this.options = options; each(queueNames, function(queueName) { queues[queueName] = new Queue(queueName, options[queueName], options); }); } function noSuchQueue(name) { throw new Error("You attempted to schedule an action in a queue (" + name + ") that doesn't exist"); } DeferredActionQueues.prototype = { schedule: function(name, target, method, args, onceFlag, stack) { var queues = this.queues; var queue = queues[name]; if (!queue) { noSuchQueue(name); } if (onceFlag) { return queue.pushUnique(target, method, args, stack); } else { return queue.push(target, method, args, stack); } }, flush: function() { var queues = this.queues; var queueNames = this.queueNames; var queueName, queue, queueItems, priorQueueNameIndex; var queueNameIndex = 0; var numberOfQueues = queueNames.length; var options = this.options; while (queueNameIndex < numberOfQueues) { queueName = queueNames[queueNameIndex]; queue = queues[queueName]; var numberOfQueueItems = queue._queue.length; if (numberOfQueueItems === 0) { queueNameIndex++; } else { queue.flush(false /* async */); queueNameIndex = 0; } } } }; __exports__["default"] = DeferredActionQueues; }); enifed("backburner/platform", ["exports"], function(__exports__) { "use strict"; // In IE 6-8, try/finally doesn't work without a catch. // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug. // This tests for another broken try/catch behavior that only exhibits in the same versions of IE. var needsIETryCatchFix = (function(e,x){ try{ x(); } catch(e) { } // jshint ignore:line return !!e; })(); __exports__.needsIETryCatchFix = needsIETryCatchFix; }); enifed("backburner/queue", ["./utils","exports"], function(__dependency1__, __exports__) { "use strict"; var isString = __dependency1__.isString; function Queue(name, options, globalOptions) { this.name = name; this.globalOptions = globalOptions || {}; this.options = options; this._queue = []; this.targetQueues = Object.create(null); this._queueBeingFlushed = undefined; } Queue.prototype = { push: function(target, method, args, stack) { var queue = this._queue; queue.push(target, method, args, stack); return { queue: this, target: target, method: method }; }, pushUniqueWithoutGuid: function(target, method, args, stack) { var queue = this._queue; for (var i = 0, l = queue.length; i < l; i += 4) { var currentTarget = queue[i]; var currentMethod = queue[i+1]; if (currentTarget === target && currentMethod === method) { queue[i+2] = args; // replace args queue[i+3] = stack; // replace stack return; } } queue.push(target, method, args, stack); }, targetQueue: function(targetQueue, target, method, args, stack) { var queue = this._queue; for (var i = 0, l = targetQueue.length; i < l; i += 4) { var currentMethod = targetQueue[i]; var currentIndex = targetQueue[i + 1]; if (currentMethod === method) { queue[currentIndex + 2] = args; // replace args queue[currentIndex + 3] = stack; // replace stack return; } } targetQueue.push( method, queue.push(target, method, args, stack) - 4 ); }, pushUniqueWithGuid: function(guid, target, method, args, stack) { var hasLocalQueue = this.targetQueues[guid]; if (hasLocalQueue) { this.targetQueue(hasLocalQueue, target, method, args, stack); } else { this.targetQueues[guid] = [ method, this._queue.push(target, method, args, stack) - 4 ]; } return { queue: this, target: target, method: method }; }, pushUnique: function(target, method, args, stack) { var queue = this._queue, currentTarget, currentMethod, i, l; var KEY = this.globalOptions.GUID_KEY; if (target && KEY) { var guid = target[KEY]; if (guid) { return this.pushUniqueWithGuid(guid, target, method, args, stack); } } this.pushUniqueWithoutGuid(target, method, args, stack); return { queue: this, target: target, method: method }; }, invoke: function(target, method, args, _, _errorRecordedForStack) { if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } }, invokeWithOnError: function(target, method, args, onError, errorRecordedForStack) { try { if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } } catch(error) { onError(error, errorRecordedForStack); } }, flush: function(sync) { var queue = this._queue; var length = queue.length; if (length === 0) { return; } var globalOptions = this.globalOptions; var options = this.options; var before = options && options.before; var after = options && options.after; var onError = globalOptions.onError || (globalOptions.onErrorTarget && globalOptions.onErrorTarget[globalOptions.onErrorMethod]); var target, method, args, errorRecordedForStack; var invoke = onError ? this.invokeWithOnError : this.invoke; this.targetQueues = Object.create(null); var queueItems = this._queueBeingFlushed = this._queue.slice(); this._queue = []; if (before) { before(); } for (var i = 0; i < length; i += 4) { target = queueItems[i]; method = queueItems[i+1]; args = queueItems[i+2]; errorRecordedForStack = queueItems[i+3]; // Debugging assistance if (isString(method)) { method = target[method]; } // method could have been nullified / canceled during flush if (method) { // // ** Attention intrepid developer ** // // To find out the stack of this task when it was scheduled onto // the run loop, add the following to your app.js: // // Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production. // // Once that is in place, when you are at a breakpoint and navigate // here in the stack explorer, you can look at `errorRecordedForStack.stack`, // which will be the captured stack when this job was scheduled. // invoke(target, method, args, onError, errorRecordedForStack); } } if (after) { after(); } this._queueBeingFlushed = undefined; if (sync !== false && this._queue.length > 0) { // check if new items have been added this.flush(true); } }, cancel: function(actionToCancel) { var queue = this._queue, currentTarget, currentMethod, i, l; var target = actionToCancel.target; var method = actionToCancel.method; var GUID_KEY = this.globalOptions.GUID_KEY; if (GUID_KEY && this.targetQueues && target) { var targetQueue = this.targetQueues[target[GUID_KEY]]; if (targetQueue) { for (i = 0, l = targetQueue.length; i < l; i++) { if (targetQueue[i] === method) { targetQueue.splice(i, 1); } } } } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === target && currentMethod === method) { queue.splice(i, 4); return true; } } // if not found in current queue // could be in the queue that is being flushed queue = this._queueBeingFlushed; if (!queue) { return; } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === target && currentMethod === method) { // don't mess with array during flush // just nullify the method queue[i+1] = null; return true; } } } }; __exports__["default"] = Queue; }); enifed("backburner/utils", ["exports"], function(__exports__) { "use strict"; var NUMBER = /\d+/; function each(collection, callback) { for (var i = 0; i < collection.length; i++) { callback(collection[i]); } } __exports__.each = each;// Date.now is not available in browsers < IE9 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility var now = Date.now || function() { return new Date().getTime(); }; __exports__.now = now; function isString(suspect) { return typeof suspect === 'string'; } __exports__.isString = isString;function isFunction(suspect) { return typeof suspect === 'function'; } __exports__.isFunction = isFunction;function isNumber(suspect) { return typeof suspect === 'number'; } __exports__.isNumber = isNumber;function isCoercableNumber(number) { return isNumber(number) || NUMBER.test(number); } __exports__.isCoercableNumber = isCoercableNumber;function wrapInTryCatch(func) { return function () { try { return func.apply(this, arguments); } catch (e) { throw e; } }; } __exports__.wrapInTryCatch = wrapInTryCatch; }); enifed("calculateVersion", [], function() { "use strict"; 'use strict'; var fs = eriuqer('fs'); var path = eriuqer('path'); module.exports = function () { var packageVersion = eriuqer('../package.json').version; var output = [packageVersion]; var gitPath = path.join(__dirname,'..','.git'); var headFilePath = path.join(gitPath, 'HEAD'); if (packageVersion.indexOf('+') > -1) { try { if (fs.existsSync(headFilePath)) { var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'}); var branchName = headFile.split('/').slice(-1)[0].trim(); var refPath = headFile.split(' ')[1]; var branchSHA; if (refPath) { var branchPath = path.join(gitPath, refPath.trim()); branchSHA = fs.readFileSync(branchPath); } else { branchSHA = branchName; } output.push(branchSHA.slice(0,10)); } } catch (err) { console.error(err.stack); } return output.join('.'); } else { return packageVersion; } }; }); enifed('container.jscs-test', function () { 'use strict'; module('JSCS - .'); test('container.js should pass jscs', function() { ok(true, 'container.js should pass jscs.'); }); }); enifed('container.jshint', function () { 'use strict'; module('JSHint - .'); test('container.js should pass jshint', function() { ok(true, 'container.js should pass jshint.'); }); }); enifed('container/container.jscs-test', function () { 'use strict'; module('JSCS - container'); test('container/container.js should pass jscs', function() { ok(true, 'container/container.js should pass jscs.'); }); }); enifed('container/container.jshint', function () { 'use strict'; module('JSHint - container'); test('container/container.js should pass jshint', function() { ok(true, 'container/container.js should pass jshint.'); }); }); enifed('container/registry.jscs-test', function () { 'use strict'; module('JSCS - container'); test('container/registry.js should pass jscs', function() { ok(true, 'container/registry.js should pass jscs.'); }); }); enifed('container/registry.jshint', function () { 'use strict'; module('JSHint - container'); test('container/registry.js should pass jshint', function() { ok(true, 'container/registry.js should pass jshint.'); }); }); enifed('container/tests/container_helper', ['exports'], function (exports) { 'use strict'; var setProperties = function(object, properties) { for (var key in properties) { if (properties.hasOwnProperty(key)) { object[key] = properties[key]; } } }; var guids = 0; var factory = function() { /*jshint validthis: true */ var Klass = function(options) { setProperties(this, options); this._guid = guids++; }; Klass.prototype.constructor = Klass; Klass.prototype.destroy = function() { this.isDestroyed = true; }; Klass.prototype.toString = function() { return ""; }; Klass.create = create; Klass.extend = extend; Klass.reopen = extend; Klass.reopenClass = reopenClass; return Klass; function create(options) { return new this.prototype.constructor(options); } function reopenClass(options) { setProperties(this, options); } function extend(options) { var Child = function(options) { Klass.call(this, options); }; var Parent = this; Child.prototype = new Parent(); Child.prototype.constructor = Child; setProperties(Child, Klass); setProperties(Child.prototype, options); Child.create = create; Child.extend = extend; Child.reopen = extend; Child.reopenClass = reopenClass; return Child; } }; exports.factory = factory; exports.setProperties = setProperties; }); enifed('container/tests/container_helper.jscs-test', function () { 'use strict'; module('JSCS - container/tests'); test('container/tests/container_helper.js should pass jscs', function() { ok(true, 'container/tests/container_helper.js should pass jscs.'); }); }); enifed('container/tests/container_helper.jshint', function () { 'use strict'; module('JSHint - container/tests'); test('container/tests/container_helper.js should pass jshint', function() { ok(true, 'container/tests/container_helper.js should pass jshint.'); }); }); enifed('container/tests/container_test', ['container/tests/container_helper', 'container/registry'], function (container_helper, Registry) { 'use strict'; var originalModelInjections; QUnit.module("Container", { setup: function() { originalModelInjections = Ember.MODEL_FACTORY_INJECTIONS; }, teardown: function() { Ember.MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); QUnit.test("A registered factory returns the same instance each time", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var postController = container.lookup('controller:post'); ok(postController instanceof PostController, "The lookup is an instance of the factory"); equal(postController, container.lookup('controller:post')); }); QUnit.test("A registered factory is returned from lookupFactory", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var PostControllerFactory = container.lookupFactory('controller:post'); ok(PostControllerFactory, 'factory is returned'); ok(PostControllerFactory.create() instanceof PostController, "The return of factory.create is an instance of PostController"); }); QUnit.test("A registered factory is returned from lookupFactory is the same factory each time", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); deepEqual(container.lookupFactory('controller:post'), container.lookupFactory('controller:post'), 'The return of lookupFactory is always the same'); }); QUnit.test("A factory returned from lookupFactory has a debugkey", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var PostFactory = container.lookupFactory('controller:post'); ok(!PostFactory.container, 'factory instance receives a container'); equal(PostFactory._debugContainerKey, 'controller:post', 'factory instance receives _debugContainerKey'); }); QUnit.test("fallback for to create time injections if factory has no extend", function() { var registry = new Registry['default'](); var container = registry.container(); var AppleController = container_helper.factory(); var PostController = container_helper.factory(); PostController.extend = undefined; // remove extend registry.register('controller:apple', AppleController); registry.register('controller:post', PostController); registry.injection('controller:post', 'apple', 'controller:apple'); var postController = container.lookup('controller:post'); ok(postController.container, 'instance receives a container'); equal(postController.container, container, 'instance receives the correct container'); equal(postController._debugContainerKey, 'controller:post', 'instance receives _debugContainerKey'); ok(postController.apple instanceof AppleController, 'instance receives an apple of instance AppleController'); }); QUnit.test("The descendants of a factory returned from lookupFactory have a container and debugkey", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var instance; registry.register('controller:post', PostController); instance = container.lookupFactory('controller:post').create(); ok(instance.container, 'factory instance receives a container'); equal(instance._debugContainerKey, 'controller:post', 'factory instance receives _debugContainerKey'); ok(instance instanceof PostController, 'factory instance is instance of factory'); }); QUnit.test("A registered factory returns a fresh instance if singleton: false is passed as an option", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var postController1 = container.lookup('controller:post'); var postController2 = container.lookup('controller:post', { singleton: false }); var postController3 = container.lookup('controller:post', { singleton: false }); var postController4 = container.lookup('controller:post'); equal(postController1.toString(), postController4.toString(), "Singleton factories looked up normally return the same value"); notEqual(postController1.toString(), postController2.toString(), "Singleton factories are not equal to factories looked up with singleton: false"); notEqual(postController2.toString(), postController3.toString(), "Two factories looked up with singleton: false are not equal"); notEqual(postController3.toString(), postController4.toString(), "A singleton factory looked up after a factory called with singleton: false is not equal"); ok(postController1 instanceof PostController, "All instances are instances of the registered factory"); ok(postController2 instanceof PostController, "All instances are instances of the registered factory"); ok(postController3 instanceof PostController, "All instances are instances of the registered factory"); ok(postController4 instanceof PostController, "All instances are instances of the registered factory"); }); QUnit.test("A container lookup has access to the container", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var postController = container.lookup('controller:post'); equal(postController.container, container); }); QUnit.test("A factory type with a registered injection's instances receive that injection", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var Store = container_helper.factory(); registry.register('controller:post', PostController); registry.register('store:main', Store); registry.typeInjection('controller', 'store', 'store:main'); var postController = container.lookup('controller:post'); var store = container.lookup('store:main'); equal(postController.store, store); }); QUnit.test("An individual factory with a registered injection receives the injection", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var Store = container_helper.factory(); registry.register('controller:post', PostController); registry.register('store:main', Store); registry.injection('controller:post', 'store', 'store:main'); var postController = container.lookup('controller:post'); var store = container.lookup('store:main'); equal(store.container, container); equal(store._debugContainerKey, 'store:main'); equal(postController.container, container); equal(postController._debugContainerKey, 'controller:post'); equal(postController.store, store, 'has the correct store injected'); }); QUnit.test("A factory with both type and individual injections", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var Store = container_helper.factory(); var Router = container_helper.factory(); registry.register('controller:post', PostController); registry.register('store:main', Store); registry.register('router:main', Router); registry.injection('controller:post', 'store', 'store:main'); registry.typeInjection('controller', 'router', 'router:main'); var postController = container.lookup('controller:post'); var store = container.lookup('store:main'); var router = container.lookup('router:main'); equal(postController.store, store); equal(postController.router, router); }); QUnit.test("A factory with both type and individual factoryInjections", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var Store = container_helper.factory(); var Router = container_helper.factory(); registry.register('controller:post', PostController); registry.register('store:main', Store); registry.register('router:main', Router); registry.factoryInjection('controller:post', 'store', 'store:main'); registry.factoryTypeInjection('controller', 'router', 'router:main'); var PostControllerFactory = container.lookupFactory('controller:post'); var store = container.lookup('store:main'); var router = container.lookup('router:main'); equal(PostControllerFactory.store, store, 'PostControllerFactory has the instance of store'); equal(PostControllerFactory.router, router, 'PostControllerFactory has the route instance'); }); QUnit.test("A non-singleton instance is never cached", function() { var registry = new Registry['default'](); var container = registry.container(); var PostView = container_helper.factory(); registry.register('view:post', PostView, { singleton: false }); var postView1 = container.lookup('view:post'); var postView2 = container.lookup('view:post'); ok(postView1 !== postView2, "Non-singletons are not cached"); }); QUnit.test("A non-instantiated property is not instantiated", function() { var registry = new Registry['default'](); var container = registry.container(); var template = function() {}; registry.register('template:foo', template, { instantiate: false }); equal(container.lookup('template:foo'), template); }); QUnit.test("A failed lookup returns undefined", function() { var registry = new Registry['default'](); var container = registry.container(); equal(container.lookup('doesnot:exist'), undefined); }); QUnit.test("An invalid factory throws an error", function() { var registry = new Registry['default'](); var container = registry.container(); registry.register('controller:foo', {}); throws(function() { container.lookup('controller:foo'); }, /Failed to create an instance of \'controller:foo\'/); }); QUnit.test("Injecting a failed lookup raises an error", function() { Ember.MODEL_FACTORY_INJECTIONS = true; var registry = new Registry['default'](); var container = registry.container(); var fooInstance = {}; var fooFactory = {}; var Foo = { create: function(args) { return fooInstance; }, extend: function(args) { return fooFactory; } }; registry.register('model:foo', Foo); registry.injection('model:foo', 'store', 'store:main'); throws(function() { container.lookup('model:foo'); }); }); QUnit.test("Injecting a falsy value does not raise an error", function() { var registry = new Registry['default'](); var container = registry.container(); var ApplicationController = container_helper.factory(); registry.register('controller:application', ApplicationController); registry.register('user:current', null, { instantiate: false }); registry.injection('controller:application', 'currentUser', 'user:current'); equal(container.lookup('controller:application').currentUser, null); }); QUnit.test("Destroying the container destroys any cached singletons", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var PostView = container_helper.factory(); var template = function() {}; registry.register('controller:post', PostController); registry.register('view:post', PostView, { singleton: false }); registry.register('template:post', template, { instantiate: false }); registry.injection('controller:post', 'postView', 'view:post'); var postController = container.lookup('controller:post'); var postView = postController.postView; ok(postView instanceof PostView, "The non-singleton was injected"); container.destroy(); ok(postController.isDestroyed, "Singletons are destroyed"); ok(!postView.isDestroyed, "Non-singletons are not destroyed"); }); QUnit.test("The container can use a registry hook to resolve factories lazily", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.resolver = function(fullName) { if (fullName === 'controller:post') { return PostController; } }; var postController = container.lookup('controller:post'); ok(postController instanceof PostController, "The correct factory was provided"); }); QUnit.test("The container normalizes names before resolving", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.normalizeFullName = function(fullName) { return 'controller:post'; }; registry.register('controller:post', PostController); var postController = container.lookup('controller:normalized'); ok(postController instanceof PostController, "Normalizes the name before resolving"); }); QUnit.test("The container normalizes names when looking factory up", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); registry.normalizeFullName = function(fullName) { return 'controller:post'; }; registry.register('controller:post', PostController); var fact = container.lookupFactory('controller:normalized'); equal(fact.toString() === PostController.extend().toString(), true, "Normalizes the name when looking factory up"); }); QUnit.test("The container can get options that should be applied to a given factory", function() { var registry = new Registry['default'](); var container = registry.container(); var PostView = container_helper.factory(); registry.resolver = function(fullName) { if (fullName === 'view:post') { return PostView; } }; registry.options('view:post', { instantiate: true, singleton: false }); var postView1 = container.lookup('view:post'); var postView2 = container.lookup('view:post'); ok(postView1 instanceof PostView, "The correct factory was provided"); ok(postView2 instanceof PostView, "The correct factory was provided"); ok(postView1 !== postView2, "The two lookups are different"); }); QUnit.test("The container can get options that should be applied to all factories for a given type", function() { var registry = new Registry['default'](); var container = registry.container(); var PostView = container_helper.factory(); registry.resolver = function(fullName) { if (fullName === 'view:post') { return PostView; } }; registry.optionsForType('view', { singleton: false }); var postView1 = container.lookup('view:post'); var postView2 = container.lookup('view:post'); ok(postView1 instanceof PostView, "The correct factory was provided"); ok(postView2 instanceof PostView, "The correct factory was provided"); ok(postView1 !== postView2, "The two lookups are different"); }); QUnit.test("factory resolves are cached", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var resolveWasCalled = []; registry.resolve = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); container.lookupFactory('controller:post'); deepEqual(resolveWasCalled, ['controller:post']); container.lookupFactory('controller:post'); deepEqual(resolveWasCalled, ['controller:post']); }); QUnit.test("factory for non extendables (MODEL) resolves are cached", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = container_helper.factory(); var resolveWasCalled = []; registry.resolve = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); container.lookupFactory('model:post'); deepEqual(resolveWasCalled, ['model:post']); container.lookupFactory('model:post'); deepEqual(resolveWasCalled, ['model:post']); }); QUnit.test("factory for non extendables resolves are cached", function() { var registry = new Registry['default'](); var container = registry.container(); var PostController = {}; var resolveWasCalled = []; registry.resolve = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); container.lookupFactory('foo:post'); deepEqual(resolveWasCalled, ['foo:post']); container.lookupFactory('foo:post'); deepEqual(resolveWasCalled, ['foo:post']); }); QUnit.test("The `_onLookup` hook is called on factories when looked up the first time", function() { expect(2); var registry = new Registry['default'](); var container = registry.container(); var Apple = container_helper.factory(); Apple.reopenClass({ _onLookup: function(fullName) { equal(fullName, 'apple:main', 'calls lazy injection method with the lookup full name'); equal(this, Apple, 'calls lazy injection method in the factory context'); } }); registry.register('apple:main', Apple); container.lookupFactory('apple:main'); container.lookupFactory('apple:main'); }); QUnit.test("A factory's lazy injections are validated when first instantiated", function() { var registry = new Registry['default'](); var container = registry.container(); var Apple = container_helper.factory(); var Orange = container_helper.factory(); Apple.reopenClass({ _lazyInjections: function() { return ['orange:main', 'banana:main']; } }); registry.register('apple:main', Apple); registry.register('orange:main', Orange); throws(function() { container.lookup('apple:main'); }, /Attempting to inject an unknown injection: `banana:main`/); }); QUnit.test("Lazy injection validations are cached", function() { expect(1); var registry = new Registry['default'](); var container = registry.container(); var Apple = container_helper.factory(); var Orange = container_helper.factory(); Apple.reopenClass({ _lazyInjections: function() { ok(true, 'should call lazy injection method'); return ['orange:main']; } }); registry.register('apple:main', Apple); registry.register('orange:main', Orange); container.lookup('apple:main'); container.lookup('apple:main'); }); }); enifed('container/tests/container_test.jscs-test', function () { 'use strict'; module('JSCS - container/tests'); test('container/tests/container_test.js should pass jscs', function() { ok(true, 'container/tests/container_test.js should pass jscs.'); }); }); enifed('container/tests/container_test.jshint', function () { 'use strict'; module('JSHint - container/tests'); test('container/tests/container_test.js should pass jshint', function() { ok(true, 'container/tests/container_test.js should pass jshint.'); }); }); enifed('container/tests/registry_test', ['container/tests/container_helper', 'container'], function (container_helper, _container) { 'use strict'; var originalModelInjections; QUnit.module("Registry", { setup: function() { originalModelInjections = Ember.MODEL_FACTORY_INJECTIONS; }, teardown: function() { Ember.MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); QUnit.test("A registered factory is returned from resolve", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var PostControllerFactory = registry.resolve('controller:post'); ok(PostControllerFactory, 'factory is returned'); ok(PostControllerFactory.create() instanceof PostController, "The return of factory.create is an instance of PostController"); }); QUnit.test("The registered factory returned from resolve is the same factory each time", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); deepEqual(registry.resolve('controller:post'), registry.resolve('controller:post'), 'The return of resolve is always the same'); }); QUnit.test("A registered factory returns true for `has` if an item is registered", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); equal(registry.has('controller:post'), true, "The `has` method returned true for registered factories"); equal(registry.has('controller:posts'), false, "The `has` method returned false for unregistered factories"); }); QUnit.test("Throw exception when trying to inject `type:thing` on all type(s)", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); throws(function() { registry.typeInjection('controller', 'injected', 'controller:post'); }, 'Cannot inject a `controller:post` on other controller(s).'); }); QUnit.test("The registry can take a hook to resolve factories lazily", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.resolver = function(fullName) { if (fullName === 'controller:post') { return PostController; } }; strictEqual(registry.resolve('controller:post'), PostController, "The correct factory was provided"); }); QUnit.test("The registry respects the resolver hook for `has`", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.resolver = function(fullName) { if (fullName === 'controller:post') { return PostController; } }; ok(registry.has('controller:post'), "the `has` method uses the resolver hook"); }); QUnit.test("The registry normalizes names when resolving", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.normalizeFullName = function(fullName) { return 'controller:post'; }; registry.register('controller:post', PostController); var type = registry.resolve('controller:normalized'); strictEqual(type, PostController, "Normalizes the name when resolving"); }); QUnit.test("The registry normalizes names when checking if the factory is registered", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.normalizeFullName = function(fullName) { return 'controller:post'; }; registry.register('controller:post', PostController); var isPresent = registry.has('controller:normalized'); equal(isPresent, true, "Normalizes the name when checking if the factory or instance is present"); }); QUnit.test("validateFullName throws an error if name is incorrect", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.normalize = function(fullName) { return 'controller:post'; }; registry.register('controller:post', PostController); throws(function() { registry.resolve('post'); }, 'TypeError: Invalid Fullname, expected: `type:name` got: post'); }); QUnit.test("The registry normalizes names when injecting", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); var user = { name: 'Stef' }; registry.normalize = function(fullName) { return 'controller:post'; }; registry.register('controller:post', PostController); registry.register('user:post', user, { instantiate: false }); registry.injection('controller:post', 'user', 'controller:normalized'); deepEqual(registry.resolve('controller:post'), user, "Normalizes the name when injecting"); }); QUnit.test("cannot register an `undefined` factory", function() { var registry = new _container.Registry(); throws(function() { registry.register('controller:apple', undefined); }, ''); }); QUnit.test("can re-register a factory", function() { var registry = new _container.Registry(); var FirstApple = container_helper.factory('first'); var SecondApple = container_helper.factory('second'); registry.register('controller:apple', FirstApple); registry.register('controller:apple', SecondApple); ok(registry.resolve('controller:apple').create() instanceof SecondApple); }); QUnit.test("cannot re-register a factory if it has been resolved", function() { var registry = new _container.Registry(); var FirstApple = container_helper.factory('first'); var SecondApple = container_helper.factory('second'); registry.register('controller:apple', FirstApple); strictEqual(registry.resolve('controller:apple'), FirstApple); throws(function() { registry.register('controller:apple', SecondApple); }, 'Cannot re-register: `controller:apple`, as it has already been resolved.'); strictEqual(registry.resolve('controller:apple'), FirstApple); }); QUnit.test('registry.has should not accidentally cause injections on that factory to be run. (Mitigate merely on observing)', function() { expect(1); var registry = new _container.Registry(); var FirstApple = container_helper.factory('first'); var SecondApple = container_helper.factory('second'); SecondApple.extend = function(a, b, c) { ok(false, 'should not extend or touch the injected model, merely to inspect existence of another'); }; registry.register('controller:apple', FirstApple); registry.register('controller:second-apple', SecondApple); registry.injection('controller:apple', 'badApple', 'controller:second-apple'); ok(registry.has('controller:apple')); }); QUnit.test('once resolved, always return the same result', function() { expect(1); var registry = new _container.Registry(); registry.resolver = function() { return 'bar'; }; var Bar = registry.resolve('models:bar'); registry.resolver = function() { return 'not bar'; }; equal(registry.resolve('models:bar'), Bar); }); QUnit.test("factory resolves are cached", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); var resolveWasCalled = []; registry.resolver = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); registry.resolve('controller:post'); deepEqual(resolveWasCalled, ['controller:post']); registry.resolve('controller:post'); deepEqual(resolveWasCalled, ['controller:post']); }); QUnit.test("factory for non extendables (MODEL) resolves are cached", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); var resolveWasCalled = []; registry.resolver = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); registry.resolve('model:post'); deepEqual(resolveWasCalled, ['model:post']); registry.resolve('model:post'); deepEqual(resolveWasCalled, ['model:post']); }); QUnit.test("factory for non extendables resolves are cached", function() { var registry = new _container.Registry(); var PostController = {}; var resolveWasCalled = []; registry.resolver = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); registry.resolve('foo:post'); deepEqual(resolveWasCalled, ['foo:post']); registry.resolve('foo:post'); deepEqual(resolveWasCalled, ['foo:post']); }); QUnit.test("registry.container creates an associated container", function() { var registry = new _container.Registry(); var PostController = container_helper.factory(); registry.register('controller:post', PostController); var container = registry.container(); var postController = container.lookup('controller:post'); ok(postController instanceof PostController, "The lookup is an instance of the registered factory"); strictEqual(registry._defaultContainer, container, "_defaultContainer is set to the first created container and used for Ember 1.x Container compatibility"); }); QUnit.test("`resolve` can be handled by a fallback registry", function() { var fallback = new _container.Registry(); var registry = new _container.Registry({ fallback: fallback }); var PostController = container_helper.factory(); fallback.register('controller:post', PostController); var PostControllerFactory = registry.resolve('controller:post'); ok(PostControllerFactory, 'factory is returned'); ok(PostControllerFactory.create() instanceof PostController, "The return of factory.create is an instance of PostController"); }); QUnit.test("`has` can be handled by a fallback registry", function() { var fallback = new _container.Registry(); var registry = new _container.Registry({ fallback: fallback }); var PostController = container_helper.factory(); fallback.register('controller:post', PostController); equal(registry.has('controller:post'), true, "Fallback registry is checked for registration"); }); QUnit.test("`getInjections` includes injections from a fallback registry", function() { var fallback = new _container.Registry(); var registry = new _container.Registry({ fallback: fallback }); equal(registry.getInjections('model:user').length, 0, "No injections in the primary registry"); fallback.injection('model:user', 'post', 'model:post'); equal(registry.getInjections('model:user').length, 1, "Injections from the fallback registry are merged"); }); QUnit.test("`getTypeInjections` includes type injections from a fallback registry", function() { var fallback = new _container.Registry(); var registry = new _container.Registry({ fallback: fallback }); equal(registry.getTypeInjections('model').length, 0, "No injections in the primary registry"); fallback.injection('model', 'source', 'source:main'); equal(registry.getTypeInjections('model').length, 1, "Injections from the fallback registry are merged"); }); QUnit.test("`getFactoryInjections` includes factory injections from a fallback registry", function() { var fallback = new _container.Registry(); var registry = new _container.Registry({ fallback: fallback }); equal(registry.getFactoryInjections('model:user').length, 0, "No factory injections in the primary registry"); fallback.factoryInjection('model:user', 'store', 'store:main'); equal(registry.getFactoryInjections('model:user').length, 1, "Factory injections from the fallback registry are merged"); }); QUnit.test("`getFactoryTypeInjections` includes factory type injections from a fallback registry", function() { var fallback = new _container.Registry(); var registry = new _container.Registry({ fallback: fallback }); equal(registry.getFactoryTypeInjections('model').length, 0, "No factory type injections in the primary registry"); fallback.factoryInjection('model', 'store', 'store:main'); equal(registry.getFactoryTypeInjections('model').length, 1, "Factory type injections from the fallback registry are merged"); }); }); enifed('container/tests/registry_test.jscs-test', function () { 'use strict'; module('JSCS - container/tests'); test('container/tests/registry_test.js should pass jscs', function() { ok(true, 'container/tests/registry_test.js should pass jscs.'); }); }); enifed('container/tests/registry_test.jshint', function () { 'use strict'; module('JSHint - container/tests'); test('container/tests/registry_test.js should pass jshint', function() { ok(true, 'container/tests/registry_test.js should pass jshint.'); }); }); enifed("dag-map", ["exports"], function(__exports__) { "use strict"; function visit(vertex, fn, visited, path) { var name = vertex.name; var vertices = vertex.incoming; var names = vertex.incomingNames; var len = names.length; var i; if (!visited) { visited = {}; } if (!path) { path = []; } if (visited.hasOwnProperty(name)) { return; } path.push(name); visited[name] = true; for (i = 0; i < len; i++) { visit(vertices[names[i]], fn, visited, path); } fn(vertex, path); path.pop(); } /** * DAG stands for Directed acyclic graph. * * It is used to build a graph of dependencies checking that there isn't circular * dependencies. p.e Registering initializers with a certain precedence order. * * @class DAG * @constructor */ function DAG() { this.names = []; this.vertices = Object.create(null); } /** * DAG Vertex * * @class Vertex * @constructor */ function Vertex(name) { this.name = name; this.incoming = {}; this.incomingNames = []; this.hasOutgoing = false; this.value = null; } /** * Adds a vertex entry to the graph unless it is already added. * * @private * @method add * @param {String} name The name of the vertex to add */ DAG.prototype.add = function(name) { if (!name) { throw new Error("Can't add Vertex without name"); } if (this.vertices[name] !== undefined) { return this.vertices[name]; } var vertex = new Vertex(name); this.vertices[name] = vertex; this.names.push(name); return vertex; }; /** * Adds a vertex to the graph and sets its value. * * @private * @method map * @param {String} name The name of the vertex. * @param value The value to put in the vertex. */ DAG.prototype.map = function(name, value) { this.add(name).value = value; }; /** * Connects the vertices with the given names, adding them to the graph if * necessary, only if this does not produce is any circular dependency. * * @private * @method addEdge * @param {String} fromName The name the vertex where the edge starts. * @param {String} toName The name the vertex where the edge ends. */ DAG.prototype.addEdge = function(fromName, toName) { if (!fromName || !toName || fromName === toName) { return; } var from = this.add(fromName); var to = this.add(toName); if (to.incoming.hasOwnProperty(fromName)) { return; } function checkCycle(vertex, path) { if (vertex.name === toName) { throw new Error("cycle detected: " + toName + " <- " + path.join(" <- ")); } } visit(from, checkCycle); from.hasOutgoing = true; to.incoming[fromName] = from; to.incomingNames.push(fromName); }; /** * Visits all the vertex of the graph calling the given function with each one, * ensuring that the vertices are visited respecting their precedence. * * @method topsort * @param {Function} fn The function to be invoked on each vertex. */ DAG.prototype.topsort = function(fn) { var visited = {}; var vertices = this.vertices; var names = this.names; var len = names.length; var i, vertex; for (i = 0; i < len; i++) { vertex = vertices[names[i]]; if (!vertex.hasOutgoing) { visit(vertex, fn, visited); } } }; /** * Adds a vertex with the given name and value to the graph and joins it with the * vertices referenced in _before_ and _after_. If there isn't vertices with those * names, they are added too. * * If either _before_ or _after_ are falsy/empty, the added vertex will not have * an incoming/outgoing edge. * * @method addEdges * @param {String} name The name of the vertex to be added. * @param value The value of that vertex. * @param before An string or array of strings with the names of the vertices before * which this vertex must be visited. * @param after An string or array of strings with the names of the vertex after * which this vertex must be visited. * */ DAG.prototype.addEdges = function(name, value, before, after) { var i; this.map(name, value); if (before) { if (typeof before === 'string') { this.addEdge(name, before); } else { for (i = 0; i < before.length; i++) { this.addEdge(name, before[i]); } } } if (after) { if (typeof after === 'string') { this.addEdge(after, name); } else { for (i = 0; i < after.length; i++) { this.addEdge(after[i], name); } } } }; __exports__["default"] = DAG; }); enifed("dag-map.umd", ["./dag-map"], function(__dependency1__) { "use strict"; var DAG = __dependency1__["default"]; /* global define:true module:true window: true */ if (typeof enifed === 'function' && enifed.amd) { enifed(function() { return DAG; }); } else if (typeof module !== 'undefined' && module.exports) { module.exports = DAG; } else if (typeof this !== 'undefined') { this['DAG'] = DAG; } }); enifed("dom-helper", ["./morph-range","./morph-attr","./dom-helper/build-html-dom","./dom-helper/classes","./dom-helper/prop","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { "use strict"; var Morph = __dependency1__["default"]; var AttrMorph = __dependency2__["default"]; var buildHTMLDOM = __dependency3__.buildHTMLDOM; var svgNamespace = __dependency3__.svgNamespace; var svgHTMLIntegrationPoints = __dependency3__.svgHTMLIntegrationPoints; var addClasses = __dependency4__.addClasses; var removeClasses = __dependency4__.removeClasses; var normalizeProperty = __dependency5__.normalizeProperty; var isAttrRemovalValue = __dependency5__.isAttrRemovalValue; var doc = typeof document === 'undefined' ? false : document; var deletesBlankTextNodes = doc && (function(document){ var element = document.createElement('div'); element.appendChild( document.createTextNode('') ); var clonedElement = element.cloneNode(true); return clonedElement.childNodes.length === 0; })(doc); var ignoresCheckedAttribute = doc && (function(document){ var element = document.createElement('input'); element.setAttribute('checked', 'checked'); var clonedElement = element.cloneNode(false); return !clonedElement.checked; })(doc); var canRemoveSvgViewBoxAttribute = doc && (doc.createElementNS ? (function(document){ var element = document.createElementNS(svgNamespace, 'svg'); element.setAttribute('viewBox', '0 0 100 100'); element.removeAttribute('viewBox'); return !element.getAttribute('viewBox'); })(doc) : true); var canClone = doc && (function(document){ var element = document.createElement('div'); element.appendChild( document.createTextNode(' ')); element.appendChild( document.createTextNode(' ')); var clonedElement = element.cloneNode(true); return clonedElement.childNodes[0].nodeValue === ' '; })(doc); // This is not the namespace of the element, but of // the elements inside that elements. function interiorNamespace(element){ if ( element && element.namespaceURI === svgNamespace && !svgHTMLIntegrationPoints[element.tagName] ) { return svgNamespace; } else { return null; } } // The HTML spec allows for "omitted start tags". These tags are optional // when their intended child is the first thing in the parent tag. For // example, this is a tbody start tag: // // // // // // The tbody may be omitted, and the browser will accept and render: // //
// // // However, the omitted start tag will still be added to the DOM. Here // we test the string and context to see if the browser is about to // perform this cleanup. // // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags // describes which tags are omittable. The spec for tbody and colgroup // explains this behavior: // // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element // var omittedStartTagChildTest = /<([\w:]+)/; function detectOmittedStartTag(string, contextualElement){ // Omitted start tags are only inside table tags. if (contextualElement.tagName === 'TABLE') { var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); if (omittedStartTagChildMatch) { var omittedStartTagChild = omittedStartTagChildMatch[1]; // It is already asserted that the contextual element is a table // and not the proper start tag. Just see if a tag was omitted. return omittedStartTagChild === 'tr' || omittedStartTagChild === 'col'; } } } function buildSVGDOM(html, dom){ var div = dom.document.createElement('div'); div.innerHTML = ''+html+''; return div.firstChild.childNodes; } /* * A class wrapping DOM functions to address environment compatibility, * namespaces, contextual elements for morph un-escaped content * insertion. * * When entering a template, a DOMHelper should be passed: * * template(context, { hooks: hooks, dom: new DOMHelper() }); * * TODO: support foreignObject as a passed contextual element. It has * a namespace (svg) that does not match its internal namespace * (xhtml). * * @class DOMHelper * @constructor * @param {HTMLDocument} _document The document DOM methods are proxied to */ function DOMHelper(_document){ this.document = _document || document; if (!this.document) { throw new Error("A document object must be passed to the DOMHelper, or available on the global scope"); } this.canClone = canClone; this.namespace = null; } var prototype = DOMHelper.prototype; prototype.constructor = DOMHelper; prototype.getElementById = function(id, rootNode) { rootNode = rootNode || this.document; return rootNode.getElementById(id); }; prototype.insertBefore = function(element, childElement, referenceChild) { return element.insertBefore(childElement, referenceChild); }; prototype.appendChild = function(element, childElement) { return element.appendChild(childElement); }; prototype.childAt = function(element, indices) { var child = element; for (var i = 0; i < indices.length; i++) { child = child.childNodes.item(indices[i]); } return child; }; // Note to a Fellow Implementor: // Ahh, accessing a child node at an index. Seems like it should be so simple, // doesn't it? Unfortunately, this particular method has caused us a surprising // amount of pain. As you'll note below, this method has been modified to walk // the linked list of child nodes rather than access the child by index // directly, even though there are two (2) APIs in the DOM that do this for us. // If you're thinking to yourself, "What an oversight! What an opportunity to // optimize this code!" then to you I say: stop! For I have a tale to tell. // // First, this code must be compatible with simple-dom for rendering on the // server where there is no real DOM. Previously, we accessed a child node // directly via `element.childNodes[index]`. While we *could* in theory do a // full-fidelity simulation of a live `childNodes` array, this is slow, // complicated and error-prone. // // "No problem," we thought, "we'll just use the similar // `childNodes.item(index)` API." Then, we could just implement our own `item` // method in simple-dom and walk the child node linked list there, allowing // us to retain the performance advantages of the (surely optimized) `item()` // API in the browser. // // Unfortunately, an enterprising soul named Samy Alzahrani discovered that in // IE8, accessing an item out-of-bounds via `item()` causes an exception where // other browsers return null. This necessitated a... check of // `childNodes.length`, bringing us back around to having to support a // full-fidelity `childNodes` array! // // Worst of all, Kris Selden investigated how browsers are actualy implemented // and discovered that they're all linked lists under the hood anyway. Accessing // `childNodes` requires them to allocate a new live collection backed by that // linked list, which is itself a rather expensive operation. Our assumed // optimization had backfired! That is the danger of magical thinking about // the performance of native implementations. // // And this, my friends, is why the following implementation just walks the // linked list, as surprised as that may make you. Please ensure you understand // the above before changing this and submitting a PR. // // Tom Dale, January 18th, 2015, Portland OR prototype.childAtIndex = function(element, index) { var node = element.firstChild; for (var idx = 0; node && idx < index; idx++) { node = node.nextSibling; } return node; }; prototype.appendText = function(element, text) { return element.appendChild(this.document.createTextNode(text)); }; prototype.setAttribute = function(element, name, value) { element.setAttribute(name, String(value)); }; prototype.setAttributeNS = function(element, namespace, name, value) { element.setAttributeNS(namespace, name, String(value)); }; if (canRemoveSvgViewBoxAttribute){ prototype.removeAttribute = function(element, name) { element.removeAttribute(name); }; } else { prototype.removeAttribute = function(element, name) { if (element.tagName === 'svg' && name === 'viewBox') { element.setAttribute(name, null); } else { element.removeAttribute(name); } }; } prototype.setPropertyStrict = function(element, name, value) { element[name] = value; }; prototype.setProperty = function(element, name, value, namespace) { var lowercaseName = name.toLowerCase(); if (element.namespaceURI === svgNamespace || lowercaseName === 'style') { if (isAttrRemovalValue(value)) { element.removeAttribute(name); } else { if (namespace) { element.setAttributeNS(namespace, name, value); } else { element.setAttribute(name, value); } } } else { var normalized = normalizeProperty(element, name); if (normalized) { element[normalized] = value; } else { if (isAttrRemovalValue(value)) { element.removeAttribute(name); } else { if (namespace && element.setAttributeNS) { element.setAttributeNS(namespace, name, value); } else { element.setAttribute(name, value); } } } } }; if (doc && doc.createElementNS) { // Only opt into namespace detection if a contextualElement // is passed. prototype.createElement = function(tagName, contextualElement) { var namespace = this.namespace; if (contextualElement) { if (tagName === 'svg') { namespace = svgNamespace; } else { namespace = interiorNamespace(contextualElement); } } if (namespace) { return this.document.createElementNS(namespace, tagName); } else { return this.document.createElement(tagName); } }; prototype.setAttributeNS = function(element, namespace, name, value) { element.setAttributeNS(namespace, name, String(value)); }; } else { prototype.createElement = function(tagName) { return this.document.createElement(tagName); }; prototype.setAttributeNS = function(element, namespace, name, value) { element.setAttribute(name, String(value)); }; } prototype.addClasses = addClasses; prototype.removeClasses = removeClasses; prototype.setNamespace = function(ns) { this.namespace = ns; }; prototype.detectNamespace = function(element) { this.namespace = interiorNamespace(element); }; prototype.createDocumentFragment = function(){ return this.document.createDocumentFragment(); }; prototype.createTextNode = function(text){ return this.document.createTextNode(text); }; prototype.createComment = function(text){ return this.document.createComment(text); }; prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { for (var i=0, len=blankChildTextNodes.length;i]*selected/; return function detectAutoSelectedOption(select, option, html) { //jshint ignore:line return select.selectedIndex === 0 && !detectAutoSelectedOptionRegex.test(html); }; })(); } else { detectAutoSelectedOption = function detectAutoSelectedOption(select, option, html) { //jshint ignore:line var selectedAttribute = option.getAttribute('selected'); return select.selectedIndex === 0 && ( selectedAttribute === null || ( selectedAttribute !== '' && selectedAttribute.toLowerCase() !== 'selected' ) ); }; } var tagNamesRequiringInnerHTMLFix = doc && (function(document) { var tagNamesRequiringInnerHTMLFix; // IE 9 and earlier don't allow us to set innerHTML on col, colgroup, frameset, // html, style, table, tbody, tfoot, thead, title, tr. Detect this and add // them to an initial list of corrected tags. // // Here we are only dealing with the ones which can have child nodes. // var tableNeedsInnerHTMLFix; var tableInnerHTMLTestElement = document.createElement('table'); try { tableInnerHTMLTestElement.innerHTML = ''; } catch (e) { } finally { tableNeedsInnerHTMLFix = (tableInnerHTMLTestElement.childNodes.length === 0); } if (tableNeedsInnerHTMLFix) { tagNamesRequiringInnerHTMLFix = { colgroup: ['table'], table: [], tbody: ['table'], tfoot: ['table'], thead: ['table'], tr: ['table', 'tbody'] }; } // IE 8 doesn't allow setting innerHTML on a select tag. Detect this and // add it to the list of corrected tags. // var selectInnerHTMLTestElement = document.createElement('select'); selectInnerHTMLTestElement.innerHTML = ''; if (!selectInnerHTMLTestElement.childNodes[0]) { tagNamesRequiringInnerHTMLFix = tagNamesRequiringInnerHTMLFix || {}; tagNamesRequiringInnerHTMLFix.select = []; } return tagNamesRequiringInnerHTMLFix; })(doc); function scriptSafeInnerHTML(element, html) { // without a leading text node, IE will drop a leading script tag. html = '­'+html; element.innerHTML = html; var nodes = element.childNodes; // Look for ­ to remove it. var shyElement = nodes[0]; while (shyElement.nodeType === 1 && !shyElement.nodeName) { shyElement = shyElement.firstChild; } // At this point it's the actual unicode character. if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { var newValue = shyElement.nodeValue.slice(1); if (newValue.length) { shyElement.nodeValue = shyElement.nodeValue.slice(1); } else { shyElement.parentNode.removeChild(shyElement); } } return nodes; } function buildDOMWithFix(html, contextualElement){ var tagName = contextualElement.tagName; // Firefox versions < 11 do not have support for element.outerHTML. var outerHTML = contextualElement.outerHTML || new XMLSerializer().serializeToString(contextualElement); if (!outerHTML) { throw "Can't set innerHTML on "+tagName+" in this browser"; } var wrappingTags = tagNamesRequiringInnerHTMLFix[tagName.toLowerCase()]; var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0]; var endTag = ''; var wrappedHTML = [startTag, html, endTag]; var i = wrappingTags.length; var wrappedDepth = 1 + i; while(i--) { wrappedHTML.unshift('<'+wrappingTags[i]+'>'); wrappedHTML.push(''); } var wrapper = document.createElement('div'); scriptSafeInnerHTML(wrapper, wrappedHTML.join('')); var element = wrapper; while (wrappedDepth--) { element = element.firstChild; while (element && element.nodeType !== 1) { element = element.nextSibling; } } while (element && element.tagName !== tagName) { element = element.nextSibling; } return element ? element.childNodes : []; } var buildDOM; if (needsShy) { buildDOM = function buildDOM(html, contextualElement, dom){ contextualElement = dom.cloneNode(contextualElement, false); scriptSafeInnerHTML(contextualElement, html); return contextualElement.childNodes; }; } else { buildDOM = function buildDOM(html, contextualElement, dom){ contextualElement = dom.cloneNode(contextualElement, false); contextualElement.innerHTML = html; return contextualElement.childNodes; }; } var buildIESafeDOM; if (tagNamesRequiringInnerHTMLFix || movesWhitespace) { buildIESafeDOM = function buildIESafeDOM(html, contextualElement, dom) { // Make a list of the leading text on script nodes. Include // script tags without any whitespace for easier processing later. var spacesBefore = []; var spacesAfter = []; if (typeof html === 'string') { html = html.replace(/(\s*)()(\s*)/g, function(match, tag, spaces) { spacesAfter.push(spaces); return tag; }); } // Fetch nodes var nodes; if (tagNamesRequiringInnerHTMLFix[contextualElement.tagName.toLowerCase()]) { // buildDOMWithFix uses string wrappers for problematic innerHTML. nodes = buildDOMWithFix(html, contextualElement); } else { nodes = buildDOM(html, contextualElement, dom); } // Build a list of script tags, the nodes themselves will be // mutated as we add test nodes. var i, j, node, nodeScriptNodes; var scriptNodes = []; for (i=0;i 0) { textNode = dom.document.createTextNode(spaceBefore); scriptNode.parentNode.insertBefore(textNode, scriptNode); } spaceAfter = spacesAfter[i]; if (spaceAfter && spaceAfter.length > 0) { textNode = dom.document.createTextNode(spaceAfter); scriptNode.parentNode.insertBefore(textNode, scriptNode.nextSibling); } } return nodes; }; } else { buildIESafeDOM = buildDOM; } // When parsing innerHTML, the browser may set up DOM with some things // not desired. For example, with a select element context and option // innerHTML the first option will be marked selected. // // This method cleans up some of that, resetting those values back to // their defaults. // function buildSafeDOM(html, contextualElement, dom) { var childNodes = buildIESafeDOM(html, contextualElement, dom); if (contextualElement.tagName === 'SELECT') { // Walk child nodes for (var i = 0; childNodes[i]; i++) { // Find and process the first option child node if (childNodes[i].tagName === 'OPTION') { if (detectAutoSelectedOption(childNodes[i].parentNode, childNodes[i], html)) { // If the first node is selected but does not have an attribute, // presume it is not really selected. childNodes[i].parentNode.selectedIndex = -1; } break; } } } return childNodes; } var buildHTMLDOM; if (needsIntegrationPointFix) { buildHTMLDOM = function buildHTMLDOM(html, contextualElement, dom){ if (svgHTMLIntegrationPoints[contextualElement.tagName]) { return buildSafeDOM(html, document.createElement('div'), dom); } else { return buildSafeDOM(html, contextualElement, dom); } }; } else { buildHTMLDOM = buildSafeDOM; } __exports__.buildHTMLDOM = buildHTMLDOM; }); enifed("dom-helper/classes", ["exports"], function(__exports__) { "use strict"; var doc = typeof document === 'undefined' ? false : document; // PhantomJS has a broken classList. See https://github.com/ariya/phantomjs/issues/12782 var canClassList = doc && (function(){ var d = document.createElement('div'); if (!d.classList) { return false; } d.classList.add('boo'); d.classList.add('boo', 'baz'); return (d.className === 'boo baz'); })(); function buildClassList(element) { var classString = (element.getAttribute('class') || ''); return classString !== '' && classString !== ' ' ? classString.split(' ') : []; } function intersect(containingArray, valuesArray) { var containingIndex = 0; var containingLength = containingArray.length; var valuesIndex = 0; var valuesLength = valuesArray.length; var intersection = new Array(valuesLength); // TODO: rewrite this loop in an optimal manner for (;containingIndex 0 ? existingClasses.join(' ') : ''); } } function removeClassesViaAttribute(element, classNames) { var existingClasses = buildClassList(element); var indexes = intersect(classNames, existingClasses); var didChange = false; var newClasses = []; for (var i=0, l=existingClasses.length; i 0 ? newClasses.join(' ') : ''); } } var addClasses, removeClasses; if (canClassList) { addClasses = function addClasses(element, classNames) { if (element.classList) { if (classNames.length === 1) { element.classList.add(classNames[0]); } else if (classNames.length === 2) { element.classList.add(classNames[0], classNames[1]); } else { element.classList.add.apply(element.classList, classNames); } } else { addClassesViaAttribute(element, classNames); } }; removeClasses = function removeClasses(element, classNames) { if (element.classList) { if (classNames.length === 1) { element.classList.remove(classNames[0]); } else if (classNames.length === 2) { element.classList.remove(classNames[0], classNames[1]); } else { element.classList.remove.apply(element.classList, classNames); } } else { removeClassesViaAttribute(element, classNames); } }; } else { addClasses = addClassesViaAttribute; removeClasses = removeClassesViaAttribute; } __exports__.addClasses = addClasses; __exports__.removeClasses = removeClasses; }); enifed("dom-helper/prop", ["exports"], function(__exports__) { "use strict"; function isAttrRemovalValue(value) { return value === null || value === undefined; } __exports__.isAttrRemovalValue = isAttrRemovalValue;// TODO should this be an o_create kind of thing? var propertyCaches = {}; __exports__.propertyCaches = propertyCaches; function normalizeProperty(element, attrName) { var tagName = element.tagName; var key; var cache = propertyCaches[tagName]; if (!cache) { // TODO should this be an o_create kind of thing? cache = {}; for (key in element) { cache[key.toLowerCase()] = key; } propertyCaches[tagName] = cache; } // presumes that the attrName has been lowercased. return cache[attrName]; } __exports__.normalizeProperty = normalizeProperty; }); enifed('ember-application.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-application.js should pass jscs', function() { ok(true, 'ember-application.js should pass jscs.'); }); }); enifed('ember-application.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-application.js should pass jshint', function() { ok(true, 'ember-application.js should pass jshint.'); }); }); enifed('ember-application/ext/controller.jscs-test', function () { 'use strict'; module('JSCS - ember-application/ext'); test('ember-application/ext/controller.js should pass jscs', function() { ok(true, 'ember-application/ext/controller.js should pass jscs.'); }); }); enifed('ember-application/ext/controller.jshint', function () { 'use strict'; module('JSHint - ember-application/ext'); test('ember-application/ext/controller.js should pass jshint', function() { ok(true, 'ember-application/ext/controller.js should pass jshint.'); }); }); enifed('ember-application/system/application-instance.jscs-test', function () { 'use strict'; module('JSCS - ember-application/system'); test('ember-application/system/application-instance.js should pass jscs', function() { ok(true, 'ember-application/system/application-instance.js should pass jscs.'); }); }); enifed('ember-application/system/application-instance.jshint', function () { 'use strict'; module('JSHint - ember-application/system'); test('ember-application/system/application-instance.js should pass jshint', function() { ok(true, 'ember-application/system/application-instance.js should pass jshint.'); }); }); enifed('ember-application/system/application.jscs-test', function () { 'use strict'; module('JSCS - ember-application/system'); test('ember-application/system/application.js should pass jscs', function() { ok(true, 'ember-application/system/application.js should pass jscs.'); }); }); enifed('ember-application/system/application.jshint', function () { 'use strict'; module('JSHint - ember-application/system'); test('ember-application/system/application.js should pass jshint', function() { ok(true, 'ember-application/system/application.js should pass jshint.'); }); }); enifed('ember-application/system/resolver.jscs-test', function () { 'use strict'; module('JSCS - ember-application/system'); test('ember-application/system/resolver.js should pass jscs', function() { ok(true, 'ember-application/system/resolver.js should pass jscs.'); }); }); enifed('ember-application/system/resolver.jshint', function () { 'use strict'; module('JSHint - ember-application/system'); test('ember-application/system/resolver.js should pass jshint', function() { ok(true, 'ember-application/system/resolver.js should pass jshint.'); }); }); enifed('ember-application/tests/system/application_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-application/system/application', 'ember-application/system/resolver', 'ember-routing/system/router', 'ember-views/views/view', 'ember-runtime/controllers/controller', 'ember-routing/location/none_location', 'ember-runtime/system/object', 'ember-views/system/jquery', 'ember-template-compiler/system/compile'], function (Ember, run, Application, DefaultResolver, Router, View, Controller, NoneLocation, EmberObject, jQuery, compile) { 'use strict'; /*globals EmberDev */ var trim = jQuery['default'].trim; var app, application, originalLookup, originalDebug; QUnit.module("Ember.Application", { setup: function() { originalLookup = Ember['default'].lookup; originalDebug = Ember['default'].debug; jQuery['default']("#qunit-fixture").html("
HI
HI
"); run['default'](function() { application = Application['default'].create({ rootElement: '#one', router: null }); }); }, teardown: function() { jQuery['default']("#qunit-fixture").empty(); Ember['default'].debug = originalDebug; Ember['default'].lookup = originalLookup; if (application) { run['default'](application, 'destroy'); } if (app) { run['default'](app, 'destroy'); } } }); QUnit.test("you can make a new application in a non-overlapping element", function() { run['default'](function() { app = Application['default'].create({ rootElement: '#two', router: null }); }); run['default'](app, 'destroy'); ok(true, "should not raise"); }); QUnit.test("you cannot make a new application that is a parent of an existing application", function() { expectAssertion(function() { run['default'](function() { Application['default'].create({ rootElement: '#qunit-fixture' }); }); }); }); QUnit.test("you cannot make a new application that is a descendent of an existing application", function() { expectAssertion(function() { run['default'](function() { Application['default'].create({ rootElement: '#one-child' }); }); }); }); QUnit.test("you cannot make a new application that is a duplicate of an existing application", function() { expectAssertion(function() { run['default'](function() { Application['default'].create({ rootElement: '#one' }); }); }); }); QUnit.test("you cannot make two default applications without a rootElement error", function() { expectAssertion(function() { run['default'](function() { Application['default'].create({ router: false }); }); }); }); QUnit.test("acts like a namespace", function() { var lookup = Ember['default'].lookup = {}; var app; run['default'](function() { app = lookup.TestApp = Application['default'].create({ rootElement: '#two', router: false }); }); Ember['default'].BOOTED = false; app.Foo = EmberObject['default'].extend(); equal(app.Foo.toString(), "TestApp.Foo", "Classes pick up their parent namespace"); }); QUnit.module("Ember.Application initialization", { teardown: function() { if (app) { run['default'](app, 'destroy'); } Ember['default'].TEMPLATES = {}; } }); QUnit.test('initialized application go to initial route', function() { run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); app.register('template:application', compile['default']("{{outlet}}") ); Ember['default'].TEMPLATES.index = compile['default']( "

Hi from index

" ); }); equal(jQuery['default']('#qunit-fixture h1').text(), "Hi from index"); }); QUnit.test("initialize application via initialize call", function() { run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); app.ApplicationView = View['default'].extend({ template: function() { return "

Hello!

"; } }); }); // This is not a public way to access the container; we just // need to make some assertions about the created router var router = app.__container__.lookup('router:main'); equal(router instanceof Router['default'], true, "Router was set from initialize call"); equal(router.location instanceof NoneLocation['default'], true, "Location was set from location implementation name"); }); QUnit.test("initialize application with stateManager via initialize call from Router class", function() { run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); app.register('template:application', function() { return "

Hello!

"; }); }); var router = app.__container__.lookup('router:main'); equal(router instanceof Router['default'], true, "Router was set from initialize call"); equal(jQuery['default']("#qunit-fixture h1").text(), "Hello!"); }); QUnit.test("ApplicationView is inserted into the page", function() { run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); app.ApplicationView = View['default'].extend({ render: function(buffer) { buffer.push("

Hello!

"); } }); app.ApplicationController = Controller['default'].extend(); app.Router.reopen({ location: 'none' }); }); equal(jQuery['default']("#qunit-fixture h1").text(), "Hello!"); }); QUnit.test("Minimal Application initialized with just an application template", function() { jQuery['default']('#qunit-fixture').html(''); run['default'](function () { app = Application['default'].create({ rootElement: '#qunit-fixture' }); }); equal(trim(jQuery['default']('#qunit-fixture').text()), 'Hello World'); }); QUnit.test('enable log of libraries with an ENV var', function() { if (EmberDev && EmberDev.runningProdBuild) { ok(true, 'Logging does not occur in production builds'); return; } var debug = Ember['default'].debug; var messages = []; Ember['default'].LOG_VERSION = true; Ember['default'].debug = function(message) { messages.push(message); }; Ember['default'].libraries.register("my-lib", "2.0.0a"); run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); }); equal(messages[1], "Ember : " + Ember['default'].VERSION); equal(messages[2], "jQuery : " + jQuery['default']().jquery); equal(messages[3], "my-lib : " + "2.0.0a"); Ember['default'].libraries.deRegister("my-lib"); Ember['default'].LOG_VERSION = false; Ember['default'].debug = debug; }); QUnit.test('disable log version of libraries with an ENV var', function() { var logged = false; Ember['default'].LOG_VERSION = false; Ember['default'].debug = function(message) { logged = true; }; jQuery['default']("#qunit-fixture").empty(); run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); }); ok(!logged, 'library version logging skipped'); }); QUnit.test("can resolve custom router", function() { var CustomRouter = Router['default'].extend(); var CustomResolver = DefaultResolver['default'].extend({ resolveMain: function(parsedName) { if (parsedName.type === "router") { return CustomRouter; } else { return this._super(parsedName); } } }); app = run['default'](function() { return Application['default'].create({ Resolver: CustomResolver }); }); ok(app.__container__.lookup('router:main') instanceof CustomRouter, 'application resolved the correct router'); }); QUnit.test("throws helpful error if `app.then` is used", function() { run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); }); expectDeprecation(function() { run['default'](app, 'then', function() { return this; }); }, /Do not use `.then` on an instance of Ember.Application. Please use the `.ready` hook instead./); }); QUnit.test("registers controls onto to container", function() { run['default'](function() { app = Application['default'].create({ rootElement: '#qunit-fixture' }); }); ok(app.__container__.lookup('view:select'), "Select control is registered into views"); }); }); enifed('ember-application/tests/system/application_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/application_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/application_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/application_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/application_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/application_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/controller_test', ['ember-runtime/controllers/controller', 'ember-application/ext/controller', 'ember-runtime/system/container', 'ember-runtime/system/native_array', 'ember-runtime/controllers/array_controller', 'ember-metal/computed'], function (Controller, __dep1__, system__container, native_array, ArrayController, computed) { 'use strict'; /*jshint newcap:false */ QUnit.module("Controller dependencies"); QUnit.test("If a controller specifies a dependency, but does not have a container it should error", function() { var AController = Controller['default'].extend({ needs: 'posts' }); expectAssertion(function() { AController.create(); }, /specifies `needs`, but does not have a container. Please ensure this controller was instantiated with a container./); }); QUnit.test("If a controller specifies a dependency, it is accessible", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:post', Controller['default'].extend({ needs: 'posts' })); registry.register('controller:posts', Controller['default'].extend()); var postController = container.lookup('controller:post'); var postsController = container.lookup('controller:posts'); equal(postsController, postController.get('controllers.posts'), "controller.posts must be auto synthesized"); }); QUnit.test("If a controller specifies an unavailable dependency, it raises", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:post', Controller['default'].extend({ needs: ['comments'] })); throws(function() { container.lookup('controller:post'); }, /controller:comments/); registry.register('controller:blog', Controller['default'].extend({ needs: ['posts', 'comments'] })); throws(function() { container.lookup('controller:blog'); }, /controller:posts, controller:comments/); }); QUnit.test("Mixin sets up controllers if there is needs before calling super", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:other', ArrayController['default'].extend({ needs: 'posts', model: computed.computed.alias('controllers.posts') })); registry.register('controller:another', ArrayController['default'].extend({ needs: 'posts', modelBinding: 'controllers.posts' })); registry.register('controller:posts', ArrayController['default'].extend()); container.lookup('controller:posts').set('model', native_array.A(['a','b','c'])); deepEqual(['a','b','c'], container.lookup('controller:other').toArray()); deepEqual(['a','b','c'], container.lookup('controller:another').toArray()); }); QUnit.test("raises if trying to get a controller that was not pre-defined in `needs`", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:foo', Controller['default'].extend()); registry.register('controller:bar', Controller['default'].extend({ needs: 'foo' })); var fooController = container.lookup('controller:foo'); var barController = container.lookup('controller:bar'); throws(function() { fooController.get('controllers.bar'); }, /#needs does not include `bar`/, 'throws if controllers is accesed but needs not defined'); equal(barController.get('controllers.foo'), fooController, 'correctly needed controllers should continue to work'); throws(function() { barController.get('controllers.baz'); }, /#needs does not include `baz`/, 'should throw if no such controller was needed'); }); QUnit.test("setting the value of a controller dependency should not be possible", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:post', Controller['default'].extend({ needs: 'posts' })); registry.register('controller:posts', Controller['default'].extend()); var postController = container.lookup('controller:post'); container.lookup('controller:posts'); throws(function() { postController.set('controllers.posts', 'epic-self-troll'); }, /You cannot overwrite the value of `controllers.posts` of .+/, 'should raise when attempting to set the value of a controller dependency property'); postController.set('controllers.posts.title', "A Troll's Life"); equal(postController.get('controllers.posts.title'), "A Troll's Life", "can set the value of controllers.posts.title"); }); QUnit.test("raises if a dependency with a period is requested", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:big.bird', Controller['default'].extend()); registry.register('controller:foo', Controller['default'].extend({ needs: 'big.bird' })); expectAssertion(function() { container.lookup('controller:foo'); }, /needs must not specify dependencies with periods in their names \(big\.bird\)/, 'throws if periods used'); }); QUnit.test("can unit test controllers with `needs` dependencies by stubbing their `controllers` properties", function() { expect(1); var BrotherController = Controller['default'].extend({ needs: 'sister', foo: computed.computed.alias('controllers.sister.foo') }); var broController = BrotherController.create({ controllers: { sister: { foo: 5 } } }); equal(broController.get('foo'), 5, "`needs` dependencies can be stubbed"); }); }); enifed('ember-application/tests/system/controller_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/controller_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/controller_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/controller_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/controller_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/controller_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/dependency_injection/custom_resolver_test', ['ember-views/system/jquery', 'ember-metal/run_loop', 'ember-application/system/application', 'ember-application/system/resolver'], function (jQuery, run, Application, DefaultResolver) { 'use strict'; var application; QUnit.module("Ember.Application Dependency Injection – customResolver", { setup: function() { function fallbackTemplate() { return "

Fallback

"; } var Resolver = DefaultResolver['default'].extend({ resolveTemplate: function(resolvable) { var resolvedTemplate = this._super(resolvable); if (resolvedTemplate) { return resolvedTemplate; } return fallbackTemplate; } }); application = run['default'](function() { return Application['default'].create({ Resolver: Resolver, rootElement: '#qunit-fixture' }); }); }, teardown: function() { run['default'](application, 'destroy'); } }); QUnit.test("a resolver can be supplied to application", function() { equal(jQuery['default']("h1", application.rootElement).text(), "Fallback"); }); }); enifed('ember-application/tests/system/dependency_injection/custom_resolver_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/custom_resolver_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/dependency_injection/custom_resolver_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/dependency_injection/custom_resolver_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/custom_resolver_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/custom_resolver_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/dependency_injection/default_resolver_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-metal/logger', 'ember-runtime/controllers/controller', 'ember-runtime/system/object', 'ember-runtime/system/namespace', 'ember-application/system/application', 'ember-htmlbars/helpers'], function (Ember, run, Logger, Controller, EmberObject, Namespace, Application, helpers) { 'use strict'; var registry, locator, application, originalLookup, originalLoggerInfo; QUnit.module("Ember.Application Dependency Injection - default resolver", { setup: function() { originalLookup = Ember['default'].lookup; application = run['default'](Application['default'], 'create'); registry = application.registry; locator = application.__container__; originalLoggerInfo = Logger['default'].info; }, teardown: function() { Ember['default'].TEMPLATES = {}; Ember['default'].lookup = originalLookup; run['default'](application, 'destroy'); var UserInterfaceNamespace = Namespace['default'].NAMESPACES_BY_ID['UserInterface']; if (UserInterfaceNamespace) { run['default'](UserInterfaceNamespace, 'destroy'); } Logger['default'].info = originalLoggerInfo; } }); QUnit.test('the default resolver can look things up in other namespaces', function() { var UserInterface = Ember['default'].lookup.UserInterface = Namespace['default'].create(); UserInterface.NavigationController = Controller['default'].extend(); var nav = locator.lookup('controller:userInterface/navigation'); ok(nav instanceof UserInterface.NavigationController, "the result should be an instance of the specified class"); }); QUnit.test('the default resolver looks up templates in Ember.TEMPLATES', function() { function fooTemplate() {} function fooBarTemplate() {} function fooBarBazTemplate() {} Ember['default'].TEMPLATES['foo'] = fooTemplate; Ember['default'].TEMPLATES['fooBar'] = fooBarTemplate; Ember['default'].TEMPLATES['fooBar/baz'] = fooBarBazTemplate; equal(locator.lookup('template:foo'), fooTemplate, "resolves template:foo"); equal(locator.lookup('template:fooBar'), fooBarTemplate, "resolves template:foo_bar"); equal(locator.lookup('template:fooBar.baz'), fooBarBazTemplate, "resolves template:foo_bar.baz"); }); QUnit.test('the default resolver looks up basic name as no prefix', function() { ok(Controller['default'].detect(locator.lookup('controller:basic')), 'locator looksup correct controller'); }); function detectEqual(first, second, message) { ok(first.detect(second), message); } QUnit.test('the default resolver looks up arbitrary types on the namespace', function() { application.FooManager = EmberObject['default'].extend({}); detectEqual(application.FooManager, registry.resolver('manager:foo'), "looks up FooManager on application"); }); QUnit.test("the default resolver resolves models on the namespace", function() { application.Post = EmberObject['default'].extend({}); detectEqual(application.Post, locator.lookupFactory('model:post'), "looks up Post model on application"); }); QUnit.test("the default resolver resolves *:main on the namespace", function() { application.FooBar = EmberObject['default'].extend({}); detectEqual(application.FooBar, locator.lookupFactory('foo-bar:main'), "looks up FooBar type without name on application"); }); QUnit.test("the default resolver resolves helpers", function() { expect(2); function fooresolvertestHelper() { ok(true, 'found fooresolvertestHelper'); } function barBazResolverTestHelper() { ok(true, 'found barBazResolverTestHelper'); } helpers.registerHelper('fooresolvertest', fooresolvertestHelper); helpers.registerHelper('bar-baz-resolver-test', barBazResolverTestHelper); var retrievedFooResolverTestHelper, retrievedBarBazResolverTestHelper; retrievedFooResolverTestHelper = locator.lookup('helper:fooresolvertest').helperFunction; retrievedBarBazResolverTestHelper = locator.lookup('helper:bar-baz-resolver-test').helperFunction; fooresolvertestHelper(); barBazResolverTestHelper(); }); QUnit.test("the default resolver resolves container-registered helpers", function() { function gooresolvertestHelper() { return 'GOO'; } function gooGazResolverTestHelper() { return 'GAZ'; } application.register('helper:gooresolvertest', gooresolvertestHelper); application.register('helper:goo-baz-resolver-test', gooGazResolverTestHelper); equal(gooresolvertestHelper, locator.lookup('helper:gooresolvertest'), "looks up gooresolvertest helper"); equal(gooGazResolverTestHelper, locator.lookup('helper:goo-baz-resolver-test'), "looks up gooGazResolverTestHelper helper"); }); QUnit.test("the default resolver throws an error if the fullName to resolve is invalid", function() { throws(function() { registry.resolve(undefined);}, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve(null); }, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve(''); }, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve(''); }, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve(':'); }, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve('model'); }, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve('model:'); }, TypeError, /Invalid fullName/ ); throws(function() { registry.resolve(':type'); }, TypeError, /Invalid fullName/ ); }); QUnit.test("the default resolver logs hits if `LOG_RESOLVER` is set", function() { expect(3); application.LOG_RESOLVER = true; application.ScoobyDoo = EmberObject['default'].extend(); application.toString = function() { return 'App'; }; Logger['default'].info = function(symbol, name, padding, lookupDescription) { equal(symbol, '[✓]', 'proper symbol is printed when a module is found'); equal(name, 'doo:scooby', 'proper lookup value is logged'); equal(lookupDescription, 'App.ScoobyDoo'); }; registry.resolve('doo:scooby'); }); QUnit.test("the default resolver logs misses if `LOG_RESOLVER` is set", function() { expect(3); application.LOG_RESOLVER = true; application.toString = function() { return 'App'; }; Logger['default'].info = function(symbol, name, padding, lookupDescription) { equal(symbol, '[ ]', 'proper symbol is printed when a module is not found'); equal(name, 'doo:scooby', 'proper lookup value is logged'); equal(lookupDescription, 'App.ScoobyDoo'); }; registry.resolve('doo:scooby'); }); QUnit.test("doesn't log without LOG_RESOLVER", function() { var infoCount = 0; application.ScoobyDoo = EmberObject['default'].extend(); Logger['default'].info = function(symbol, name) { infoCount = infoCount + 1; }; registry.resolve('doo:scooby'); registry.resolve('doo:scrappy'); equal(infoCount, 0, 'Logger.info should not be called if LOG_RESOLVER is not set'); }); QUnit.test("lookup description", function() { application.toString = function() { return 'App'; }; equal(registry.describe('controller:foo'), 'App.FooController', 'Type gets appended at the end'); equal(registry.describe('controller:foo.bar'), 'App.FooBarController', 'dots are removed'); equal(registry.describe('model:foo'), 'App.Foo', "models don't get appended at the end"); }); }); enifed('ember-application/tests/system/dependency_injection/default_resolver_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/default_resolver_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/dependency_injection/default_resolver_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/dependency_injection/default_resolver_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/default_resolver_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/default_resolver_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/dependency_injection/normalization_test', ['ember-metal/run_loop', 'ember-metal/array', 'ember-application/system/application'], function (run, array, Application) { 'use strict'; var application, registry; QUnit.module("Ember.Application Dependency Injection – normalization", { setup: function() { application = run['default'](Application['default'], 'create'); registry = application.registry; }, teardown: function() { run['default'](application, 'destroy'); } }); QUnit.test('normalization', function() { ok(registry.normalize, 'registry#normalize is present'); equal(registry.normalize('foo:bar'), 'foo:bar'); equal(registry.normalize('controller:posts'), 'controller:posts'); equal(registry.normalize('controller:posts_index'), 'controller:postsIndex'); equal(registry.normalize('controller:posts.index'), 'controller:postsIndex'); equal(registry.normalize('controller:posts.post.index'), 'controller:postsPostIndex'); equal(registry.normalize('controller:posts_post.index'), 'controller:postsPostIndex'); equal(registry.normalize('controller:posts.post_index'), 'controller:postsPostIndex'); equal(registry.normalize('controller:postsIndex'), 'controller:postsIndex'); equal(registry.normalize('controller:blogPosts.index'), 'controller:blogPostsIndex'); equal(registry.normalize('controller:blog/posts.index'), 'controller:blog/postsIndex'); equal(registry.normalize('controller:blog/posts.post.index'), 'controller:blog/postsPostIndex'); equal(registry.normalize('controller:blog/posts_post.index'), 'controller:blog/postsPostIndex'); equal(registry.normalize('template:blog/posts_index'), 'template:blog/posts_index'); }); QUnit.test('normalization is indempotent', function() { var examples = ['controller:posts', 'controller:posts.post.index', 'controller:blog/posts.post_index', 'template:foo_bar']; array.forEach.call(examples, function (example) { equal(registry.normalize(registry.normalize(example)), registry.normalize(example)); }); }); }); enifed('ember-application/tests/system/dependency_injection/normalization_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/normalization_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/dependency_injection/normalization_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/dependency_injection/normalization_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/normalization_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/normalization_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/dependency_injection/to_string_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-application/system/application', 'ember-runtime/system/object', 'ember-application/system/resolver', 'ember-metal/utils'], function (Ember, run, Application, EmberObject, DefaultResolver, utils) { 'use strict'; var originalLookup, App, originalModelInjections; QUnit.module("Ember.Application Dependency Injection – toString", { setup: function() { originalModelInjections = Ember['default'].MODEL_FACTORY_INJECTIONS; Ember['default'].MODEL_FACTORY_INJECTIONS = true; originalLookup = Ember['default'].lookup; run['default'](function() { App = Application['default'].create(); Ember['default'].lookup = { App: App }; }); App.Post = EmberObject['default'].extend(); }, teardown: function() { Ember['default'].lookup = originalLookup; run['default'](App, 'destroy'); Ember['default'].MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); QUnit.test("factories", function() { var PostFactory = App.__container__.lookupFactory('model:post'); equal(PostFactory.toString(), 'App.Post', 'expecting the model to be post'); }); QUnit.test("instances", function() { var post = App.__container__.lookup('model:post'); var guid = utils.guidFor(post); equal(post.toString(), '', 'expecting the model to be post'); }); QUnit.test("with a custom resolver", function() { run['default'](App, 'destroy'); run['default'](function() { App = Application['default'].create({ Resolver: DefaultResolver['default'].extend({ makeToString: function(factory, fullName) { return fullName; } }) }); }); App.registry.register('model:peter', EmberObject['default'].extend()); var peter = App.__container__.lookup('model:peter'); var guid = utils.guidFor(peter); equal(peter.toString(), '', 'expecting the supermodel to be peter'); }); }); enifed('ember-application/tests/system/dependency_injection/to_string_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/to_string_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/dependency_injection/to_string_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/dependency_injection/to_string_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/to_string_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/to_string_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/dependency_injection_test', ['ember-metal/run_loop', 'ember-runtime/system/object', 'ember-application/system/application'], function (run, EmberObject, Application) { 'use strict'; var EmberApplication = Application['default']; var originalLookup = Ember.lookup; var registry, locator, lookup, application, originalModelInjections; QUnit.module("Ember.Application Dependency Injection", { setup: function() { originalModelInjections = Ember.MODEL_FACTORY_INJECTIONS; Ember.MODEL_FACTORY_INJECTIONS = true; application = run['default'](EmberApplication, 'create'); application.Person = EmberObject['default'].extend({}); application.Orange = EmberObject['default'].extend({}); application.Email = EmberObject['default'].extend({}); application.User = EmberObject['default'].extend({}); application.PostIndexController = EmberObject['default'].extend({}); application.register('model:person', application.Person, { singleton: false }); application.register('model:user', application.User, { singleton: false }); application.register('fruit:favorite', application.Orange); application.register('communication:main', application.Email, { singleton: false }); application.register('controller:postIndex', application.PostIndexController, { singleton: true }); registry = application.registry; locator = application.__container__; lookup = Ember.lookup = {}; }, teardown: function() { run['default'](application, 'destroy'); application = locator = null; Ember.lookup = originalLookup; Ember.MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); QUnit.test('container lookup is normalized', function() { var dotNotationController = locator.lookup('controller:post.index'); var camelCaseController = locator.lookup('controller:postIndex'); ok(dotNotationController instanceof application.PostIndexController); ok(camelCaseController instanceof application.PostIndexController); equal(dotNotationController, camelCaseController); }); QUnit.test('registered entities can be looked up later', function() { equal(registry.resolve('model:person'), application.Person); equal(registry.resolve('model:user'), application.User); equal(registry.resolve('fruit:favorite'), application.Orange); equal(registry.resolve('communication:main'), application.Email); equal(registry.resolve('controller:postIndex'), application.PostIndexController); equal(locator.lookup('fruit:favorite'), locator.lookup('fruit:favorite'), 'singleton lookup worked'); ok(locator.lookup('model:user') !== locator.lookup('model:user'), 'non-singleton lookup worked'); }); QUnit.test('injections', function() { application.inject('model', 'fruit', 'fruit:favorite'); application.inject('model:user', 'communication', 'communication:main'); var user = locator.lookup('model:user'); var person = locator.lookup('model:person'); var fruit = locator.lookup('fruit:favorite'); equal(user.get('fruit'), fruit); equal(person.get('fruit'), fruit); ok(application.Email.detectInstance(user.get('communication'))); }); }); enifed('ember-application/tests/system/dependency_injection_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/dependency_injection_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/dependency_injection_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/dependency_injection_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/dependency_injection_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/initializers_test', ['ember-metal/run_loop', 'ember-application/system/application', 'ember-metal/array', 'ember-views/system/jquery', 'container/registry'], function (run, Application, array, jQuery, Registry) { 'use strict'; var app; QUnit.module("Ember.Application initializers", { setup: function() { }, teardown: function() { if (app) { run['default'](function() { app.destroy(); }); } } }); QUnit.test("initializers require proper 'name' and 'initialize' properties", function() { var MyApplication = Application['default'].extend(); expectAssertion(function() { run['default'](function() { MyApplication.initializer({ name: 'initializer' }); }); }); expectAssertion(function() { run['default'](function() { MyApplication.initializer({ initialize: Ember.K }); }); }); }); QUnit.test("initializers are passed a registry and App", function() { var MyApplication = Application['default'].extend(); MyApplication.initializer({ name: 'initializer', initialize: function(registry, App) { ok(registry instanceof Registry['default'], "initialize is passed a registry"); ok(App instanceof Application['default'], "initialize is passed an Application"); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); }); QUnit.test("initializers can be registered in a specified order", function() { var order = []; var MyApplication = Application['default'].extend(); MyApplication.initializer({ name: 'fourth', after: 'third', initialize: function(registry) { order.push('fourth'); } }); MyApplication.initializer({ name: 'second', after: 'first', before: 'third', initialize: function(registry) { order.push('second'); } }); MyApplication.initializer({ name: 'fifth', after: 'fourth', before: 'sixth', initialize: function(registry) { order.push('fifth'); } }); MyApplication.initializer({ name: 'first', before: 'second', initialize: function(registry) { order.push('first'); } }); MyApplication.initializer({ name: 'third', initialize: function(registry) { order.push('third'); } }); MyApplication.initializer({ name: 'sixth', initialize: function(registry) { order.push('sixth'); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']); }); QUnit.test("initializers can be registered in a specified order as an array", function() { var order = []; var MyApplication = Application['default'].extend(); MyApplication.initializer({ name: 'third', initialize: function(registry) { order.push('third'); } }); MyApplication.initializer({ name: 'second', after: 'first', before: ['third', 'fourth'], initialize: function(registry) { order.push('second'); } }); MyApplication.initializer({ name: 'fourth', after: ['second', 'third'], initialize: function(registry) { order.push('fourth'); } }); MyApplication.initializer({ name: 'fifth', after: 'fourth', before: 'sixth', initialize: function(registry) { order.push('fifth'); } }); MyApplication.initializer({ name: 'first', before: ['second'], initialize: function(registry) { order.push('first'); } }); MyApplication.initializer({ name: 'sixth', initialize: function(registry) { order.push('sixth'); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']); }); QUnit.test("initializers can have multiple dependencies", function () { var order = []; var a = { name: "a", before: "b", initialize: function(registry) { order.push('a'); } }; var b = { name: "b", initialize: function(registry) { order.push('b'); } }; var c = { name: "c", after: "b", initialize: function(registry) { order.push('c'); } }; var afterB = { name: "after b", after: "b", initialize: function(registry) { order.push("after b"); } }; var afterC = { name: "after c", after: "c", initialize: function(registry) { order.push("after c"); } }; Application['default'].initializer(b); Application['default'].initializer(a); Application['default'].initializer(afterC); Application['default'].initializer(afterB); Application['default'].initializer(c); run['default'](function() { app = Application['default'].create({ router: false, rootElement: '#qunit-fixture' }); }); ok(array.indexOf.call(order, a.name) < array.indexOf.call(order, b.name), 'a < b'); ok(array.indexOf.call(order, b.name) < array.indexOf.call(order, c.name), 'b < c'); ok(array.indexOf.call(order, b.name) < array.indexOf.call(order, afterB.name), 'b < afterB'); ok(array.indexOf.call(order, c.name) < array.indexOf.call(order, afterC.name), 'c < afterC'); }); QUnit.test("initializers set on Application subclasses should not be shared between apps", function() { var firstInitializerRunCount = 0; var secondInitializerRunCount = 0; var FirstApp = Application['default'].extend(); FirstApp.initializer({ name: 'first', initialize: function(registry) { firstInitializerRunCount++; } }); var SecondApp = Application['default'].extend(); SecondApp.initializer({ name: 'second', initialize: function(registry) { secondInitializerRunCount++; } }); jQuery['default']('#qunit-fixture').html('
'); run['default'](function() { FirstApp.create({ router: false, rootElement: '#qunit-fixture #first' }); }); equal(firstInitializerRunCount, 1, 'first initializer only was run'); equal(secondInitializerRunCount, 0, 'first initializer only was run'); run['default'](function() { SecondApp.create({ router: false, rootElement: '#qunit-fixture #second' }); }); equal(firstInitializerRunCount, 1, 'second initializer only was run'); equal(secondInitializerRunCount, 1, 'second initializer only was run'); }); QUnit.test("initializers are concatenated", function() { var firstInitializerRunCount = 0; var secondInitializerRunCount = 0; var FirstApp = Application['default'].extend(); FirstApp.initializer({ name: 'first', initialize: function(registry) { firstInitializerRunCount++; } }); var SecondApp = FirstApp.extend(); SecondApp.initializer({ name: 'second', initialize: function(registry) { secondInitializerRunCount++; } }); jQuery['default']('#qunit-fixture').html('
'); run['default'](function() { FirstApp.create({ router: false, rootElement: '#qunit-fixture #first' }); }); equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created'); equal(secondInitializerRunCount, 0, 'first initializer only was run when base class created'); firstInitializerRunCount = 0; run['default'](function() { SecondApp.create({ router: false, rootElement: '#qunit-fixture #second' }); }); equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created'); equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created'); }); QUnit.test("initializers are per-app", function() { expect(0); var FirstApp = Application['default'].extend(); FirstApp.initializer({ name: 'shouldNotCollide', initialize: function(registry) {} }); var SecondApp = Application['default'].extend(); SecondApp.initializer({ name: 'shouldNotCollide', initialize: function(registry) {} }); }); if (Ember.FEATURES.isEnabled("ember-application-initializer-context")) { QUnit.test("initializers should be executed in their own context", function() { expect(1); var MyApplication = Application['default'].extend(); MyApplication.initializer({ name: 'coolBabeInitializer', myProperty: 'coolBabe', initialize: function(registry, application) { equal(this.myProperty, 'coolBabe', 'should have access to its own context'); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); }); } }); enifed('ember-application/tests/system/initializers_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/initializers_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/initializers_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/initializers_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/initializers_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/initializers_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/instance_initializers_test', ['ember-metal/run_loop', 'ember-application/system/application', 'ember-application/system/application-instance', 'ember-metal/array', 'ember-views/system/jquery'], function (run, Application, ApplicationInstance, array, jQuery) { 'use strict'; var app; if (Ember.FEATURES.isEnabled('ember-application-instance-initializers')) { QUnit.module("Ember.Application instance initializers", { setup: function() { }, teardown: function() { if (app) { run['default'](function() { app.destroy(); }); } } }); QUnit.test("initializers require proper 'name' and 'initialize' properties", function() { var MyApplication = Application['default'].extend(); expectAssertion(function() { run['default'](function() { MyApplication.instanceInitializer({ name: 'initializer' }); }); }); expectAssertion(function() { run['default'](function() { MyApplication.instanceInitializer({ initialize: Ember.K }); }); }); }); QUnit.test("initializers are passed an app instance", function() { var MyApplication = Application['default'].extend(); MyApplication.instanceInitializer({ name: 'initializer', initialize: function(instance) { ok(instance instanceof ApplicationInstance['default'], "initialize is passed an application instance"); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); }); QUnit.test("initializers can be registered in a specified order", function() { var order = []; var MyApplication = Application['default'].extend(); MyApplication.instanceInitializer({ name: 'fourth', after: 'third', initialize: function(registry) { order.push('fourth'); } }); MyApplication.instanceInitializer({ name: 'second', after: 'first', before: 'third', initialize: function(registry) { order.push('second'); } }); MyApplication.instanceInitializer({ name: 'fifth', after: 'fourth', before: 'sixth', initialize: function(registry) { order.push('fifth'); } }); MyApplication.instanceInitializer({ name: 'first', before: 'second', initialize: function(registry) { order.push('first'); } }); MyApplication.instanceInitializer({ name: 'third', initialize: function(registry) { order.push('third'); } }); MyApplication.instanceInitializer({ name: 'sixth', initialize: function(registry) { order.push('sixth'); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']); }); QUnit.test("initializers can be registered in a specified order as an array", function() { var order = []; var MyApplication = Application['default'].extend(); MyApplication.instanceInitializer({ name: 'third', initialize: function(registry) { order.push('third'); } }); MyApplication.instanceInitializer({ name: 'second', after: 'first', before: ['third', 'fourth'], initialize: function(registry) { order.push('second'); } }); MyApplication.instanceInitializer({ name: 'fourth', after: ['second', 'third'], initialize: function(registry) { order.push('fourth'); } }); MyApplication.instanceInitializer({ name: 'fifth', after: 'fourth', before: 'sixth', initialize: function(registry) { order.push('fifth'); } }); MyApplication.instanceInitializer({ name: 'first', before: ['second'], initialize: function(registry) { order.push('first'); } }); MyApplication.instanceInitializer({ name: 'sixth', initialize: function(registry) { order.push('sixth'); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']); }); QUnit.test("initializers can have multiple dependencies", function () { var order = []; var a = { name: "a", before: "b", initialize: function(registry) { order.push('a'); } }; var b = { name: "b", initialize: function(registry) { order.push('b'); } }; var c = { name: "c", after: "b", initialize: function(registry) { order.push('c'); } }; var afterB = { name: "after b", after: "b", initialize: function(registry) { order.push("after b"); } }; var afterC = { name: "after c", after: "c", initialize: function(registry) { order.push("after c"); } }; Application['default'].instanceInitializer(b); Application['default'].instanceInitializer(a); Application['default'].instanceInitializer(afterC); Application['default'].instanceInitializer(afterB); Application['default'].instanceInitializer(c); run['default'](function() { app = Application['default'].create({ router: false, rootElement: '#qunit-fixture' }); }); ok(array.indexOf.call(order, a.name) < array.indexOf.call(order, b.name), 'a < b'); ok(array.indexOf.call(order, b.name) < array.indexOf.call(order, c.name), 'b < c'); ok(array.indexOf.call(order, b.name) < array.indexOf.call(order, afterB.name), 'b < afterB'); ok(array.indexOf.call(order, c.name) < array.indexOf.call(order, afterC.name), 'c < afterC'); }); QUnit.test("initializers set on Application subclasses should not be shared between apps", function() { var firstInitializerRunCount = 0; var secondInitializerRunCount = 0; var FirstApp = Application['default'].extend(); FirstApp.instanceInitializer({ name: 'first', initialize: function(registry) { firstInitializerRunCount++; } }); var SecondApp = Application['default'].extend(); SecondApp.instanceInitializer({ name: 'second', initialize: function(registry) { secondInitializerRunCount++; } }); jQuery['default']('#qunit-fixture').html('
'); run['default'](function() { FirstApp.create({ router: false, rootElement: '#qunit-fixture #first' }); }); equal(firstInitializerRunCount, 1, 'first initializer only was run'); equal(secondInitializerRunCount, 0, 'first initializer only was run'); run['default'](function() { SecondApp.create({ router: false, rootElement: '#qunit-fixture #second' }); }); equal(firstInitializerRunCount, 1, 'second initializer only was run'); equal(secondInitializerRunCount, 1, 'second initializer only was run'); }); QUnit.test("initializers are concatenated", function() { var firstInitializerRunCount = 0; var secondInitializerRunCount = 0; var FirstApp = Application['default'].extend(); FirstApp.instanceInitializer({ name: 'first', initialize: function(registry) { firstInitializerRunCount++; } }); var SecondApp = FirstApp.extend(); SecondApp.instanceInitializer({ name: 'second', initialize: function(registry) { secondInitializerRunCount++; } }); jQuery['default']('#qunit-fixture').html('
'); run['default'](function() { FirstApp.create({ router: false, rootElement: '#qunit-fixture #first' }); }); equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created'); equal(secondInitializerRunCount, 0, 'first initializer only was run when base class created'); firstInitializerRunCount = 0; run['default'](function() { SecondApp.create({ router: false, rootElement: '#qunit-fixture #second' }); }); equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created'); equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created'); }); QUnit.test("initializers are per-app", function() { expect(0); var FirstApp = Application['default'].extend(); FirstApp.instanceInitializer({ name: 'shouldNotCollide', initialize: function(registry) {} }); var SecondApp = Application['default'].extend(); SecondApp.instanceInitializer({ name: 'shouldNotCollide', initialize: function(registry) {} }); }); if (Ember.FEATURES.isEnabled("ember-application-initializer-context")) { QUnit.test("initializers should be executed in their own context", function() { expect(1); var MyApplication = Application['default'].extend(); MyApplication.instanceInitializer({ name: 'coolBabeInitializer', myProperty: 'coolBabe', initialize: function(registry, application) { equal(this.myProperty, 'coolBabe', 'should have access to its own context'); } }); run['default'](function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); }); } } }); enifed('ember-application/tests/system/instance_initializers_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/instance_initializers_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/instance_initializers_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/instance_initializers_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/instance_initializers_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/instance_initializers_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/logging_test', ['ember-metal/run_loop', 'ember-application/system/application', 'ember-views/views/view', 'ember-runtime/controllers/controller', 'ember-routing/system/route', 'ember-runtime/ext/rsvp', 'ember-metal/keys', 'ember-routing'], function (run, Application, View, Controller, Route, RSVP, keys) { 'use strict'; /*globals EmberDev */ var App, logs, originalLogger; QUnit.module("Ember.Application – logging of generated classes", { setup: function() { logs = {}; originalLogger = Ember.Logger.info; Ember.Logger.info = function() { var fullName = arguments[1].fullName; logs[fullName] = logs[fullName] || 0; logs[fullName]++; }; run['default'](function() { App = Application['default'].create({ LOG_ACTIVE_GENERATION: true }); App.Router.reopen({ location: 'none' }); App.Router.map(function() { this.resource("posts"); }); App.deferReadiness(); }); }, teardown: function() { Ember.Logger.info = originalLogger; run['default'](App, 'destroy'); logs = App = null; } }); function visit(path) { QUnit.stop(); var promise = run['default'](function() { return new RSVP['default'].Promise(function(resolve, reject) { var router = App.__container__.lookup('router:main'); resolve(router.handleURL(path).then(function(value) { QUnit.start(); ok(true, 'visited: `' + path + '`'); return value; }, function(reason) { QUnit.start(); ok(false, 'failed to visit:`' + path + '` reason: `' + QUnit.jsDump.parse(reason)); throw reason; })); }); }); return { then: function(resolve, reject) { run['default'](promise, 'then', resolve, reject); } }; } QUnit.test("log class generation if logging enabled", function() { if (EmberDev && EmberDev.runningProdBuild) { ok(true, 'Logging does not occur in production builds'); return; } run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(Ember.keys(logs).length, 6, 'expected logs'); }); }); QUnit.test("do NOT log class generation if logging disabled", function() { App.reopen({ LOG_ACTIVE_GENERATION: false }); run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(keys['default'](logs).length, 0, 'expected no logs'); }); }); QUnit.test("actively generated classes get logged", function() { if (EmberDev && EmberDev.runningProdBuild) { ok(true, 'Logging does not occur in production builds'); return; } run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(logs['controller:application'], 1, 'expected: ApplicationController was generated'); equal(logs['controller:posts'], 1, 'expected: PostsController was generated'); equal(logs['route:application'], 1, 'expected: ApplicationRoute was generated'); equal(logs['route:posts'], 1, 'expected: PostsRoute was generated'); }); }); QUnit.test("predefined classes do not get logged", function() { App.ApplicationController = Controller['default'].extend(); App.PostsController = Controller['default'].extend(); App.ApplicationRoute = Route['default'].extend(); App.PostsRoute = Route['default'].extend(); run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { ok(!logs['controller:application'], 'did not expect: ApplicationController was generated'); ok(!logs['controller:posts'], 'did not expect: PostsController was generated'); ok(!logs['route:application'], 'did not expect: ApplicationRoute was generated'); ok(!logs['route:posts'], 'did not expect: PostsRoute was generated'); }); }); QUnit.module("Ember.Application – logging of view lookups", { setup: function() { logs = {}; originalLogger = Ember.Logger.info; Ember.Logger.info = function() { var fullName = arguments[1].fullName; logs[fullName] = logs[fullName] || 0; logs[fullName]++; }; run['default'](function() { App = Application['default'].create({ LOG_VIEW_LOOKUPS: true }); App.Router.reopen({ location: 'none' }); App.Router.map(function() { this.resource("posts"); }); App.deferReadiness(); }); }, teardown: function() { Ember.Logger.info = originalLogger; run['default'](App, 'destroy'); logs = App = null; } }); QUnit.test("log when template and view are missing when flag is active", function() { if (EmberDev && EmberDev.runningProdBuild) { ok(true, 'Logging does not occur in production builds'); return; } App.register('template:application', function() { return ''; }); run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(logs['template:application'], undefined, 'expected: Should not log template:application since it exists.'); equal(logs['template:index'], 1, 'expected: Could not find "index" template or view.'); equal(logs['template:posts'], 1, 'expected: Could not find "posts" template or view.'); }); }); QUnit.test("do not log when template and view are missing when flag is not true", function() { App.reopen({ LOG_VIEW_LOOKUPS: false }); run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(keys['default'](logs).length, 0, 'expected no logs'); }); }); QUnit.test("log which view is used with a template", function() { if (EmberDev && EmberDev.runningProdBuild) { ok(true, 'Logging does not occur in production builds'); return; } App.register('template:application', function() { return 'Template with default view'; }); App.register('template:foo', function() { return 'Template with custom view'; }); App.register('view:posts', View['default'].extend({ templateName: 'foo' })); run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(logs['view:application'], 1, 'expected: Should log use of default view'); equal(logs['view:index'], undefined, 'expected: Should not log when index is not present.'); equal(logs['view:posts'], 1, 'expected: Rendering posts with PostsView.'); }); }); QUnit.test("do not log which views are used with templates when flag is not true", function() { App.reopen({ LOG_VIEW_LOOKUPS: false }); run['default'](App, 'advanceReadiness'); visit('/posts').then(function() { equal(keys['default'](logs).length, 0, 'expected no logs'); }); }); }); enifed('ember-application/tests/system/logging_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/logging_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/logging_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/logging_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/logging_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/logging_test.js should pass jshint.'); }); }); enifed('ember-application/tests/system/readiness_test', ['ember-metal/run_loop', 'ember-application/system/application'], function (run, EmberApplication) { 'use strict'; var jQuery, application, Application; var readyWasCalled, domReady, readyCallbacks; // We are using a small mock of jQuery because jQuery is third-party code with // very well-defined semantics, and we want to confirm that a jQuery stub run // in a more minimal server environment that implements this behavior will be // sufficient for Ember's requirements. QUnit.module("Application readiness", { setup: function() { readyWasCalled = 0; readyCallbacks = []; var jQueryInstance = { ready: function(callback) { readyCallbacks.push(callback); if (jQuery.isReady) { domReady(); } } }; jQuery = function() { return jQueryInstance; }; jQuery.isReady = false; var domReadyCalled = 0; domReady = function() { if (domReadyCalled !== 0) { return; } domReadyCalled++; var i; for (i=0; iHello world')); } }); }); assert.equal(Ember.$('#qunit-fixture').children().length, 0, "there are no elements in the fixture element"); app.visit('/').then(function(instance) { QUnit.start(); assert.ok(instance instanceof ApplicationInstance['default'], "promise is resolved with an ApplicationInstance"); run['default'](instance.view, 'appendTo', '#qunit-fixture'); assert.equal(Ember.$("#qunit-fixture > .ember-view h1").text(), "Hello world", "the application was rendered once the promise resolves"); instance.destroy(); }, function(error) { QUnit.start(); assert.ok(false, "The visit() promise was rejected: " + error); }); }); } }); enifed('ember-application/tests/system/visit_test.jscs-test', function () { 'use strict'; module('JSCS - ember-application/tests/system'); test('ember-application/tests/system/visit_test.js should pass jscs', function() { ok(true, 'ember-application/tests/system/visit_test.js should pass jscs.'); }); }); enifed('ember-application/tests/system/visit_test.jshint', function () { 'use strict'; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/visit_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/visit_test.js should pass jshint.'); }); }); enifed('ember-debug.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-debug.js should pass jscs', function() { ok(true, 'ember-debug.js should pass jscs.'); }); }); enifed('ember-debug.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-debug.js should pass jshint', function() { ok(true, 'ember-debug.js should pass jshint.'); }); }); enifed('ember-debug/tests/main_test', ['ember-metal/core'], function (Ember) { 'use strict'; QUnit.module('ember-debug'); QUnit.test('Ember.deprecate throws deprecation if second argument is falsy', function() { expect(3); throws(function() { }); throws(function() { }); throws(function() { }); }); QUnit.test('Ember.deprecate does not throw deprecation if second argument is a function and it returns true', function() { expect(1); ok(true, 'deprecation was not thrown'); }); QUnit.test('Ember.deprecate throws if second argument is a function and it returns false', function() { expect(1); throws(function() { }); }); QUnit.test('Ember.deprecate does not throw deprecations if second argument is truthy', function() { expect(1); ok(true, 'deprecations were not thrown'); }); QUnit.test('Ember.assert throws if second argument is falsy', function() { expect(3); throws(function() { }); throws(function() { }); throws(function() { }); }); QUnit.test('Ember.assert does not throw if second argument is a function and it returns true', function() { expect(1); ok(true, 'assertion was not thrown'); }); QUnit.test('Ember.assert throws if second argument is a function and it returns false', function() { expect(1); throws(function() { }); }); QUnit.test('Ember.assert does not throw if second argument is truthy', function() { expect(1); ok(true, 'assertions were not thrown'); }); QUnit.test('Ember.assert does not throw if second argument is an object', function() { expect(1); var Igor = Ember['default'].Object.extend(); ok(true, 'assertions were not thrown'); }); }); enifed('ember-debug/tests/main_test.jscs-test', function () { 'use strict'; module('JSCS - ember-debug/tests'); test('ember-debug/tests/main_test.js should pass jscs', function() { ok(true, 'ember-debug/tests/main_test.js should pass jscs.'); }); }); enifed('ember-debug/tests/main_test.jshint', function () { 'use strict'; module('JSHint - ember-debug/tests'); test('ember-debug/tests/main_test.js should pass jshint', function() { ok(true, 'ember-debug/tests/main_test.js should pass jshint.'); }); }); enifed('ember-debug/tests/warn_if_using_stripped_feature_flags_test', ['ember-metal/core', 'ember-debug'], function (Ember, ember_debug) { 'use strict'; var oldWarn, oldRunInDebug, origEnvFeatures, origEnableAll, origEnableOptional; function confirmWarns(expectedMsg) { var featuresWereStripped = true; var FEATURES = Ember['default'].ENV.FEATURES; Ember['default'].warn = function(msg, test) { if (!test) { equal(msg, expectedMsg); } }; Ember['default'].runInDebug = function (func) { func(); }; // Should trigger our 1 warning ember_debug._warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped); // Shouldn't trigger any warnings now that we're "in canary" featuresWereStripped = false; ember_debug._warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped); } QUnit.module("ember-debug - _warnIfUsingStrippedFeatureFlags", { setup: function() { oldWarn = Ember['default'].warn; oldRunInDebug = Ember['default'].runInDebug; origEnvFeatures = Ember['default'].ENV.FEATURES; origEnableAll = Ember['default'].ENV.ENABLE_ALL_FEATURES; origEnableOptional = Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES; }, teardown: function() { Ember['default'].warn = oldWarn; Ember['default'].runInDebug = oldRunInDebug; Ember['default'].ENV.FEATURES = origEnvFeatures; Ember['default'].ENV.ENABLE_ALL_FEATURES = origEnableAll; Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = origEnableOptional; } }); QUnit.test("Setting Ember.ENV.ENABLE_ALL_FEATURES truthy in non-canary, debug build causes a warning", function() { expect(1); Ember['default'].ENV.ENABLE_ALL_FEATURES = true; Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = false; Ember['default'].ENV.FEATURES = {}; confirmWarns('Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.'); }); QUnit.test("Setting Ember.ENV.ENABLE_OPTIONAL_FEATURES truthy in non-canary, debug build causes a warning", function() { expect(1); Ember['default'].ENV.ENABLE_ALL_FEATURES = false; Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = true; Ember['default'].ENV.FEATURES = {}; confirmWarns('Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.'); }); QUnit.test("Enabling a FEATURES flag in non-canary, debug build causes a warning", function() { expect(1); Ember['default'].ENV.ENABLE_ALL_FEATURES = false; Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = false; Ember['default'].ENV.FEATURES = { 'fred': true, 'barney': false, 'wilma': null }; confirmWarns('FEATURE["fred"] is set as enabled, but FEATURE flags are only available in canary builds.'); }); }); enifed('ember-debug/tests/warn_if_using_stripped_feature_flags_test.jscs-test', function () { 'use strict'; module('JSCS - ember-debug/tests'); test('ember-debug/tests/warn_if_using_stripped_feature_flags_test.js should pass jscs', function() { ok(true, 'ember-debug/tests/warn_if_using_stripped_feature_flags_test.js should pass jscs.'); }); }); enifed('ember-debug/tests/warn_if_using_stripped_feature_flags_test.jshint', function () { 'use strict'; module('JSHint - ember-debug/tests'); test('ember-debug/tests/warn_if_using_stripped_feature_flags_test.js should pass jshint', function() { ok(true, 'ember-debug/tests/warn_if_using_stripped_feature_flags_test.js should pass jshint.'); }); }); enifed('ember-dev/test-helper/assertion', ['exports', './method-call-expectation', './utils'], function (exports, MethodCallExpectation, utils) { 'use strict'; /* globals QUnit */ function AssertExpectation(Ember, message){ MethodCallExpectation['default'].call(this, Ember, 'assert'); this.expectedMessage = message; } AssertExpectation.Error = function(){}; AssertExpectation.prototype = utils.o_create(MethodCallExpectation['default'].prototype); AssertExpectation.prototype.handleCall = function(message, test){ var noAssertion = typeof test === 'function' ? test() : test; this.sawCall = true; if (noAssertion) { return; } this.actualMessage = message; // Halt execution throw new AssertExpectation.Error(); }; AssertExpectation.prototype.assert = function(fn){ try { this.runWithStub(fn); } catch (e) { if (!(e instanceof AssertExpectation.Error)) { throw e; } } // Run assertions in an order that is useful when debugging a test failure. // if (!this.sawCall) { QUnit.ok(false, "Expected Ember.assert to be called (Not called with any value)."); } else if (!this.actualMessage) { QUnit.ok(false, 'Expected a failing Ember.assert (Ember.assert called, but without a failing test).'); } else { if (this.expectedMessage) { if (this.expectedMessage instanceof RegExp) { QUnit.ok(this.expectedMessage.test(this.actualMessage), "Expected failing Ember.assert: '" + this.expectedMessage + "', but got '" + this.actualMessage + "'."); } else { QUnit.equal(this.actualMessage, this.expectedMessage, "Expected failing Ember.assert: '" + this.expectedMessage + "', but got '" + this.actualMessage + "'."); } } else { // Positive assertion that assert was called QUnit.ok(true, 'Expected a failing Ember.assert.'); } } }; var AssertionAssert = function(env){ this.env = env; }; AssertionAssert.prototype = { reset: function(){ }, inject: function(){ var assertion = this; // Looks for an exception raised within the fn. // // expectAssertion(function(){ // Ember.assert("Homie don't roll like that"); // } /* , optionalMessageStringOrRegex */); // window.expectAssertion = function expectAssertion(fn, message){ if (assertion.env.runningProdBuild){ QUnit.ok(true, 'Assertions disabled in production builds.'); return; } // do not assert as the production builds do not contain Ember.assert (new AssertExpectation(assertion.env.Ember, message)).assert(fn); }; window.ignoreAssertion = function ignoreAssertion(fn){ var stubber = new MethodCallExpectation['default'](assertion.env.Ember, 'assert'), noop = function(){}; stubber.runWithStub(fn, noop); }; }, assert: function(){ }, restore: function(){ window.expectAssertion = null; window.ignoreAssertion = null; } }; exports['default'] = AssertionAssert; }); enifed('ember-dev/test-helper/deprecation', ['exports', './method-call-expectation'], function (exports, MethodCallExpectation) { 'use strict'; /* globals QUnit */ var NONE = function(){}; var DeprecationAssert = function(env){ this.env = env; this.reset(); }; DeprecationAssert.prototype = { reset: function(){ this.expecteds = null; this.actuals = null; }, stubEmber: function(){ if (!this._previousEmberDeprecate && this._previousEmberDeprecate !== this.env.Ember.deprecate) { this._previousEmberDeprecate = this.env.Ember.deprecate; } var assertion = this; this.env.Ember.deprecate = function(msg, test) { var pushDeprecation = typeof test === 'function' ? !test() : !test; assertion.actuals = assertion.actuals || []; if (pushDeprecation) { assertion.actuals.push([msg, test]); } }; }, inject: function(){ var assertion = this; // Expects no deprecation to happen from the time of calling until // the end of the test. // // expectNoDeprecation(/* optionalStringOrRegex */); // Ember.deprecate("Old And Busted"); // window.expectNoDeprecation = function() { if (assertion.expecteds != null && typeof assertion.expecteds === 'object') { throw new Error("expectNoDeprecation was called after expectDeprecation was called!"); } assertion.stubEmber(); assertion.expecteds = NONE; }; // Expect a deprecation to happen within a function, or if no function // is pass, from the time of calling until the end of the test. Can be called // multiple times to assert deprecations with different specific messages // were fired. // // expectDeprecation(function(){ // Ember.deprecate("Old And Busted"); // }, /* optionalStringOrRegex */); // // expectDeprecation(/* optionalStringOrRegex */); // Ember.deprecate("Old And Busted"); // window.expectDeprecation = function(fn, message) { var originalExpecteds, originalActuals; if (assertion.expecteds === NONE) { throw new Error("expectDeprecation was called after expectNoDeprecation was called!"); } assertion.stubEmber(); assertion.expecteds = assertion.expecteds || []; if (fn && typeof fn !== 'function') { // fn is a message assertion.expecteds.push(fn); } else { originalExpecteds = assertion.expecteds.slice(); originalActuals = assertion.actuals ? assertion.actuals.slice() : assertion.actuals; assertion.expecteds.push(message || /.*/); if (fn) { fn(); assertion.assert(); assertion.expecteds = originalExpecteds; assertion.actuals = originalActuals; } } }; window.ignoreDeprecation = function ignoreDeprecation(fn){ var stubber = new MethodCallExpectation['default'](assertion.env.Ember, 'deprecate'), noop = function(){}; stubber.runWithStub(fn, noop); }; }, // Forces an assert the deprecations occurred, and resets the globals // storing asserts for the next run. // // expectNoDeprecation(/Old/); // setTimeout(function(){ // Ember.deprecate("Old And Busted"); // assertDeprecation(); // }); // // assertDeprecation is called after each test run to catch any expectations // without explicit asserts. // assert: function(){ var expecteds = this.expecteds || [], actuals = this.actuals || []; var o, i; if (expecteds !== NONE && expecteds.length === 0 && actuals.length === 0) { return; } if (this.env.runningProdBuild){ QUnit.ok(true, 'deprecations disabled in production builds.'); return; } if (expecteds === NONE) { var actualMessages = []; for (i=0;i 0) { QUnit.deepEqual(templateNames, [], "Ember.TEMPLATES should be empty"); this.env.Ember.TEMPLATES = {}; } } }, restore: function(){} }; exports['default'] = RemainingTemplateAssert; }); enifed('ember-dev/test-helper/remaining-view', ['exports'], function (exports) { 'use strict'; /* globals QUnit */ var RemainingViewAssert = function(env){ this.env = env; }; RemainingViewAssert.prototype = { reset: function(){}, inject: function(){}, assert: function(){ if (this.env.Ember && this.env.Ember.View) { var viewIds = [], id; for (id in this.env.Ember.View.views) { if (this.env.Ember.View.views[id] != null) { viewIds.push(id); } } if (viewIds.length > 0) { QUnit.deepEqual(viewIds, [], "Ember.View.views should be empty"); this.env.Ember.View.views = []; } } }, restore: function(){} }; exports['default'] = RemainingViewAssert; }); enifed('ember-dev/test-helper/run-loop', ['exports'], function (exports) { 'use strict'; /* globals QUnit */ function RunLoopAssertion(env){ this.env = env; } RunLoopAssertion.prototype = { reset: function(){}, inject: function(){}, assert: function(){ var run = this.env.Ember.run; if (run.currentRunLoop) { QUnit.ok(false, "Should not be in a run loop at end of test"); while (run.currentRunLoop) { run.end(); } } if (run.hasScheduledTimers()) { QUnit.ok(false, "Ember run should not have scheduled timers at end of test"); run.cancelTimers(); } }, restore: function(){} }; exports['default'] = RunLoopAssertion; }); enifed('ember-dev/test-helper/setup-qunit', ['exports'], function (exports) { 'use strict'; /* globals QUnit */ function setupQUnit(assertion, _qunitGlobal) { var qunitGlobal = QUnit; if (_qunitGlobal) { qunitGlobal = _qunitGlobal; } var originalModule = qunitGlobal.module; qunitGlobal.module = function(name, _options) { var options = _options || {}; var originalSetup = options.setup || function() { }; var originalTeardown = options.teardown || function() { }; options.setup = function() { assertion.reset(); assertion.inject(); originalSetup.call(this); }; options.teardown = function() { originalTeardown.call(this); assertion.assert(); assertion.restore(); }; return originalModule(name, options); }; } exports['default'] = setupQUnit; }); enifed('ember-dev/test-helper/utils', ['exports'], function (exports) { 'use strict'; exports.buildCompositeAssert = buildCompositeAssert; function callForEach(prop, func) { return function() { for (var i=0, l=this[prop].length;i") }); appendView(view); equal(view.element.firstChild.hasAttribute('disabled'), true, 'attribute is output'); equal(view.element.firstChild.disabled, true, 'boolean property is set true'); }); QUnit.test("disabled property can be set false with a blank string", function() { view = EmberView['default'].create({ context: { isDisabled: '' }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.hasAttribute('disabled'), false, 'attribute is not output'); equal(view.element.firstChild.disabled, false, 'boolean property is set false'); }); QUnit.test("disabled property can be set false", function() { view = EmberView['default'].create({ context: { isDisabled: false }, template: compile['default']("") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is not output"); equal(view.element.firstChild.disabled, false, 'boolean property is set false'); }); QUnit.test("disabled property can be set true with a string", function() { view = EmberView['default'].create({ context: { isDisabled: "oh, no a string" }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.hasAttribute('disabled'), true, 'attribute is output'); equal(view.element.firstChild.disabled, true, 'boolean property is set true'); }); QUnit.test("disabled attribute turns a value to a string", function() { view = EmberView['default'].create({ context: { isDisabled: false }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.hasAttribute('disabled'), true, 'attribute is output'); equal(view.element.firstChild.disabled, true, 'boolean property is set true'); }); QUnit.test("disabled attribute preserves a blank string value", function() { view = EmberView['default'].create({ context: { isDisabled: '' }, template: compile['default']("") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is not output"); equal(view.element.firstChild.disabled, false, 'boolean property is set false'); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/attr_nodes/boolean_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/boolean_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/boolean_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/boolean_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/boolean_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/boolean_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/class_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-template-compiler/system/compile', 'htmlbars-test-helpers'], function (EmberView, run, compile, htmlbars_test_helpers) { 'use strict'; var view; function appendView(view) { run['default'](function() { view.appendTo('#qunit-fixture'); }); } var isInlineIfEnabled = false; isInlineIfEnabled = true; // jscs:disable validateIndentation QUnit.module("ember-htmlbars: class attribute", { teardown: function() { if (view) { run['default'](view, view.destroy); } } }); QUnit.test("class renders before didInsertElement", function() { var matchingElement; view = EmberView['default'].create({ didInsertElement: function() { matchingElement = this.$('div.blue'); }, context: { color: 'blue' }, template: compile['default']("
Hi!
") }); appendView(view); equal(view.element.firstChild.className, 'blue', "attribute is output"); equal(matchingElement.length, 1, 'element is in the DOM when didInsertElement'); }); QUnit.test("class property can contain multiple classes", function() { view = EmberView['default'].create({ context: { classes: 'large blue' }, template: compile['default']("
") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
', "attribute is output"); ok(view.$('.large')[0], 'first class found'); ok(view.$('.blue')[0], 'second class found'); }); QUnit.test("class property is removed when updated with a null value", function() { view = EmberView['default'].create({ context: { "class": 'large' }, template: compile['default']("
") }); appendView(view); equal(view.element.firstChild.className, 'large', "attribute is output"); run['default'](view, view.set, 'context.class', null); equal(view.element.firstChild.className, '', "attribute is removed"); }); QUnit.test("class attribute concats bound values", function() { view = EmberView['default'].create({ context: { size: 'large', color: 'blue' }, template: compile['default']("
") }); appendView(view); ok(view.element.firstChild.className, 'large blue round', 'classes are set'); }); if (isInlineIfEnabled) { QUnit.test("class attribute accepts nested helpers, and updates", function() { view = EmberView['default'].create({ context: { size: 'large', hasColor: true, hasShape: false, shape: 'round' }, template: compile['default']("
") }); appendView(view); ok(view.element.firstChild.className, 'large blue no-shape', 'classes are set'); run['default'](view, view.set, 'context.hasColor', false); run['default'](view, view.set, 'context.hasShape', true); ok(view.element.firstChild.className, 'large round', 'classes are updated'); }); } QUnit.test("class attribute can accept multiple classes from a single value, and update", function() { view = EmberView['default'].create({ context: { size: 'large small' }, template: compile['default']("
") }); appendView(view); ok(view.element.firstChild.className, 'large small', 'classes are set'); run['default'](view, view.set, 'context.size', 'medium'); ok(view.element.firstChild.className, 'medium', 'classes are updated'); }); QUnit.test("class attribute can grok concatted classes, and update", function() { view = EmberView['default'].create({ context: { size: 'large', prefix: 'pre-pre pre', postfix: 'post' }, template: compile['default']("
") }); appendView(view); ok(view.element.firstChild.className, 'btn-large pre-pre pre-post whoop', 'classes are set'); run['default'](view, view.set, 'context.prefix', ''); ok(view.element.firstChild.className, 'btn-large -post whoop', 'classes are updated'); }); QUnit.test("class attribute stays in order", function() { view = EmberView['default'].create({ context: { showA: 'a', showB: 'b' }, template: compile['default']("
") }); appendView(view); run['default'](view, view.set, 'context.showB', false); run['default'](view, view.set, 'context.showB', true); ok(view.element.firstChild.className, 'r b a c', 'classes are in the right order'); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/attr_nodes/class_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/class_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/class_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/class_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/class_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/class_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/data_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-template-compiler/system/compile', 'ember-views/system/renderer', 'htmlbars-test-helpers', 'ember-htmlbars/env', 'ember-runtime/tests/utils'], function (EmberView, run, EmberObject, compile, Renderer, htmlbars_test_helpers, env, utils) { 'use strict'; var view, originalSetAttribute, setAttributeCalls, renderer; QUnit.module("ember-htmlbars: data attribute", { teardown: function() { utils.runDestroy(view); } }); QUnit.test("property is output", function() { view = EmberView['default'].create({ context: { name: 'erik' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is output"); }); QUnit.test("property set before didInsertElement", function() { var matchingElement; view = EmberView['default'].create({ didInsertElement: function() { matchingElement = this.$('div[data-name=erik]'); }, context: { name: 'erik' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is output"); equal(matchingElement.length, 1, 'element is in the DOM when didInsertElement'); }); QUnit.test("quoted attributes are concatenated", function() { view = EmberView['default'].create({ context: { firstName: 'max', lastName: 'jackson' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is output"); }); QUnit.test("quoted attributes are updated when changed", function() { view = EmberView['default'].create({ context: { firstName: 'max', lastName: 'jackson' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "precond - attribute is output"); run['default'](view, view.set, 'context.firstName', 'james'); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is output"); }); QUnit.test("quoted attributes are not removed when value is null", function() { view = EmberView['default'].create({ context: { firstName: 'max', lastName: 'jackson' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); equal(view.element.firstChild.getAttribute('data-name'), 'max', "precond - attribute is output"); run['default'](view, view.set, 'context.firstName', null); equal(view.element.firstChild.getAttribute('data-name'), '', "attribute is output"); }); QUnit.test("unquoted attributes are removed when value is null", function() { view = EmberView['default'].create({ context: { firstName: 'max' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); equal(view.element.firstChild.getAttribute('data-name'), 'max', "precond - attribute is output"); run['default'](view, view.set, 'context.firstName', null); ok(!view.element.firstChild.hasAttribute('data-name'), "attribute is removed output"); }); QUnit.test("unquoted attributes that are null are not added", function() { view = EmberView['default'].create({ context: { firstName: null }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is not present"); }); QUnit.test("unquoted attributes are added when changing from null", function() { view = EmberView['default'].create({ context: { firstName: null }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "precond - attribute is not present"); run['default'](view, view.set, 'context.firstName', 'max'); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is added output"); }); QUnit.test("property value is directly added to attribute", function() { view = EmberView['default'].create({ context: { name: '"" data-foo="blah"' }, template: compile['default']("
Hi!
") }); utils.runAppend(view); equal(view.element.firstChild.getAttribute('data-name'), '"" data-foo="blah"', "attribute is output"); }); QUnit.test("path is output", function() { view = EmberView['default'].create({ context: { name: { firstName: 'erik' } }, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is output"); }); QUnit.test("changed property updates", function() { var context = EmberObject['default'].create({ name: 'erik' }); view = EmberView['default'].create({ context: context, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "precond - attribute is output"); run['default'](context, context.set, 'name', 'mmun'); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is updated output"); }); QUnit.test("updates are scheduled in the render queue", function() { expect(4); var context = EmberObject['default'].create({ name: 'erik' }); view = EmberView['default'].create({ context: context, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "precond - attribute is output"); run['default'](function() { run['default'].schedule('render', function() { htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "precond - attribute is not updated sync"); }); context.set('name', 'mmun'); run['default'].schedule('render', function() { htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is updated output"); }); }); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "attribute is updated output"); }); QUnit.test("updates fail silently after an element is destroyed", function() { var context = EmberObject['default'].create({ name: 'erik' }); view = EmberView['default'].create({ context: context, template: compile['default']("
Hi!
") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, '
Hi!
', "precond - attribute is output"); run['default'](function() { context.set('name', 'mmun'); utils.runDestroy(view); }); }); QUnit.module('ember-htmlbars: {{attribute}} helper -- setAttribute', { setup: function() { renderer = new Renderer['default'](env.domHelper); originalSetAttribute = env.domHelper.setAttribute; env.domHelper.setAttribute = function(element, name, value) { if (name.substr(0, 5) === 'data-') { setAttributeCalls.push([name, value]); } originalSetAttribute.call(env.domHelper, element, name, value); }; setAttributeCalls = []; }, teardown: function() { env.domHelper.setAttribute = originalSetAttribute; utils.runDestroy(view); } }); QUnit.test('calls setAttribute for new values', function() { var context = EmberObject['default'].create({ name: 'erik' }); view = EmberView['default'].create({ renderer: renderer, context: context, template: compile['default']("
Hi!
") }); utils.runAppend(view); run['default'](context, context.set, 'name', 'mmun'); var expected = [ ['data-name', 'erik'], ['data-name', 'mmun'] ]; deepEqual(setAttributeCalls, expected); }); QUnit.test('does not call setAttribute if the same value is set', function() { var context = EmberObject['default'].create({ name: 'erik' }); view = EmberView['default'].create({ renderer: renderer, context: context, template: compile['default']("
Hi!
") }); utils.runAppend(view); run['default'](function() { context.set('name', 'mmun'); context.set('name', 'erik'); }); var expected = [ ['data-name', 'erik'] ]; deepEqual(setAttributeCalls, expected); }); }); enifed('ember-htmlbars/tests/attr_nodes/data_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/data_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/data_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/data_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/data_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/data_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/href_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-template-compiler/system/compile', 'htmlbars-test-helpers'], function (EmberView, run, compile, htmlbars_test_helpers) { 'use strict'; var view; function appendView(view) { run['default'](function() { view.appendTo('#qunit-fixture'); }); } // jscs:disable validateIndentation QUnit.module("ember-htmlbars: href attribute", { teardown: function() { if (view) { run['default'](view, view.destroy); } } }); QUnit.test("href is set", function() { view = EmberView['default'].create({ context: { url: 'http://example.com' }, template: compile['default']("") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/attr_nodes/href_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/href_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/href_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/href_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/href_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/href_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/property_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-template-compiler/system/compile'], function (EmberView, run, compile) { 'use strict'; var view; function appendView(view) { run['default'](function() { view.appendTo('#qunit-fixture'); }); } function canSetFalsyMaxLength() { var input = document.createElement('input'); input.maxLength = 0; return input.maxLength === 0; } // jscs:disable validateIndentation QUnit.module("ember-htmlbars: property", { teardown: function() { if (view) { run['default'](view, view.destroy); } } }); QUnit.test("maxlength sets the property and attribute", function() { view = EmberView['default'].create({ context: { length: 5 }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.maxLength, 5); Ember.run(view, view.set, 'context.length', 1); equal(view.element.firstChild.maxLength, 1); }); QUnit.test("quoted maxlength sets the property and attribute", function() { view = EmberView['default'].create({ context: { length: 5 }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.maxLength, '5'); if (canSetFalsyMaxLength()) { Ember.run(view, view.set, 'context.length', null); equal(view.element.firstChild.maxLength, 0); } else { Ember.run(view, view.set, 'context.length', 1); equal(view.element.firstChild.maxLength, 1); } }); QUnit.test("array value can be set as property", function() { view = EmberView['default'].create({ context: {}, template: compile['default']("") }); appendView(view); Ember.run(view, view.set, 'context.items', [4,5]); ok(true, "no legacy assertion prohibited setting an array"); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/attr_nodes/property_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/property_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/property_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/property_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/property_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/property_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/sanitized_test', ['ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-htmlbars/utils/string', 'ember-runtime/tests/utils', 'ember-metal/environment'], function (EmberView, compile, string, utils, environment) { 'use strict'; /* jshint scripturl:true */ var view; QUnit.module("ember-htmlbars: sanitized attribute", { teardown: function() { utils.runDestroy(view); } }); // jscs:disable validateIndentation var badTags = [ { tag: 'a', attr: 'href', unquotedTemplate: compile['default'](""), quotedTemplate: compile['default'](""), multipartTemplate: compile['default']("") }, { tag: 'body', attr: 'background', unquotedTemplate: compile['default'](""), quotedTemplate: compile['default'](""), multipartTemplate: compile['default']("") }, { tag: 'link', attr: 'href', unquotedTemplate: compile['default'](""), quotedTemplate: compile['default'](""), multipartTemplate: compile['default']("") }, { tag: 'img', attr: 'src', unquotedTemplate: compile['default'](""), quotedTemplate: compile['default'](""), multipartTemplate: compile['default']("") }, { tag: 'iframe', attr: 'src', // Setting an iframe with a bad protocol results in the browser // being redirected. in IE8. Skip the iframe tests on that platform. skip: (environment['default'].hasDOM && document.documentMode && document.documentMode <= 8), unquotedTemplate: compile['default'](""), quotedTemplate: compile['default'](""), multipartTemplate: compile['default']("") } ]; for (var i=0, l=badTags.length; i") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); Ember.run(view, view.set, 'context.viewBoxString', null); equal(view.element.getAttribute('svg'), null, "attribute is removed"); }); QUnit.test("quoted viewBox property is output", function() { var viewBoxString = '0 0 100 100'; view = EmberView['default'].create({ context: { viewBoxString: viewBoxString }, template: compile['default']("") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); }); QUnit.test("quoted viewBox property is concat", function() { var viewBoxString = '100 100'; view = EmberView['default'].create({ context: { viewBoxString: viewBoxString }, template: compile['default']("") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); var newViewBoxString = '200 200'; Ember.run(view, view.set, 'context.viewBoxString', newViewBoxString); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); }); QUnit.test("class is output", function() { view = EmberView['default'].create({ context: { color: 'blue' }, template: compile['default']("") }); appendView(view); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); Ember.run(view, view.set, 'context.color', 'red'); htmlbars_test_helpers.equalInnerHTML(view.element, '', "attribute is output"); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/attr_nodes/svg_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/svg_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/svg_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/svg_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/svg_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/svg_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/value_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-template-compiler/system/compile'], function (EmberView, run, compile) { 'use strict'; var view; function appendView(view) { run['default'](function() { view.appendTo('#qunit-fixture'); }); } // jscs:disable validateIndentation QUnit.module("ember-htmlbars: value attribute", { teardown: function() { if (view) { run['default'](view, view.destroy); } } }); QUnit.test("property is output", function() { view = EmberView['default'].create({ context: { name: 'rick' }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.tagName, 'INPUT', "input element is created"); equal(view.element.firstChild.value, "rick", 'property is set true'); }); QUnit.test("string property is output", function() { view = EmberView['default'].create({ context: { name: 'rick' }, template: compile['default']("") }); appendView(view); equal(view.element.firstChild.tagName, 'INPUT', "input element is created"); equal(view.element.firstChild.value, "rick", 'property is set true'); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/attr_nodes/value_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/value_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/value_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/attr_nodes/value_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/attr_nodes'); test('ember-htmlbars/tests/attr_nodes/value_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/attr_nodes/value_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/compat/handlebars_get_test', ['ember-metal/core', 'ember-views/views/metamorph_view', 'ember-views/views/view', 'ember-htmlbars/compat/handlebars-get', 'ember-runtime/system/container', 'ember-runtime/tests/utils', 'ember-htmlbars/compat'], function (Ember, _MetamorphView, EmberView, handlebarsGet, system__container, utils, EmberHandlebars) { 'use strict'; var compile = EmberHandlebars['default'].compile; var originalLookup = Ember['default'].lookup; var TemplateTests, registry, container, lookup, view; QUnit.module("ember-htmlbars: Ember.Handlebars.get", { setup: function() { Ember['default'].lookup = lookup = {}; registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.optionsForType('helper', { instantiate: false }); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; Ember['default'].lookup = lookup = originalLookup; TemplateTests = null; } }); QUnit.test('it can lookup a path from the current context', function() { expect(1); registry.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; ignoreDeprecation(function() { equal(handlebarsGet['default'](context, path, options), 'bar'); }); }); view = EmberView['default'].create({ container: container, controller: { foo: 'bar' }, template: compile('{{handlebars-get "foo"}}') }); utils.runAppend(view); }); QUnit.test('it can lookup a path from the current keywords', function() { expect(1); registry.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; ignoreDeprecation(function() { equal(handlebarsGet['default'](context, path, options), 'bar'); }); }); view = EmberView['default'].create({ container: container, controller: { foo: 'bar' }, template: compile('{{#with foo as bar}}{{handlebars-get "bar"}}{{/with}}') }); utils.runAppend(view); }); QUnit.test('it can lookup a path from globals', function() { expect(1); lookup.Blammo = { foo: 'blah' }; registry.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; ignoreDeprecation(function() { equal(handlebarsGet['default'](context, path, options), lookup.Blammo.foo); }); }); view = EmberView['default'].create({ container: container, controller: { }, template: compile('{{handlebars-get "Blammo.foo"}}') }); utils.runAppend(view); }); QUnit.test('it raises a deprecation warning on use', function() { expect(1); registry.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; expectDeprecation(function() { handlebarsGet['default'](context, path, options); }, 'Usage of Ember.Handlebars.get is deprecated, use a Component or Ember.Handlebars.makeBoundHelper instead.'); }); view = EmberView['default'].create({ container: container, controller: { foo: 'bar' }, template: compile('{{handlebars-get "foo"}}') }); utils.runAppend(view); }); }); enifed('ember-htmlbars/tests/compat/handlebars_get_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/handlebars_get_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/compat/handlebars_get_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/compat/handlebars_get_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/handlebars_get_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/compat/handlebars_get_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/compat/helper_test', ['ember-htmlbars/compat/helper', 'ember-views/views/view', 'ember-views/views/component', 'ember-htmlbars/system/make-view-helper', 'ember-htmlbars/helpers', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (compat__helper, EmberView, Component, makeViewHelper, helpers, compile, utils) { 'use strict'; var view; // jscs:disable validateIndentation QUnit.module('ember-htmlbars: Handlebars compatible helpers', { teardown: function() { utils.runDestroy(view); delete helpers['default'].test; delete helpers['default']['view-helper']; } }); QUnit.test('wraps provided function so that original path params are provided to the helper', function() { expect(2); function someHelper(param1, param2, options) { equal(param1, 'blammo'); equal(param2, 'blazzico'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('{{test "blammo" "blazzico"}}') }); utils.runAppend(view); }); QUnit.test('combines `env` and `options` for the wrapped helper', function() { expect(1); function someHelper(options) { equal(options.data.view, view); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('{{test}}') }); utils.runAppend(view); }); QUnit.test('adds `hash` into options `options` for the wrapped helper', function() { expect(1); function someHelper(options) { equal(options.hash.bestFriend, 'Jacquie'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('{{test bestFriend="Jacquie"}}') }); utils.runAppend(view); }); QUnit.test('bound `hash` params are provided with their original paths', function() { expect(1); function someHelper(options) { equal(options.hash.bestFriend, 'value'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'Jacquie' }, template: compile['default']('{{test bestFriend=value}}') }); utils.runAppend(view); }); QUnit.test('bound ordered params are provided with their original paths', function() { expect(2); function someHelper(param1, param2, options) { equal(param1, 'first'); equal(param2, 'second'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { first: 'blammo', second: 'blazzico' }, template: compile['default']('{{test first second}}') }); utils.runAppend(view); }); QUnit.test('allows unbound usage within an element', function() { expect(4); function someHelper(param1, param2, options) { equal(param1, 'blammo'); equal(param2, 'blazzico'); return "class='foo'"; } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('
Bar
') }); expectDeprecation(function() { utils.runAppend(view); }, 'Returning a string of attributes from a helper inside an element is deprecated.'); equal(view.$('.foo').length, 1, 'class attribute was added by helper'); }); QUnit.test('registering a helper created from `Ember.Handlebars.makeViewHelper` does not double wrap the helper', function() { expect(1); var ViewHelperComponent = Component['default'].extend({ layout: compile['default']('woot!') }); var helper = makeViewHelper['default'](ViewHelperComponent); compat__helper.registerHandlebarsCompatibleHelper('view-helper', helper); view = EmberView['default'].extend({ template: compile['default']('{{view-helper}}') }).create(); utils.runAppend(view); equal(view.$().text(), 'woot!'); }); QUnit.test('does not add `options.fn` if no block was specified', function() { expect(1); function someHelper(options) { ok(!options.fn, '`options.fn` is not present when block is not specified'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('{{test}}') }); utils.runAppend(view); }); QUnit.test('does not return helper result if block was specified', function() { expect(1); function someHelper(options) { return 'asdf'; } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('{{#test}}lkj;{{/test}}') }); utils.runAppend(view); equal(view.$().text(), ''); }); QUnit.test('allows usage of the template fn', function() { expect(1); function someHelper(options) { options.fn(); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('{{#test}}foo{{/test}}') }); utils.runAppend(view); equal(view.$().text(), 'foo'); }); QUnit.test('ordered param types are added to options.types', function() { expect(3); function someHelper(param1, param2, param3, options) { equal(options.types[0], 'NUMBER'); equal(options.types[1], 'ID'); equal(options.types[2], 'STRING'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { first: 'blammo', second: 'blazzico' }, template: compile['default']('{{test 1 two "3"}}') }); utils.runAppend(view); }); QUnit.test('`hash` params are to options.hashTypes', function() { expect(3); function someHelper(options) { equal(options.hashTypes.string, 'STRING'); equal(options.hashTypes.number, 'NUMBER'); equal(options.hashTypes.id, 'ID'); } compat__helper.registerHandlebarsCompatibleHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'Jacquie' }, template: compile['default']('{{test string="foo" number=42 id=someBoundThing}}') }); utils.runAppend(view); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/compat/helper_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/helper_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/compat/helper_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/compat/helper_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/helper_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/compat/helper_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/compat/make-view-helper_test', ['ember-views/views/view', 'container/registry', 'ember-template-compiler/system/compile', 'ember-htmlbars/system/make-view-helper', 'ember-views/views/component', 'ember-runtime/tests/utils'], function (EmberView, Registry, compile, makeViewHelper, Component, utils) { 'use strict'; var registry, container, view; QUnit.module('ember-htmlbars: makeViewHelper compat', { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('helper', { instantiate: false }); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; } }); QUnit.test('makeViewHelper', function() { expect(1); var ViewHelperComponent = Component['default'].extend({ layout: compile['default']('woot!') }); var helper = makeViewHelper['default'](ViewHelperComponent); registry.register('helper:view-helper', helper); view = EmberView['default'].extend({ template: compile['default']('{{view-helper}}'), container: container }).create(); utils.runAppend(view); equal(view.$().text(), 'woot!'); }); }); enifed('ember-htmlbars/tests/compat/make-view-helper_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/make-view-helper_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/compat/make-view-helper_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/compat/make-view-helper_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/make-view-helper_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/compat/make-view-helper_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/compat/make_bound_helper_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-runtime/system/native_array', 'ember-views/views/simple_bound_view', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-runtime/tests/utils', 'ember-runtime/system/string', 'ember-htmlbars/compat'], function (EmberView, run, EmberObject, native_array, SimpleBoundView, property_get, property_set, utils, string, EmberHandlebars) { 'use strict'; /*jshint newcap:false*/ var compile, helpers, helper; compile = EmberHandlebars['default'].compile; helpers = EmberHandlebars['default'].helpers; helper = EmberHandlebars['default'].helper; var view; var originalLookup = Ember.lookup; function registerRepeatHelper() { expectDeprecationInHTMLBars(); helper('repeat', function(value, options) { var count = options.hash.count || 1; var a = []; while (a.length < count) { a.push(value); } return a.join(''); }); } function expectDeprecationInHTMLBars() { // leave this empty function as a place holder to // enable a deprecation notice } QUnit.module("ember-htmlbars: makeBoundHelper", { setup: function() { }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("primitives should work correctly [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); expectDeprecation('Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); view = EmberView['default'].create({ prims: Ember.A(["string", 12]), template: compile('{{#each view.prims}}{{#if this}}inside-if{{/if}}{{#with this}}inside-with{{/with}}{{/each}}') }); utils.runAppend(view); equal(view.$().text(), 'inside-ifinside-withinside-ifinside-with'); }); QUnit.test("should update bound helpers when properties change", function() { expectDeprecationInHTMLBars(); helper('capitalize', function(value) { return value.toUpperCase(); }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ name: "Brogrammer" }), template: compile("{{capitalize name}}") }); utils.runAppend(view); equal(view.$().text(), 'BROGRAMMER', "helper output is correct"); run['default'](function() { property_set.set(view, 'controller.name', 'wes'); }); equal(view.$().text(), 'WES', "helper output updated"); }); QUnit.test("should update bound helpers in a subexpression when properties change", function() { expectDeprecationInHTMLBars(); helper('dasherize', function(value) { return string.dasherize(value); }); view = EmberView['default'].create({ controller: { prop: "isThing" }, template: compile("
{{prop}}
") }); utils.runAppend(view); equal(view.$('div[data-foo="is-thing"]').text(), 'isThing', "helper output is correct"); run['default'](view, 'set', 'controller.prop', 'notThing'); equal(view.$('div[data-foo="not-thing"]').text(), 'notThing', "helper output is correct"); }); QUnit.test("should allow for computed properties with dependencies", function() { expectDeprecationInHTMLBars(); helper('capitalizeName', function(value) { return property_get.get(value, 'name').toUpperCase(); }, 'name'); view = EmberView['default'].create({ controller: EmberObject['default'].create({ person: EmberObject['default'].create({ name: 'Brogrammer' }) }), template: compile("{{capitalizeName person}}") }); utils.runAppend(view); equal(view.$().text(), 'BROGRAMMER', "helper output is correct"); run['default'](function() { property_set.set(view, 'controller.person.name', 'wes'); }); equal(view.$().text(), 'WES', "helper output updated"); }); QUnit.test("bound helpers should support options", function() { registerRepeatHelper(); view = EmberView['default'].create({ controller: EmberObject['default'].create({ text: 'ab' }), template: compile("{{repeat text count=3}}") }); utils.runAppend(view); equal(view.$().text(), 'ababab', "helper output is correct"); }); QUnit.test("bound helpers should support keywords", function() { expectDeprecationInHTMLBars(); helper('capitalize', function(value) { return value.toUpperCase(); }); view = EmberView['default'].create({ text: 'ab', template: compile("{{capitalize view.text}}") }); utils.runAppend(view); equal(view.$().text(), 'AB', "helper output is correct"); }); QUnit.test("bound helpers should support global paths [DEPRECATED]", function() { expectDeprecationInHTMLBars(); helper('capitalize', function(value) { return value.toUpperCase(); }); Ember.lookup = { Text: 'ab' }; view = EmberView['default'].create({ template: compile("{{capitalize Text}}") }); expectDeprecation(function() { utils.runAppend(view); }, /Global lookup of Text from a Handlebars template is deprecated/); equal(view.$().text(), 'AB', "helper output is correct"); }); QUnit.test("bound helper should support this keyword", function() { expectDeprecationInHTMLBars(); helper('capitalize', function(value) { return property_get.get(value, 'text').toUpperCase(); }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ text: 'ab' }), template: compile("{{capitalize this}}") }); utils.runAppend(view); equal(view.$().text(), 'AB', "helper output is correct"); }); QUnit.test("bound helpers should support bound options", function() { registerRepeatHelper(); view = EmberView['default'].create({ controller: EmberObject['default'].create({ text: 'ab', numRepeats: 3 }), template: compile('{{repeat text countBinding="numRepeats"}}') }); utils.runAppend(view); equal(view.$().text(), 'ababab', "helper output is correct"); run['default'](function() { view.set('controller.numRepeats', 4); }); equal(view.$().text(), 'abababab', "helper correctly re-rendered after bound option was changed"); run['default'](function() { view.set('controller.numRepeats', 2); view.set('controller.text', "YES"); }); equal(view.$().text(), 'YESYES', "helper correctly re-rendered after both bound option and property changed"); }); QUnit.test("bound helpers should support unquoted values as bound options", function() { registerRepeatHelper(); view = EmberView['default'].create({ controller: EmberObject['default'].create({ text: 'ab', numRepeats: 3 }), template: compile('{{repeat text count=numRepeats}}') }); utils.runAppend(view); equal(view.$().text(), 'ababab', "helper output is correct"); run['default'](function() { view.set('controller.numRepeats', 4); }); equal(view.$().text(), 'abababab', "helper correctly re-rendered after bound option was changed"); run['default'](function() { view.set('controller.numRepeats', 2); view.set('controller.text', "YES"); }); equal(view.$().text(), 'YESYES', "helper correctly re-rendered after both bound option and property changed"); }); QUnit.test("bound helpers should support multiple bound properties", function() { expectDeprecationInHTMLBars(); helper('combine', function() { return [].slice.call(arguments, 0, -1).join(''); }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ thing1: 'ZOID', thing2: 'BERG' }), template: compile('{{combine thing1 thing2}}') }); utils.runAppend(view); equal(view.$().text(), 'ZOIDBERG', "helper output is correct"); run['default'](function() { view.set('controller.thing2', "NERD"); }); equal(view.$().text(), 'ZOIDNERD', "helper correctly re-rendered after second bound helper property changed"); run['default'](function() { view.get('controller').setProperties({ thing1: "WOOT", thing2: "YEAH" }); }); equal(view.$().text(), 'WOOTYEAH', "helper correctly re-rendered after both bound helper properties changed"); }); QUnit.test("bound helpers should expose property names in options.data.properties", function() { expectDeprecationInHTMLBars(); helper('echo', function() { var options = arguments[arguments.length - 1]; var values = [].slice.call(arguments, 0, -1); var a = []; for (var i = 0; i < values.length; ++i) { var propertyName = options.data.properties[i]; a.push(propertyName); } return a.join(' '); }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ thing1: 'ZOID', thing2: 'BERG', thing3: EmberObject['default'].create({ foo: 123 }) }), template: compile('{{echo thing1 thing2 thing3.foo}}') }); utils.runAppend(view); equal(view.$().text(), 'thing1 thing2 thing3.foo', "helper output is correct"); }); QUnit.test("bound helpers can be invoked with zero args", function() { expectDeprecationInHTMLBars(); helper('troll', function(options) { return options.hash.text || "TROLOLOL"; }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ trollText: "yumad" }), template: compile('{{troll}} and {{troll text="bork"}}') }); utils.runAppend(view); equal(view.$().text(), 'TROLOLOL and bork', "helper output is correct"); }); QUnit.test("bound helpers should not be invoked with blocks", function() { registerRepeatHelper(); view = EmberView['default'].create({ controller: EmberObject['default'].create({}), template: compile("{{#repeat}}Sorry, Charlie{{/repeat}}") }); expectAssertion(function() { utils.runAppend(view); }, /registerBoundHelper-generated helpers do not support use with Handlebars blocks/i); }); QUnit.test("should observe dependent keys passed to registerBoundHelper", function() { try { expectDeprecationInHTMLBars(); var simplyObject = EmberObject['default'].create({ firstName: 'Jim', lastName: 'Owen', birthday: EmberObject['default'].create({ year: '2009' }) }); helper('fullName', function(value) { return [ value.get('firstName'), value.get('lastName'), value.get('birthday.year') ].join(' '); }, 'firstName', 'lastName', 'birthday.year'); view = EmberView['default'].create({ template: compile('{{fullName this}}'), context: simplyObject }); utils.runAppend(view); equal(view.$().text(), 'Jim Owen 2009', 'simply render the helper'); run['default'](simplyObject, simplyObject.set, 'firstName', 'Tom'); equal(view.$().text(), 'Tom Owen 2009', 'render the helper after prop change'); run['default'](simplyObject, simplyObject.set, 'birthday.year', '1692'); equal(view.$().text(), 'Tom Owen 1692', 'render the helper after path change'); } finally { delete helpers['fullName']; } }); QUnit.test("shouldn't treat raw numbers as bound paths", function() { expectDeprecationInHTMLBars(); helper('sum', function(a, b) { return a + b; }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ aNumber: 1 }), template: compile("{{sum aNumber 1}} {{sum 0 aNumber}} {{sum 5 6}}") }); utils.runAppend(view); equal(view.$().text(), '2 1 11', "helper output is correct"); run['default'](view, 'set', 'controller.aNumber', 5); equal(view.$().text(), '6 5 11', "helper still updates as expected"); }); QUnit.test("shouldn't treat quoted strings as bound paths", function() { expectDeprecationInHTMLBars(); var helperCount = 0; helper('combine', function(a, b, opt) { helperCount++; return a + b; }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ word: "jerkwater", loo: "unused" }), template: compile("{{combine word 'loo'}} {{combine '' word}} {{combine 'will' \"didi\"}}") }); utils.runAppend(view); equal(view.$().text(), 'jerkwaterloo jerkwater willdidi', "helper output is correct"); run['default'](view, 'set', 'controller.word', 'bird'); equal(view.$().text(), 'birdloo bird willdidi', "helper still updates as expected"); run['default'](view, 'set', 'controller.loo', 'soup-de-doo'); equal(view.$().text(), 'birdloo bird willdidi', "helper still updates as expected"); equal(helperCount, 5, "changing controller property with same name as quoted string doesn't re-render helper"); }); QUnit.test("bound helpers can handle nulls in array (with primitives) [DEPRECATED]", function() { expectDeprecationInHTMLBars(); helper('reverse', function(val) { return val ? val.split('').reverse().join('') : "NOPE"; }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ things: native_array.A([null, 0, undefined, false, "OMG"]) }), template: compile("{{#each things}}{{this}}|{{reverse this}} {{/each}}{{#each thing in things}}{{thing}}|{{reverse thing}} {{/each}}") }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), '|NOPE 0|NOPE |NOPE false|NOPE OMG|GMO |NOPE 0|NOPE |NOPE false|NOPE OMG|GMO ', "helper output is correct"); run['default'](function() { view.get('controller.things').pushObject('blorg'); view.get('controller.things').shiftObject(); }); equal(view.$().text(), '0|NOPE |NOPE false|NOPE OMG|GMO blorg|grolb 0|NOPE |NOPE false|NOPE OMG|GMO blorg|grolb ', "helper output is still correct"); }); QUnit.test("bound helpers can handle nulls in array (with objects)", function() { expectDeprecationInHTMLBars(); helper('print-foo', function(val) { return val ? property_get.get(val, 'foo') : "NOPE"; }); view = EmberView['default'].create({ controller: EmberObject['default'].create({ things: native_array.A([null, { foo: 5 }]) }), template: compile("{{#each things}}{{foo}}|{{print-foo this}} {{/each}}{{#each thing in things}}{{thing.foo}}|{{print-foo thing}} {{/each}}") }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), '|NOPE 5|5 |NOPE 5|5 ', "helper output is correct"); run['default'](view.get('controller.things'), 'pushObject', { foo: 6 }); equal(view.$().text(), '|NOPE 5|5 6|6 |NOPE 5|5 6|6 ', "helper output is correct"); }); QUnit.test("bound helpers can handle `this` keyword when it's a non-object", function() { expectDeprecationInHTMLBars(); helper("shout", function(value) { return value + '!'; }); view = EmberView['default'].create({ context: 'alex', template: compile("{{shout this}}") }); utils.runAppend(view); equal(view.$().text(), 'alex!', "helper output is correct"); run['default'](function() { property_set.set(view, 'context', ''); }); equal(view.$().text(), '!', "helper output is correct"); run['default'](function() { property_set.set(view, 'context', 'wallace'); }); equal(view.$().text(), 'wallace!', "helper output is correct"); }); QUnit.test("should have correct argument types", function() { expectDeprecationInHTMLBars(); helper('getType', function(value) { return typeof value; }); view = EmberView['default'].create({ controller: EmberObject['default'].create(), template: compile('{{getType null}}, {{getType undefProp}}, {{getType "string"}}, {{getType 1}}, {{getType}}') }); utils.runAppend(view); equal(view.$().text(), 'undefined, undefined, string, number, object', "helper output is correct"); }); QUnit.test("when no parameters are bound, no new views are created", function() { registerRepeatHelper(); var originalRender = SimpleBoundView['default'].prototype.render; var renderWasCalled = false; SimpleBoundView['default'].prototype.render = function() { renderWasCalled = true; return originalRender.apply(this, arguments); }; try { view = EmberView['default'].create({ template: compile('{{repeat "a"}}'), controller: EmberObject['default'].create() }); utils.runAppend(view); } finally { SimpleBoundView['default'].prototype.render = originalRender; } ok(!renderWasCalled, 'simple bound view should not have been created and rendered'); equal(view.$().text(), 'a'); }); QUnit.test('when no hash parameters are bound, no new views are created', function() { registerRepeatHelper(); var originalRender = SimpleBoundView['default'].prototype.render; var renderWasCalled = false; SimpleBoundView['default'].prototype.render = function() { renderWasCalled = true; return originalRender.apply(this, arguments); }; try { view = EmberView['default'].create({ template: compile('{{repeat "a" count=3}}'), controller: EmberObject['default'].create() }); utils.runAppend(view); } finally { SimpleBoundView['default'].prototype.render = originalRender; } ok(!renderWasCalled, 'simple bound view should not have been created and rendered'); equal(view.$().text(), 'aaa'); }); }); enifed('ember-htmlbars/tests/compat/make_bound_helper_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/make_bound_helper_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/compat/make_bound_helper_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/compat/make_bound_helper_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/make_bound_helper_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/compat/make_bound_helper_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/compat/precompile_test', ['ember-htmlbars/compat'], function (EmberHandlebars) { 'use strict'; var precompile = EmberHandlebars['default'].precompile; var parse = EmberHandlebars['default'].parse; var template = 'Hello World'; var result; QUnit.module("ember-htmlbars: Ember.Handlebars.precompile"); QUnit.test("precompile creates an object when asObject isn't defined", function() { result = precompile(template); equal(typeof(result), "object"); }); QUnit.test("precompile creates an object when asObject is true", function() { result = precompile(template, true); equal(typeof(result), "object"); }); QUnit.test("precompile creates a string when asObject is false", function() { result = precompile(template, false); equal(typeof(result), "string"); }); if (!Ember.FEATURES.isEnabled('ember-htmlbars')) { // jscs:disable validateIndentation QUnit.test("precompile creates an object when passed an AST", function() { var ast = parse(template); result = precompile(ast); equal(typeof(result), "object"); }); // jscs:enable validateIndentation } }); enifed('ember-htmlbars/tests/compat/precompile_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/precompile_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/compat/precompile_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/compat/precompile_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/compat'); test('ember-htmlbars/tests/compat/precompile_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/compat/precompile_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/bind_attr_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-runtime/system/namespace', 'ember-views/views/view', 'ember-views/views/metamorph_view', 'ember-runtime/system/object', 'ember-runtime/system/native_array', 'ember-metal/computed', 'ember-metal/observer', 'ember-runtime/system/container', 'ember-metal/property_set', 'ember-runtime/tests/utils', 'ember-htmlbars/helpers', 'ember-template-compiler/system/compile'], function (Ember, run, Namespace, EmberView, _MetamorphView, EmberObject, native_array, computed, observer, system__container, property_set, utils, helpers, compile) { 'use strict'; /*jshint newcap:false*/ var view; var originalLookup = Ember['default'].lookup; var TemplateTests, registry, container, lookup; /** This module specifically tests integration with Handlebars and Ember-specific Handlebars extensions. If you add additional template support to View, you should create a new file in which to test. */ QUnit.module("ember-htmlbars: {{bind-attr}}", { setup: function() { Ember['default'].lookup = lookup = {}; lookup.TemplateTests = TemplateTests = Namespace['default'].create(); registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; Ember['default'].lookup = lookup = originalLookup; TemplateTests = null; } }); QUnit.test("should be able to bind element attributes using {{bind-attr}}", function() { var template = compile['default']('view.content.title}}'); view = EmberView['default'].create({ template: template, content: EmberObject['default'].create({ url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }) }); utils.runAppend(view); equal(view.$('img').attr('src'), "http://www.emberjs.com/assets/images/logo.png", "sets src attribute"); equal(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute"); run['default'](function() { property_set.set(view, 'content.title', "El logo de Eember"); }); equal(view.$('img').attr('alt'), "El logo de Eember", "updates alt attribute when content's title attribute changes"); run['default'](function() { property_set.set(view, 'content', EmberObject['default'].create({ url: "http://www.thegooglez.com/theydonnothing", title: "I CAN HAZ SEARCH" })); }); equal(view.$('img').attr('alt'), "I CAN HAZ SEARCH", "updates alt attribute when content object changes"); run['default'](function() { property_set.set(view, 'content', { url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }); }); equal(view.$('img').attr('alt'), "The SproutCore Logo", "updates alt attribute when content object is a hash"); run['default'](function() { property_set.set(view, 'content', EmberObject['default'].createWithMixins({ url: "http://www.emberjs.com/assets/images/logo.png", title: computed.computed(function() { return "Nanananana Ember!"; }) })); }); equal(view.$('img').attr('alt'), "Nanananana Ember!", "updates alt attribute when title property is computed"); }); QUnit.test("should be able to bind to view attributes with {{bind-attr}}", function() { view = EmberView['default'].create({ value: 'Test', template: compile['default']('view.value}}') }); utils.runAppend(view); equal(view.$('img').attr('alt'), "Test", "renders initial value"); run['default'](function() { view.set('value', 'Updated'); }); equal(view.$('img').attr('alt'), "Updated", "updates value"); }); QUnit.test("should be able to bind to globals with {{bind-attr}} (DEPRECATED)", function() { TemplateTests.set('value', 'Test'); view = EmberView['default'].create({ template: compile['default']('TemplateTests.value}}') }); expectDeprecation(function() { utils.runAppend(view); }, /Global lookup of TemplateTests.value from a Handlebars template is deprecated/); equal(view.$('img').attr('alt'), "Test", "renders initial value"); }); QUnit.test("should not allow XSS injection via {{bind-attr}}", function() { view = EmberView['default'].create({ template: compile['default']('view.content.value}}'), content: { value: 'Trololol" onmouseover="alert(\'HAX!\');' } }); utils.runAppend(view); equal(view.$('img').attr('onmouseover'), undefined); // If the whole string is here, then it means we got properly escaped equal(view.$('img').attr('alt'), 'Trololol" onmouseover="alert(\'HAX!\');'); }); QUnit.test("should be able to bind use {{bind-attr}} more than once on an element", function() { var template = compile['default']('view.content.title}}'); view = EmberView['default'].create({ template: template, content: EmberObject['default'].create({ url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }) }); utils.runAppend(view); equal(view.$('img').attr('src'), "http://www.emberjs.com/assets/images/logo.png", "sets src attribute"); equal(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute"); run['default'](function() { property_set.set(view, 'content.title', "El logo de Eember"); }); equal(view.$('img').attr('alt'), "El logo de Eember", "updates alt attribute when content's title attribute changes"); run['default'](function() { property_set.set(view, 'content', EmberObject['default'].create({ url: "http://www.thegooglez.com/theydonnothing", title: "I CAN HAZ SEARCH" })); }); equal(view.$('img').attr('alt'), "I CAN HAZ SEARCH", "updates alt attribute when content object changes"); run['default'](function() { property_set.set(view, 'content', { url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }); }); equal(view.$('img').attr('alt'), "The SproutCore Logo", "updates alt attribute when content object is a hash"); run['default'](function() { property_set.set(view, 'content', EmberObject['default'].createWithMixins({ url: "http://www.emberjs.com/assets/images/logo.png", title: computed.computed(function() { return "Nanananana Ember!"; }) })); }); equal(view.$('img').attr('alt'), "Nanananana Ember!", "updates alt attribute when title property is computed"); }); QUnit.test("{{bindAttr}} is aliased to {{bind-attr}}", function() { expect(4); var originalBindAttr = helpers['default']['bind-attr']; try { helpers['default']['bind-attr'] = { helperFunction: function() { equal(arguments[0], 'foo', 'First arg match'); equal(arguments[1], 'bar', 'Second arg match'); return 'result'; } }; expectDeprecation(function() { var result; result = helpers['default'].bindAttr.helperFunction('foo', 'bar'); equal(result, 'result', 'Result match'); }, "The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); } finally { helpers['default']['bind-attr'] = originalBindAttr; } }); QUnit.test("{{bindAttr}} can be used to bind attributes [DEPRECATED]", function() { expect(3); view = EmberView['default'].create({ value: 'Test', template: compile['default']('view.value}}') }); expectDeprecation(function() { utils.runAppend(view); }, /The 'bindAttr' view helper is deprecated in favor of 'bind-attr'/); equal(view.$('img').attr('alt'), "Test", "renders initial value"); run['default'](function() { view.set('value', 'Updated'); }); equal(view.$('img').attr('alt'), "Updated", "updates value"); }); QUnit.test("should be able to bind element attributes using {{bind-attr}} inside a block", function() { var template = compile['default']('{{#with view.content as image}}image.title}}{{/with}}'); view = EmberView['default'].create({ template: template, content: EmberObject['default'].create({ url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }) }); utils.runAppend(view); equal(view.$('img').attr('src'), "http://www.emberjs.com/assets/images/logo.png", "sets src attribute"); equal(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute"); run['default'](function() { property_set.set(view, 'content.title', "El logo de Eember"); }); equal(view.$('img').attr('alt'), "El logo de Eember", "updates alt attribute when content's title attribute changes"); }); QUnit.test("should be able to bind class attribute with {{bind-attr}}", function() { var template = compile['default'](''); view = EmberView['default'].create({ template: template, foo: 'bar' }); utils.runAppend(view); equal(view.element.firstChild.className, 'bar', 'renders class'); run['default'](function() { property_set.set(view, 'foo', 'baz'); }); equal(view.element.firstChild.className, 'baz', 'updates rendered class'); }); QUnit.test("should be able to bind unquoted class attribute with {{bind-attr}}", function() { var template = compile['default'](''); view = EmberView['default'].create({ template: template, foo: 'bar' }); utils.runAppend(view); equal(view.$('img').attr('class'), 'bar', "renders class"); run['default'](function() { property_set.set(view, 'foo', 'baz'); }); equal(view.$('img').attr('class'), 'baz', "updates class"); }); QUnit.test("should be able to bind class attribute via a truthy property with {{bind-attr}}", function() { var template = compile['default'](''); view = EmberView['default'].create({ template: template, isNumber: 5 }); utils.runAppend(view); equal(view.element.firstChild.className, 'is-truthy', 'renders class'); run['default'](function() { property_set.set(view, 'isNumber', 0); }); ok(view.element.firstChild.className !== 'is-truthy', 'removes class'); }); QUnit.test("should be able to bind class to view attribute with {{bind-attr}}", function() { var template = compile['default'](''); view = EmberView['default'].create({ template: template, foo: 'bar' }); utils.runAppend(view); equal(view.$('img').attr('class'), 'bar', "renders class"); run['default'](function() { property_set.set(view, 'foo', 'baz'); }); equal(view.$('img').attr('class'), 'baz', "updates class"); }); QUnit.test("should not allow XSS injection via {{bind-attr}} with class", function() { view = EmberView['default'].create({ template: compile['default'](''), foo: '" onmouseover="alert(\'I am in your classes hacking your app\');' }); try { utils.runAppend(view); } catch (e) { } equal(view.$('img').attr('onmouseover'), undefined); }); QUnit.test("should be able to bind class attribute using ternary operator in {{bind-attr}}", function() { var template = compile['default'](''); var content = EmberObject['default'].create({ isDisabled: true }); view = EmberView['default'].create({ template: template, content: content }); utils.runAppend(view); ok(view.$('img').hasClass('disabled'), 'disabled class is rendered'); ok(!view.$('img').hasClass('enabled'), 'enabled class is not rendered'); run['default'](function() { property_set.set(content, 'isDisabled', false); }); ok(!view.$('img').hasClass('disabled'), 'disabled class is not rendered'); ok(view.$('img').hasClass('enabled'), 'enabled class is rendered'); }); QUnit.test("should be able to add multiple classes using {{bind-attr class}}", function() { var template = compile['default']('
'); var content = EmberObject['default'].create({ isAwesomeSauce: true, isAlsoCool: true, isAmazing: true, isEnabled: true }); view = EmberView['default'].create({ template: template, content: content }); utils.runAppend(view); ok(view.$('div').hasClass('is-awesome-sauce'), "dasherizes first property and sets classname"); ok(view.$('div').hasClass('is-also-cool'), "dasherizes second property and sets classname"); ok(view.$('div').hasClass('amazing'), "uses alias for third property and sets classname"); ok(view.$('div').hasClass('is-super-duper'), "static class is present"); ok(view.$('div').hasClass('enabled'), "truthy class in ternary classname definition is rendered"); ok(!view.$('div').hasClass('disabled'), "falsy class in ternary classname definition is not rendered"); run['default'](function() { property_set.set(content, 'isAwesomeSauce', false); property_set.set(content, 'isAmazing', false); property_set.set(content, 'isEnabled', false); }); ok(!view.$('div').hasClass('is-awesome-sauce'), "removes dasherized class when property is set to false"); ok(!view.$('div').hasClass('amazing'), "removes aliased class when property is set to false"); ok(view.$('div').hasClass('is-super-duper'), "static class is still present"); ok(!view.$('div').hasClass('enabled'), "truthy class in ternary classname definition is not rendered"); ok(view.$('div').hasClass('disabled'), "falsy class in ternary classname definition is rendered"); }); QUnit.test("should be able to bind classes to globals with {{bind-attr class}} (DEPRECATED)", function() { TemplateTests.set('isOpen', true); view = EmberView['default'].create({ template: compile['default']('') }); expectDeprecation(function() { utils.runAppend(view); }, /Global lookup of TemplateTests.isOpen from a Handlebars template is deprecated/); ok(view.$('img').hasClass('is-open'), "sets classname to the dasherized value of the global property"); }); QUnit.test("should be able to bind-attr to 'this' in an {{#each}} block [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); view = EmberView['default'].create({ template: compile['default']('{{#each view.images}}{{/each}}'), images: native_array.A(['one.png', 'two.jpg', 'three.gif']) }); utils.runAppend(view); var images = view.$('img'); ok(/one\.png$/.test(images[0].src)); ok(/two\.jpg$/.test(images[1].src)); ok(/three\.gif$/.test(images[2].src)); }); QUnit.test("should be able to bind classes to 'this' in an {{#each}} block with {{bind-attr class}} [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); view = EmberView['default'].create({ template: compile['default']('{{#each view.items}}
  • Item
  • {{/each}}'), items: native_array.A(['a', 'b', 'c']) }); utils.runAppend(view); ok(view.$('li').eq(0).hasClass('a'), "sets classname to the value of the first item"); ok(view.$('li').eq(1).hasClass('b'), "sets classname to the value of the second item"); ok(view.$('li').eq(2).hasClass('c'), "sets classname to the value of the third item"); }); QUnit.test("should be able to bind-attr to var in {{#each var in list}} block", function() { view = EmberView['default'].create({ template: compile['default']('{{#each image in view.images}}{{/each}}'), images: native_array.A(['one.png', 'two.jpg', 'three.gif']) }); utils.runAppend(view); var images = view.$('img'); ok(/one\.png$/.test(images[0].src)); ok(/two\.jpg$/.test(images[1].src)); ok(/three\.gif$/.test(images[2].src)); run['default'](function() { var imagesArray = view.get('images'); imagesArray.removeAt(0); }); images = view.$('img'); ok(images.length === 2, ""); ok(/two\.jpg$/.test(images[0].src)); ok(/three\.gif$/.test(images[1].src)); }); QUnit.test("should teardown observers from bind-attr on rerender", function() { view = EmberView['default'].create({ template: compile['default']('wat'), foo: 'bar' }); utils.runAppend(view); equal(observer.observersFor(view, 'foo').length, 1); run['default'](function() { view.rerender(); }); equal(observer.observersFor(view, 'foo').length, 1); }); QUnit.test("should keep class in the order it appears in", function() { view = EmberView['default'].create({ template: compile['default']('') }); utils.runAppend(view); equal(view.element.firstChild.className, 'foo baz', 'classes are in expected order'); }); QUnit.test('should allow either quoted or unquoted values', function() { view = EmberView['default'].create({ value: 'Test', source: 'test.jpg', template: compile['default']('view.value') }); utils.runAppend(view); equal(view.$('img').attr('alt'), "Test", "renders initial value"); equal(view.$('img').attr('src'), "test.jpg", "renders initial value"); run['default'](function() { view.set('value', 'Updated'); view.set('source', 'test2.jpg'); }); equal(view.$('img').attr('alt'), "Updated", "updates value"); equal(view.$('img').attr('src'), "test2.jpg", "updates value"); }); QUnit.test("property before didInsertElement", function() { var matchingElement; view = EmberView['default'].create({ name: 'bob', template: compile['default']('
    '), didInsertElement: function() { matchingElement = this.$('div[alt=bob]'); } }); utils.runAppend(view); equal(matchingElement.length, 1, 'element is in the DOM when didInsertElement'); }); QUnit.test("asserts for
    ", function() { var template = compile['default']('
    '); view = EmberView['default'].create({ template: template, foo: 'bar' }); expectAssertion(function() { utils.runAppend(view); }, /You cannot set `class` manually and via `{{bind-attr}}` helper on the same element/); }); QUnit.test("asserts for
    ", function() { var template = compile['default']('
    '); view = EmberView['default'].create({ template: template, blah: 'bar' }); expectAssertion(function() { utils.runAppend(view); }, /You cannot set `data-bar` manually and via `{{bind-attr}}` helper on the same element/); }); }); enifed('ember-htmlbars/tests/helpers/bind_attr_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/bind_attr_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/bind_attr_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/bind_attr_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/bind_attr_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/bind_attr_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/collection_test', ['ember-views/views/collection_view', 'ember-runtime/system/object', 'ember-views/views/view', 'ember-runtime/system/array_proxy', 'ember-runtime/system/namespace', 'ember-runtime/system/container', 'ember-runtime/system/native_array', 'ember-metal/run_loop', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-views/system/jquery', 'ember-metal/computed', 'ember-runtime/tests/utils', 'ember-template-compiler/system/compile'], function (CollectionView, EmberObject, EmberView, ArrayProxy, Namespace, system__container, native_array, run, property_get, property_set, jQuery, computed, utils, compile) { 'use strict'; /*jshint newcap:false*/ var trim = jQuery['default'].trim; var view; var originalLookup = Ember.lookup; var TemplateTests, registry, container, lookup; function nthChild(view, nth) { return property_get.get(view, 'childViews').objectAt(nth || 0); } var firstChild = nthChild; function firstGrandchild(view) { return property_get.get(property_get.get(view, 'childViews').objectAt(0), 'childViews').objectAt(0); } QUnit.module("collection helper", { setup: function() { Ember.lookup = lookup = {}; lookup.TemplateTests = TemplateTests = Namespace['default'].create(); registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); // registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; Ember.lookup = lookup = originalLookup; TemplateTests = null; } }); QUnit.test("Collection views that specify an example view class have their children be of that class", function() { var ExampleViewCollection = CollectionView['default'].extend({ itemViewClass: EmberView['default'].extend({ isCustom: true }), content: native_array.A(['foo']) }); view = EmberView['default'].create({ exampleViewCollection: ExampleViewCollection, template: compile['default']('{{#collection view.exampleViewCollection}}OHAI{{/collection}}') }); utils.runAppend(view); ok(firstGrandchild(view).isCustom, "uses the example view class"); }); QUnit.test("itemViewClass works in the #collection helper with a global (DEPRECATED)", function() { TemplateTests.ExampleItemView = EmberView['default'].extend({ isAlsoCustom: true }); view = EmberView['default'].create({ exampleController: ArrayProxy['default'].create({ content: native_array.A(['alpha']) }), template: compile['default']('{{#collection content=view.exampleController itemViewClass=TemplateTests.ExampleItemView}}beta{{/collection}}') }); var deprecation = /Resolved the view "TemplateTests.ExampleItemView" on the global context/; deprecation = /Global lookup of TemplateTests.ExampleItemView from a Handlebars template is deprecated/; expectDeprecation(function() { utils.runAppend(view); }, deprecation); ok(firstGrandchild(view).isAlsoCustom, "uses the example view class specified in the #collection helper"); }); QUnit.test("itemViewClass works in the #collection helper with a property", function() { var ExampleItemView = EmberView['default'].extend({ isAlsoCustom: true }); var ExampleCollectionView = CollectionView['default']; view = EmberView['default'].create({ possibleItemView: ExampleItemView, exampleCollectionView: ExampleCollectionView, exampleController: ArrayProxy['default'].create({ content: native_array.A(['alpha']) }), template: compile['default']('{{#collection view.exampleCollectionView content=view.exampleController itemViewClass=view.possibleItemView}}beta{{/collection}}') }); utils.runAppend(view); ok(firstGrandchild(view).isAlsoCustom, "uses the example view class specified in the #collection helper"); }); QUnit.test("itemViewClass works in the #collection via container", function() { registry.register('view:example-item', EmberView['default'].extend({ isAlsoCustom: true })); view = EmberView['default'].create({ container: container, exampleCollectionView: CollectionView['default'].extend(), exampleController: ArrayProxy['default'].create({ content: native_array.A(['alpha']) }), template: compile['default']('{{#collection view.exampleCollectionView content=view.exampleController itemViewClass="example-item"}}beta{{/collection}}') }); utils.runAppend(view); ok(firstGrandchild(view).isAlsoCustom, "uses the example view class specified in the #collection helper"); }); QUnit.test("passing a block to the collection helper sets it as the template for example views", function() { var CollectionTestView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }); view = EmberView['default'].create({ collectionTestView: CollectionTestView, template: compile['default']('{{#collection view.collectionTestView}} {{/collection}}') }); utils.runAppend(view); equal(view.$('label').length, 3, 'one label element is created for each content item'); }); QUnit.test("collection helper should try to use container to resolve view", function() { var registry = new system__container.Registry(); var container = registry.container(); var ACollectionView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }); registry.register('view:collectionTest', ACollectionView); var controller = { container: container }; view = EmberView['default'].create({ controller: controller, template: compile['default']('{{#collection "collectionTest"}} {{/collection}}') }); utils.runAppend(view); equal(view.$('label').length, 3, 'one label element is created for each content item'); }); QUnit.test("collection helper should accept relative paths", function() { view = EmberView['default'].create({ template: compile['default']('{{#collection view.collection}} {{/collection}}'), collection: CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }) }); utils.runAppend(view); equal(view.$('label').length, 3, 'one label element is created for each content item'); }); QUnit.test("empty views should be removed when content is added to the collection (regression, ht: msofaer)", function() { var EmptyView = EmberView['default'].extend({ template : compile['default']("
    ") }); var ListView = CollectionView['default'].extend({ emptyView: EmptyView }); var listController = ArrayProxy['default'].create({ content : native_array.A() }); view = EmberView['default'].create({ listView: ListView, listController: listController, template: compile['default']('{{#collection view.listView content=view.listController tagName="table"}} {{/collection}}') }); utils.runAppend(view); equal(view.$('tr').length, 1, 'Make sure the empty view is there (regression)'); run['default'](function() { listController.pushObject({ title : "Go Away, Placeholder Row!" }); }); equal(view.$('tr').length, 1, 'has one row'); equal(view.$('tr:nth-child(1) td').text(), 'Go Away, Placeholder Row!', 'The content is the updated data.'); }); QUnit.test("should be able to specify which class should be used for the empty view", function() { var App; run['default'](function() { lookup.App = App = Namespace['default'].create(); }); var EmptyView = EmberView['default'].extend({ template: compile['default']('This is an empty view') }); view = EmberView['default'].create({ container: { lookupFactory: function() { return EmptyView; } }, template: compile['default']('{{collection emptyViewClass="empty-view"}}') }); utils.runAppend(view); equal(view.$().text(), 'This is an empty view', "Empty view should be rendered."); utils.runDestroy(App); }); QUnit.test("if no content is passed, and no 'else' is specified, nothing is rendered", function() { var CollectionTestView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A() }); view = EmberView['default'].create({ collectionTestView: CollectionTestView, template: compile['default']('{{#collection view.collectionTestView}} {{/collection}}') }); utils.runAppend(view); equal(view.$('li').length, 0, 'if no "else" is specified, nothing is rendered'); }); QUnit.test("if no content is passed, and 'else' is specified, the else block is rendered", function() { var CollectionTestView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A() }); view = EmberView['default'].create({ collectionTestView: CollectionTestView, template: compile['default']('{{#collection view.collectionTestView}} {{ else }} {{/collection}}') }); utils.runAppend(view); equal(view.$('li:has(del)').length, 1, 'the else block is rendered'); }); QUnit.test("a block passed to a collection helper defaults to the content property of the context", function() { var CollectionTestView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }); view = EmberView['default'].create({ collectionTestView: CollectionTestView, template: compile['default']('{{#collection view.collectionTestView}} {{/collection}}') }); utils.runAppend(view); equal(view.$('li:nth-child(1) label').length, 1); equal(view.$('li:nth-child(1) label').text(), 'foo'); equal(view.$('li:nth-child(2) label').length, 1); equal(view.$('li:nth-child(2) label').text(), 'bar'); equal(view.$('li:nth-child(3) label').length, 1); equal(view.$('li:nth-child(3) label').text(), 'baz'); }); QUnit.test("a block passed to a collection helper defaults to the view", function() { var CollectionTestView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }); view = EmberView['default'].create({ collectionTestView: CollectionTestView, template: compile['default']('{{#collection view.collectionTestView}} {{/collection}}') }); utils.runAppend(view); // Preconds equal(view.$('li:nth-child(1) label').length, 1); equal(view.$('li:nth-child(1) label').text(), 'foo'); equal(view.$('li:nth-child(2) label').length, 1); equal(view.$('li:nth-child(2) label').text(), 'bar'); equal(view.$('li:nth-child(3) label').length, 1); equal(view.$('li:nth-child(3) label').text(), 'baz'); run['default'](function() { property_set.set(firstChild(view), 'content', native_array.A()); }); equal(view.$('label').length, 0, "all list item views should be removed from DOM"); }); QUnit.test("should include an id attribute if id is set in the options hash", function() { var CollectionTestView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }); view = EmberView['default'].create({ collectionTestView: CollectionTestView, template: compile['default']('{{#collection view.collectionTestView id="baz"}}foo{{/collection}}') }); utils.runAppend(view); equal(view.$('ul#baz').length, 1, "adds an id attribute"); }); QUnit.test("should give its item views the class specified by itemClass", function() { var ItemClassTestCollectionView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo', 'bar', 'baz']) }); view = EmberView['default'].create({ itemClassTestCollectionView: ItemClassTestCollectionView, template: compile['default']('{{#collection view.itemClassTestCollectionView itemClass="baz"}}foo{{/collection}}') }); utils.runAppend(view); equal(view.$('ul li.baz').length, 3, "adds class attribute"); }); QUnit.test("should give its item views the classBinding specified by itemClassBinding", function() { var ItemClassBindingTestCollectionView = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A([EmberObject['default'].create({ isBaz: false }), EmberObject['default'].create({ isBaz: true }), EmberObject['default'].create({ isBaz: true })]) }); view = EmberView['default'].create({ itemClassBindingTestCollectionView: ItemClassBindingTestCollectionView, isBar: true, template: compile['default']('{{#collection view.itemClassBindingTestCollectionView itemClassBinding="view.isBar"}}foo{{/collection}}') }); utils.runAppend(view); equal(view.$('ul li.is-bar').length, 3, "adds class on initial rendering"); // NOTE: in order to bind an item's class to a property of the item itself (e.g. `isBaz` above), it will be necessary // to introduce a new keyword that could be used from within `itemClassBinding`. For instance, `itemClassBinding="item.isBaz"`. }); QUnit.test("should give its item views the property specified by itemPropertyBinding", function() { var ItemPropertyBindingTestItemView = EmberView['default'].extend({ tagName: 'li' }); // Use preserveContext=false so the itemView handlebars context is the view context // Set itemView bindings using item* view = EmberView['default'].create({ baz: "baz", content: native_array.A([EmberObject['default'].create(), EmberObject['default'].create(), EmberObject['default'].create()]), container: { lookupFactory: function() { return ItemPropertyBindingTestItemView; } }, template: compile['default']('{{#collection contentBinding="view.content" tagName="ul" itemViewClass="item-property-binding-test-item-view" itemPropertyBinding="view.baz" preserveContext=false}}{{view.property}}{{/collection}}') }); utils.runAppend(view); equal(view.$('ul li').length, 3, "adds 3 itemView"); view.$('ul li').each(function(i, li) { equal(jQuery['default'](li).text(), "baz", "creates the li with the property = baz"); }); run['default'](function() { property_set.set(view, 'baz', "yobaz"); }); equal(view.$('ul li:first').text(), "yobaz", "change property of sub view"); }); QUnit.test("should unsubscribe stream bindings", function() { view = EmberView['default'].create({ baz: "baz", content: native_array.A([EmberObject['default'].create(), EmberObject['default'].create(), EmberObject['default'].create()]), template: compile['default']('{{#collection contentBinding="view.content" itemPropertyBinding="view.baz"}}{{view.property}}{{/collection}}') }); utils.runAppend(view); var barStreamBinding = view._streamBindings['view.baz']; equal(barStreamBinding.subscribers.length, 3*2, "adds 3 subscribers"); run['default'](function() { view.get('content').popObject(); }); equal(barStreamBinding.subscribers.length, 2*2, "removes 1 subscriber"); }); QUnit.test("should work inside a bound {{#if}}", function() { var testData = native_array.A([EmberObject['default'].create({ isBaz: false }), EmberObject['default'].create({ isBaz: true }), EmberObject['default'].create({ isBaz: true })]); var IfTestCollectionView = CollectionView['default'].extend({ tagName: 'ul', content: testData }); view = EmberView['default'].create({ ifTestCollectionView: IfTestCollectionView, template: compile['default']('{{#if view.shouldDisplay}}{{#collection view.ifTestCollectionView}}{{content.isBaz}}{{/collection}}{{/if}}'), shouldDisplay: true }); utils.runAppend(view); equal(view.$('ul li').length, 3, "renders collection when conditional is true"); run['default'](function() { property_set.set(view, 'shouldDisplay', false); }); equal(view.$('ul li').length, 0, "removes collection when conditional changes to false"); run['default'](function() { property_set.set(view, 'shouldDisplay', true); }); equal(view.$('ul li').length, 3, "collection renders when conditional changes to true"); }); QUnit.test("should pass content as context when using {{#each}} helper [DEPRECATED]", function() { view = EmberView['default'].create({ template: compile['default']('{{#each view.releases}}Mac OS X {{version}}: {{name}} {{/each}}'), releases: native_array.A([ { version: '10.7', name: 'Lion' }, { version: '10.6', name: 'Snow Leopard' }, { version: '10.5', name: 'Leopard' } ]) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "Mac OS X 10.7: Lion Mac OS X 10.6: Snow Leopard Mac OS X 10.5: Leopard ", "prints each item in sequence"); }); QUnit.test("should re-render when the content object changes", function() { var RerenderTest = CollectionView['default'].extend({ tagName: 'ul', content: native_array.A() }); view = EmberView['default'].create({ rerenderTestView: RerenderTest, template: compile['default']('{{#collection view.rerenderTestView}}{{view.content}}{{/collection}}') }); utils.runAppend(view); run['default'](function() { property_set.set(firstChild(view), 'content', native_array.A(['bing', 'bat', 'bang'])); }); run['default'](function() { property_set.set(firstChild(view), 'content', native_array.A(['ramalamadingdong'])); }); equal(view.$('li').length, 1, "rerenders with correct number of items"); equal(trim(view.$('li:eq(0)').text()), "ramalamadingdong"); }); QUnit.test("select tagName on collection helper automatically sets child tagName to option", function() { var RerenderTest = CollectionView['default'].extend({ content: native_array.A(['foo']) }); view = EmberView['default'].create({ rerenderTestView: RerenderTest, template: compile['default']('{{#collection view.rerenderTestView tagName="select"}}{{view.content}}{{/collection}}') }); utils.runAppend(view); equal(view.$('option').length, 1, "renders the correct child tag name"); }); QUnit.test("tagName works in the #collection helper", function() { var RerenderTest = CollectionView['default'].extend({ content: native_array.A(['foo', 'bar']) }); view = EmberView['default'].create({ rerenderTestView: RerenderTest, template: compile['default']('{{#collection view.rerenderTestView tagName="ol"}}{{view.content}}{{/collection}}') }); utils.runAppend(view); equal(view.$('ol').length, 1, "renders the correct tag name"); equal(view.$('li').length, 2, "rerenders with correct number of items"); run['default'](function() { property_set.set(firstChild(view), 'content', native_array.A(['bing', 'bat', 'bang'])); }); equal(view.$('li').length, 3, "rerenders with correct number of items"); equal(trim(view.$('li:eq(0)').text()), "bing"); }); QUnit.test("should render nested collections", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('view:inner-list', CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['one','two','three']) })); registry.register('view:outer-list', CollectionView['default'].extend({ tagName: 'ul', content: native_array.A(['foo']) })); view = EmberView['default'].create({ container: container, template: compile['default']('{{#collection "outer-list" class="outer"}}{{content}}{{#collection "inner-list" class="inner"}}{{content}}{{/collection}}{{/collection}}') }); utils.runAppend(view); equal(view.$('ul.outer > li').length, 1, "renders the outer list with correct number of items"); equal(view.$('ul.inner').length, 1, "the inner list exsits"); equal(view.$('ul.inner > li').length, 3, "renders the inner list with correct number of items"); }); QUnit.test("should render multiple, bound nested collections (#68)", function() { var view; run['default'](function() { TemplateTests.contentController = ArrayProxy['default'].create({ content: native_array.A(['foo','bar']) }); var InnerList = CollectionView['default'].extend({ tagName: 'ul', contentBinding: 'parentView.innerListContent' }); var OuterListItem = EmberView['default'].extend({ innerListView: InnerList, template: compile['default']('{{#collection view.innerListView class="inner"}}{{content}}{{/collection}}{{content}}'), innerListContent: computed.computed(function() { return native_array.A([1,2,3]); }) }); var OuterList = CollectionView['default'].extend({ tagName: 'ul', contentBinding: 'TemplateTests.contentController', itemViewClass: OuterListItem }); view = EmberView['default'].create({ outerListView: OuterList, template: compile['default']('{{collection view.outerListView class="outer"}}') }); }); utils.runAppend(view); equal(view.$('ul.outer > li').length, 2, "renders the outer list with correct number of items"); equal(view.$('ul.inner').length, 2, "renders the correct number of inner lists"); equal(view.$('ul.inner:first > li').length, 3, "renders the first inner list with correct number of items"); equal(view.$('ul.inner:last > li').length, 3, "renders the second list with correct number of items"); utils.runDestroy(view); }); QUnit.test("should allow view objects to be swapped out without throwing an error (#78)", function() { var view, dataset, secondDataset; run['default'](function() { TemplateTests.datasetController = EmberObject['default'].create(); var ExampleCollectionView = CollectionView['default'].extend({ contentBinding: 'parentView.items', tagName: 'ul', template: compile['default']("{{view.content}}") }); var ReportingView = EmberView['default'].extend({ exampleCollectionView: ExampleCollectionView, datasetBinding: 'TemplateTests.datasetController.dataset', readyBinding: 'dataset.ready', itemsBinding: 'dataset.items', template: compile['default']("{{#if view.ready}}{{collection view.exampleCollectionView}}{{else}}Loading{{/if}}") }); view = ReportingView.create(); }); utils.runAppend(view); equal(view.$().text(), "Loading", "renders the loading text when the dataset is not ready"); run['default'](function() { dataset = EmberObject['default'].create({ ready: true, items: native_array.A([1,2,3]) }); TemplateTests.datasetController.set('dataset', dataset); }); equal(view.$('ul > li').length, 3, "renders the collection with the correct number of items when the dataset is ready"); run['default'](function() { secondDataset = EmberObject['default'].create({ ready: false }); TemplateTests.datasetController.set('dataset', secondDataset); }); equal(view.$().text(), "Loading", "renders the loading text when the second dataset is not ready"); utils.runDestroy(view); }); QUnit.test("context should be content", function() { var view; registry = new system__container.Registry(); container = registry.container(); var items = native_array.A([ EmberObject['default'].create({ name: 'Dave' }), EmberObject['default'].create({ name: 'Mary' }), EmberObject['default'].create({ name: 'Sara' }) ]); registry.register('view:an-item', EmberView['default'].extend({ template: compile['default']("Greetings {{name}}") })); view = EmberView['default'].create({ container: container, controller: { items: items }, template: compile['default']('{{collection contentBinding="items" itemViewClass="an-item"}}') }); utils.runAppend(view); equal(view.$().text(), "Greetings DaveGreetings MaryGreetings Sara"); utils.runDestroy(view); }); }); enifed('ember-htmlbars/tests/helpers/collection_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/collection_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/collection_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/collection_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/collection_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/collection_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/component_test', ['ember-views/component_lookup', 'container/registry', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (ComponentLookup, Registry, EmberView, compile, utils) { 'use strict'; var set = Ember.set; var get = Ember.get; var view, registry, container; QUnit.module("ember-htmlbars: {{#component}} helper", { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.register('component-lookup:main', ComponentLookup['default']); }, teardown: function() { utils.runDestroy(view); utils.runDestroy(container); registry = container = view = null; } }); QUnit.test("component helper with unquoted string is bound", function() { registry.register('template:components/foo-bar', compile['default']('yippie! {{location}} {{yield}}')); registry.register('template:components/baz-qux', compile['default']('yummy {{location}} {{yield}}')); view = EmberView['default'].create({ container: container, dynamicComponent: 'foo-bar', location: 'Caracas', template: compile['default']('{{#component view.dynamicComponent location=view.location}}arepas!{{/component}}') }); utils.runAppend(view); equal(view.$().text(), 'yippie! Caracas arepas!', 'component was looked up and rendered'); Ember.run(function() { set(view, "dynamicComponent", 'baz-qux'); set(view, "location", 'Loisaida'); }); equal(view.$().text(), 'yummy Loisaida arepas!', 'component was updated and re-rendered'); }); QUnit.test("component helper with actions", function() { registry.register('template:components/foo-bar', compile['default']('yippie! {{yield}}')); registry.register('component:foo-bar', Ember.Component.extend({ classNames: 'foo-bar', didInsertElement: function() { // trigger action on click in absence of app's EventDispatcher var self = this; this.$().on('click', function() { self.sendAction('fooBarred'); }); }, willDestroyElement: function() { this.$().off('click'); } })); var actionTriggered = 0; var controller = Ember.Controller.extend({ dynamicComponent: 'foo-bar', actions: { mappedAction: function() { actionTriggered++; } } }).create(); view = EmberView['default'].create({ container: container, controller: controller, template: compile['default']('{{#component dynamicComponent fooBarred="mappedAction"}}arepas!{{/component}}') }); utils.runAppend(view); Ember.run(function() { view.$('.foo-bar').trigger('click'); }); equal(actionTriggered, 1, 'action was triggered'); }); QUnit.test('component helper maintains expected logical parentView', function() { registry.register('template:components/foo-bar', compile['default']('yippie! {{yield}}')); var componentInstance; registry.register('component:foo-bar', Ember.Component.extend({ didInsertElement: function() { componentInstance = this; } })); view = EmberView['default'].create({ container: container, dynamicComponent: 'foo-bar', template: compile['default']('{{#component view.dynamicComponent}}arepas!{{/component}}') }); utils.runAppend(view); equal(get(componentInstance, 'parentView'), view, 'component\'s parentView is the view invoking the helper'); }); QUnit.test("nested component helpers", function() { registry.register('template:components/foo-bar', compile['default']('yippie! {{location}} {{yield}}')); registry.register('template:components/baz-qux', compile['default']('yummy {{location}} {{yield}}')); registry.register('template:components/corge-grault', compile['default']('delicious {{location}} {{yield}}')); view = EmberView['default'].create({ container: container, dynamicComponent1: 'foo-bar', dynamicComponent2: 'baz-qux', location: 'Caracas', template: compile['default']('{{#component view.dynamicComponent1 location=view.location}}{{#component view.dynamicComponent2 location=view.location}}arepas!{{/component}}{{/component}}') }); utils.runAppend(view); equal(view.$().text(), 'yippie! Caracas yummy Caracas arepas!', 'components were looked up and rendered'); Ember.run(function() { set(view, "dynamicComponent1", 'corge-grault'); set(view, "location", 'Loisaida'); }); equal(view.$().text(), 'delicious Loisaida yummy Loisaida arepas!', 'components were updated and re-rendered'); }); QUnit.test("component helper can be used with a quoted string (though you probably would not do this)", function() { registry.register('template:components/foo-bar', compile['default']('yippie! {{location}} {{yield}}')); view = EmberView['default'].create({ container: container, location: 'Caracas', template: compile['default']('{{#component "foo-bar" location=view.location}}arepas!{{/component}}') }); utils.runAppend(view); equal(view.$().text(), 'yippie! Caracas arepas!', 'component was looked up and rendered'); }); QUnit.test("component with unquoted param resolving to non-existent component", function() { view = EmberView['default'].create({ container: container, dynamicComponent: 'does-not-exist', location: 'Caracas', template: compile['default']('{{#component view.dynamicComponent location=view.location}}arepas!{{/component}}') }); throws(function() { utils.runAppend(view); }, /HTMLBars error: Could not find component named "does-not-exist"./); }); QUnit.test("component with quoted param for non-existent component", function() { view = EmberView['default'].create({ container: container, location: 'Caracas', template: compile['default']('{{#component "does-not-exist" location=view.location}}arepas!{{/component}}') }); throws(function() { utils.runAppend(view); }, /HTMLBars error: Could not find component named "does-not-exist"./); }); }); enifed('ember-htmlbars/tests/helpers/component_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/component_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/component_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/component_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/component_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/component_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/debug_test', ['ember-metal/core', 'ember-metal/logger', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (Ember, EmberLogger, EmberView, compile, utils) { 'use strict'; var originalLookup = Ember['default'].lookup; var lookup; var originalLog, logCalls; var view; QUnit.module("Handlebars {{log}} helper", { setup: function() { Ember['default'].lookup = lookup = { Ember: Ember['default'] }; originalLog = EmberLogger['default'].log; logCalls = []; EmberLogger['default'].log = function() { logCalls.push.apply(logCalls, arguments); }; }, teardown: function() { utils.runDestroy(view); view = null; EmberLogger['default'].log = originalLog; Ember['default'].lookup = originalLookup; } }); QUnit.test("should be able to log multiple properties", function() { var context = { value: 'one', valueTwo: 'two' }; view = EmberView['default'].create({ context: context, template: compile['default']('{{log value valueTwo}}') }); utils.runAppend(view); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one'); equal(logCalls[1], 'two'); }); QUnit.test("should be able to log primitives", function() { var context = { value: 'one', valueTwo: 'two' }; view = EmberView['default'].create({ context: context, template: compile['default']('{{log value "foo" 0 valueTwo true}}') }); utils.runAppend(view); equal(view.$().text(), "", "shouldn't render any text"); strictEqual(logCalls[0], 'one'); strictEqual(logCalls[1], 'foo'); strictEqual(logCalls[2], 0); strictEqual(logCalls[3], 'two'); strictEqual(logCalls[4], true); }); }); enifed('ember-htmlbars/tests/helpers/debug_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/debug_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/debug_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/debug_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/debug_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/debug_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/each_test', ['ember-metal/core', 'ember-runtime/system/object', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/views/metamorph_view', 'ember-metal/computed', 'ember-runtime/controllers/array_controller', 'ember-runtime/system/native_array', 'ember-runtime/controllers/controller', 'ember-runtime/controllers/object_controller', 'ember-runtime/system/container', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-runtime/tests/utils', 'ember-template-compiler/system/compile'], function (Ember, EmberObject, run, EmberView, _MetamorphView, computed, ArrayController, native_array, controllers__controller, ObjectController, system__container, property_get, property_set, utils, compile) { 'use strict'; /*jshint newcap:false*/ var people, view, registry, container; var template, templateMyView, MyView, MyEmptyView, templateMyEmptyView; // This function lets us write {{#EACH|people|p}} {{p}} {{/each}} // and generate: // // - {{#each p in people}} (legacy) // - {{#each people as |p|}} (legacy) function makeReplacer(useBlockParams) { return function(_, matchString) { var values = matchString.split("|"); if (values.length === 1) { return "each"; } var arr = useBlockParams ? ["each", values[1], "as", "|" + values[2] + "|"] : ["each", values[2], "in", values[1]]; var options = values[3]; if (options) { if (useBlockParams) { arr.splice(2, 0, options); } else { arr.push(options); } } return arr.join(" "); }; } var parseEachReplacerBlockParam = makeReplacer(true); var parseEachReplacerNonBlockParam = makeReplacer(false); var EACH_REGEX = /(EACH[^\}]*)/g; function parseEach(str, useBlockParams) { return str.replace(EACH_REGEX, useBlockParams ? parseEachReplacerBlockParam : parseEachReplacerNonBlockParam); } QUnit.module("parseEach test helper"); QUnit.test("block param syntax substitution", function() { equal(parseEach("{{#EACH|people|p}}p people{{/EACH}}", true), "{{#each people as |p|}}p people{{/each}}"); equal(parseEach("{{#EACH|people|p|a='b' c='d'}}p people{{/EACH}}", true), "{{#each people a='b' c='d' as |p|}}p people{{/each}}"); }); QUnit.test("non-block param syntax substitution", function() { equal(parseEach("{{#EACH|people|p}}p people{{/EACH}}", false), "{{#each p in people}}p people{{/each}}"); equal(parseEach("{{#EACH|people|p|a='b' c='d'}}p people{{/EACH}}", false), "{{#each p in people a='b' c='d'}}p people{{/each}}"); }); function templateFor(templateString, useBlockParams) { return compile['default'](parseEach(templateString, useBlockParams)); } var originalLookup = Ember['default'].lookup; var lookup; QUnit.module("the #each helper [DEPRECATED]", { setup: function() { Ember['default'].lookup = lookup = { Ember: Ember['default'] }; template = templateFor("{{#each view.people}}{{name}}{{/each}}"); people = native_array.A([{ name: "Steve Holt" }, { name: "Annabelle" }]); registry = new system__container.Registry(); container = registry.container(); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); view = EmberView['default'].create({ container: container, template: template, people: people }); templateMyView = templateFor("{{name}}"); lookup.MyView = MyView = EmberView['default'].extend({ template: templateMyView }); templateMyEmptyView = templateFor("I'm empty"); lookup.MyEmptyView = MyEmptyView = EmberView['default'].extend({ template: templateMyEmptyView }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; Ember['default'].lookup = originalLookup; } }); var assertHTML = function(view, expectedHTML) { var html = view.$().html(); // IE 8 (and prior?) adds the \r\n html = html.replace(/]*><\/script>/ig, '').replace(/[\r\n]/g, ''); equal(html, expectedHTML); }; var assertText = function(view, expectedText) { equal(view.$().text(), expectedText); }; QUnit.test("it renders the template for each item in an array", function() { assertHTML(view, "Steve HoltAnnabelle"); }); QUnit.test("it updates the view if an item is added", function() { run['default'](function() { people.pushObject({ name: "Tom Dale" }); }); assertHTML(view, "Steve HoltAnnabelleTom Dale"); }); if (typeof Handlebars === "object") { QUnit.test("should be able to use standard Handlebars #each helper", function() { utils.runDestroy(view); view = EmberView['default'].create({ context: { items: ['a', 'b', 'c'] }, template: Handlebars.compile("{{#each items}}{{this}}{{/each}}") }); utils.runAppend(view); equal(view.$().html(), "abc"); }); } QUnit.test("it allows you to access the current context using {{this}}", function() { utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor("{{#each view.people}}{{this}}{{/each}}"), people: native_array.A(['Black Francis', 'Joey Santiago', 'Kim Deal', 'David Lovering']) }); utils.runAppend(view); assertHTML(view, "Black FrancisJoey SantiagoKim DealDavid Lovering"); }); QUnit.test("it updates the view if an item is removed", function() { run['default'](function() { people.removeAt(0); }); assertHTML(view, "Annabelle"); }); QUnit.test("it updates the view if an item is replaced", function() { run['default'](function() { people.removeAt(0); people.insertAt(0, { name: "Kazuki" }); }); assertHTML(view, "KazukiAnnabelle"); }); QUnit.test("can add and replace in the same runloop", function() { run['default'](function() { people.pushObject({ name: "Tom Dale" }); people.removeAt(0); people.insertAt(0, { name: "Kazuki" }); }); assertHTML(view, "KazukiAnnabelleTom Dale"); }); QUnit.test("can add and replace the object before the add in the same runloop", function() { run['default'](function() { people.pushObject({ name: "Tom Dale" }); people.removeAt(1); people.insertAt(1, { name: "Kazuki" }); }); assertHTML(view, "Steve HoltKazukiTom Dale"); }); QUnit.test("can add and replace complicatedly", function() { run['default'](function() { people.pushObject({ name: "Tom Dale" }); people.removeAt(1); people.insertAt(1, { name: "Kazuki" }); people.pushObject({ name: "Firestone" }); people.pushObject({ name: "McMunch" }); people.removeAt(3); }); assertHTML(view, "Steve HoltKazukiTom DaleMcMunch"); }); QUnit.test("can add and replace complicatedly harder", function() { run['default'](function() { people.pushObject({ name: "Tom Dale" }); people.removeAt(1); people.insertAt(1, { name: "Kazuki" }); people.pushObject({ name: "Firestone" }); people.pushObject({ name: "McMunch" }); people.removeAt(2); }); assertHTML(view, "Steve HoltKazukiFirestoneMcMunch"); }); QUnit.test("it does not mark each option tag as selected", function() { var selectView = EmberView['default'].create({ template: templateFor(''), people: people }); utils.runAppend(selectView); equal(selectView.$('option').length, 3, "renders 3
    No Rows Yet{{view.content.title}}
    {{#each view.people}}{{/each}}
    {{name}}
    '), people: people }); utils.runAppend(tableView); equal(tableView.$('td').length, 2, "renders two elements"); run['default'](function() { people.pushObject({ name: "Black Francis" }); }); equal(tableView.$('td').length, 3, "renders an additional element when an object is added"); run['default'](function() { people.insertAt(0, { name: "Kim Deal" }); }); equal(tableView.$('td').length, 4, "renders an additional when an object is inserted at the beginning of the array"); utils.runDestroy(tableView); }); QUnit.test("it supports itemController", function() { var Controller = controllers__controller["default"].extend({ controllerName: computed.computed(function() { return "controller:"+this.get('model.name'); }) }); utils.runDestroy(view); var parentController = { container: container }; registry.register('controller:array', ArrayController['default'].extend()); view = EmberView['default'].create({ container: container, template: templateFor('{{#each view.people itemController="person"}}{{controllerName}}{{/each}}'), people: people, controller: parentController }); registry.register('controller:person', Controller); utils.runAppend(view); equal(view.$().text(), "controller:Steve Holtcontroller:Annabelle"); run['default'](function() { view.rerender(); }); assertText(view, "controller:Steve Holtcontroller:Annabelle"); run['default'](function() { people.pushObject({ name: "Yehuda Katz" }); }); assertText(view, "controller:Steve Holtcontroller:Annabellecontroller:Yehuda Katz"); run['default'](function() { property_set.set(view, 'people', native_array.A([{ name: "Trek Glowacki" }, { name: "Geoffrey Grosenbach" }])); }); assertText(view, "controller:Trek Glowackicontroller:Geoffrey Grosenbach"); strictEqual(view._childViews[0]._arrayController.get('target'), parentController, "the target property of the child controllers are set correctly"); }); QUnit.test("itemController specified in template gets a parentController property", function() { // using an ObjectController for this test to verify that parentController does accidentally get set // on the proxied model. var Controller = ObjectController['default'].extend({ controllerName: computed.computed(function() { return "controller:" + property_get.get(this, 'model.name') + ' of ' + property_get.get(this, 'parentController.company'); }) }); var parentController = { container: container, company: 'Yapp' }; registry.register('controller:array', ArrayController['default'].extend()); utils.runDestroy(view); view = EmberView['default'].create({ container: container, template: templateFor('{{#each view.people itemController="person"}}{{controllerName}}{{/each}}'), people: people, controller: parentController }); registry.register('controller:person', Controller); utils.runAppend(view); equal(view.$().text(), "controller:Steve Holt of Yappcontroller:Annabelle of Yapp"); }); QUnit.test("itemController specified in ArrayController gets a parentController property", function() { var PersonController = ObjectController['default'].extend({ controllerName: computed.computed(function() { return "controller:" + property_get.get(this, 'model.name') + ' of ' + property_get.get(this, 'parentController.company'); }) }); var PeopleController = ArrayController['default'].extend({ model: people, itemController: 'person', company: 'Yapp' }); registry.register('controller:people', PeopleController); registry.register('controller:person', PersonController); utils.runDestroy(view); view = EmberView['default'].create({ container: container, template: templateFor('{{#each}}{{controllerName}}{{/each}}'), controller: container.lookup('controller:people') }); utils.runAppend(view); equal(view.$().text(), "controller:Steve Holt of Yappcontroller:Annabelle of Yapp"); }); QUnit.test("itemController's parentController property, when the ArrayController has a parentController", function() { var PersonController = ObjectController['default'].extend({ controllerName: computed.computed(function() { return "controller:" + property_get.get(this, 'model.name') + ' of ' + property_get.get(this, 'parentController.company'); }) }); var PeopleController = ArrayController['default'].extend({ model: people, itemController: 'person', parentController: computed.computed(function() { return this.container.lookup('controller:company'); }), company: 'Yapp' }); var CompanyController = controllers__controller["default"].extend(); registry.register('controller:company', CompanyController); registry.register('controller:people', PeopleController); registry.register('controller:person', PersonController); utils.runDestroy(view); view = EmberView['default'].create({ container: container, template: templateFor('{{#each}}{{controllerName}}{{/each}}'), controller: container.lookup('controller:people') }); utils.runAppend(view); equal(view.$().text(), "controller:Steve Holt of Yappcontroller:Annabelle of Yapp"); }); QUnit.test("it supports itemController when using a custom keyword", function() { var Controller = controllers__controller["default"].extend({ controllerName: computed.computed(function() { return "controller:"+this.get('model.name'); }) }); registry.register('controller:array', ArrayController['default'].extend()); utils.runDestroy(view); view = EmberView['default'].create({ container: container, template: templateFor('{{#each person in view.people itemController="person"}}{{person.controllerName}}{{/each}}'), people: people, controller: { container: container } }); registry.register('controller:person', Controller); utils.runAppend(view); equal(view.$().text(), "controller:Steve Holtcontroller:Annabelle"); run['default'](function() { view.rerender(); }); equal(view.$().text(), "controller:Steve Holtcontroller:Annabelle"); }); QUnit.test("it supports {{itemView=}}", function() { var itemView = EmberView['default'].extend({ template: templateFor('itemView:{{name}}') }); utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people itemView="anItemView"}}'), people: people, container: container }); registry.register('view:anItemView', itemView); utils.runAppend(view); assertText(view, "itemView:Steve HoltitemView:Annabelle"); }); QUnit.test("it defers all normalization of itemView names to the resolver", function() { var itemView = EmberView['default'].extend({ template: templateFor('itemView:{{name}}') }); utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people itemView="an-item-view"}}'), people: people, container: container }); registry.register('view:an-item-view', itemView); registry.resolve = function(fullname) { equal(fullname, "view:an-item-view", "leaves fullname untouched"); return system__container.Registry.prototype.resolve.call(this, fullname); }; utils.runAppend(view); }); QUnit.test("it supports {{itemViewClass=}} with global (DEPRECATED)", function() { utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people itemViewClass=MyView}}'), people: people }); var deprecation = /Resolved the view "MyView" on the global context/; deprecation = /Global lookup of MyView from a Handlebars template is deprecated/; expectDeprecation(function() { utils.runAppend(view); }, deprecation); assertText(view, "Steve HoltAnnabelle"); }); QUnit.test("it supports {{itemViewClass=}} via container", function() { utils.runDestroy(view); view = EmberView['default'].create({ container: { lookupFactory: function(name) { equal(name, 'view:my-view'); return MyView; } }, template: templateFor('{{each view.people itemViewClass="my-view"}}'), people: people }); utils.runAppend(view); assertText(view, "Steve HoltAnnabelle"); }); QUnit.test("it supports {{itemViewClass=}} with tagName (DEPRECATED)", function() { utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people itemViewClass=MyView tagName="ul"}}'), people: people }); expectDeprecation(/Supplying a tagName to Metamorph views is unreliable and is deprecated./); utils.runAppend(view); equal(view.$('ul').length, 1, 'rendered ul tag'); equal(view.$('ul li').length, 2, 'rendered 2 li tags'); equal(view.$('ul li').text(), 'Steve HoltAnnabelle'); }); QUnit.test("it supports {{itemViewClass=}} with in format", function() { MyView = EmberView['default'].extend({ template: templateFor("{{person.name}}") }); utils.runDestroy(view); view = EmberView['default'].create({ container: { lookupFactory: function(name) { return MyView; } }, template: templateFor('{{each person in view.people itemViewClass="myView"}}'), people: people }); utils.runAppend(view); assertText(view, "Steve HoltAnnabelle"); }); QUnit.test("it supports {{emptyView=}}", function() { var emptyView = EmberView['default'].extend({ template: templateFor('emptyView:sad panda') }); utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people emptyView="anEmptyView"}}'), people: native_array.A(), container: container }); registry.register('view:anEmptyView', emptyView); utils.runAppend(view); assertText(view, "emptyView:sad panda"); }); QUnit.test("it defers all normalization of emptyView names to the resolver", function() { var emptyView = EmberView['default'].extend({ template: templateFor('emptyView:sad panda') }); utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people emptyView="an-empty-view"}}'), people: native_array.A(), container: container }); registry.register('view:an-empty-view', emptyView); registry.resolve = function(fullname) { equal(fullname, "view:an-empty-view", "leaves fullname untouched"); return system__container.Registry.prototype.resolve.call(this, fullname); }; utils.runAppend(view); }); QUnit.test("it supports {{emptyViewClass=}} with global (DEPRECATED)", function() { utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people emptyViewClass=MyEmptyView}}'), people: native_array.A() }); var deprecation = /Resolved the view "MyEmptyView" on the global context/; deprecation = /Global lookup of MyEmptyView from a Handlebars template is deprecated/; expectDeprecation(function() { utils.runAppend(view); }, deprecation); assertText(view, "I'm empty"); }); QUnit.test("it supports {{emptyViewClass=}} via container", function() { utils.runDestroy(view); view = EmberView['default'].create({ container: { lookupFactory: function(name) { equal(name, 'view:my-empty-view'); return MyEmptyView; } }, template: templateFor('{{each view.people emptyViewClass="my-empty-view"}}'), people: native_array.A() }); utils.runAppend(view); assertText(view, "I'm empty"); }); QUnit.test("it supports {{emptyViewClass=}} with tagName (DEPRECATED)", function() { utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor('{{each view.people emptyViewClass=MyEmptyView tagName="b"}}'), people: native_array.A() }); expectDeprecation(/Supplying a tagName to Metamorph views is unreliable and is deprecated./); utils.runAppend(view); equal(view.$('b').length, 1, 'rendered b tag'); equal(view.$('b').text(), "I'm empty"); }); QUnit.test("it supports {{emptyViewClass=}} with in format", function() { utils.runDestroy(view); view = EmberView['default'].create({ container: { lookupFactory: function(name) { return MyEmptyView; } }, template: templateFor('{{each person in view.people emptyViewClass="myEmptyView"}}'), people: native_array.A() }); utils.runAppend(view); assertText(view, "I'm empty"); }); QUnit.test("it supports {{else}}", function() { utils.runDestroy(view); view = EmberView['default'].create({ template: templateFor("{{#each view.items}}{{this}}{{else}}Nothing{{/each}}"), items: native_array.A(['one', 'two']) }); utils.runAppend(view); assertHTML(view, "onetwo"); run['default'](function() { view.set('items', native_array.A()); }); assertHTML(view, "Nothing"); }); QUnit.test("it works with the controller keyword", function() { utils.runDestroy(view); var controller = ArrayController['default'].create({ model: native_array.A(["foo", "bar", "baz"]) }); utils.runDestroy(view); view = EmberView['default'].create({ container: container, controller: controller, template: templateFor("{{#view}}{{#each controller}}{{this}}{{/each}}{{/view}}") }); utils.runAppend(view); equal(view.$().text(), "foobarbaz"); }); QUnit.test("views inside #each preserve the new context [DEPRECATED]", function() { utils.runDestroy(view); var controller = native_array.A([{ name: "Adam" }, { name: "Steve" }]); view = EmberView['default'].create({ container: container, controller: controller, template: templateFor('{{#each controller}}{{#view}}{{name}}{{/view}}{{/each}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "AdamSteve"); }); QUnit.test("single-arg each defaults to current context [DEPRECATED]", function() { utils.runDestroy(view); view = EmberView['default'].create({ context: native_array.A([{ name: "Adam" }, { name: "Steve" }]), template: templateFor('{{#each}}{{name}}{{/each}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "AdamSteve"); }); QUnit.test("single-arg each will iterate over controller if present [DEPRECATED]", function() { utils.runDestroy(view); view = EmberView['default'].create({ controller: native_array.A([{ name: "Adam" }, { name: "Steve" }]), template: templateFor('{{#each}}{{name}}{{/each}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "AdamSteve"); }); function testEachWithItem(moduleName, useBlockParams) { QUnit.module(moduleName, { setup: function() { registry = new system__container.Registry(); container = registry.container(); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); container = view = null; } }); QUnit.test("#each accepts a name binding", function() { view = EmberView['default'].create({ template: templateFor("{{#EACH|view.items|item}}{{view.title}} {{item}}{{/each}}", useBlockParams), title: "My Cool Each Test", items: native_array.A([1, 2]) }); utils.runAppend(view); equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2"); }); QUnit.test("#each accepts a name binding and does not change the context", function() { var controller = controllers__controller["default"].create({ name: 'bob the controller' }); var obj = EmberObject['default'].create({ name: 'henry the item' }); view = EmberView['default'].create({ template: templateFor("{{#EACH|view.items|item}}{{name}}{{/each}}", useBlockParams), title: "My Cool Each Test", items: native_array.A([obj]), controller: controller }); utils.runAppend(view); equal(view.$().text(), "bob the controller"); }); QUnit.test("#each accepts a name binding and can display child properties", function() { view = EmberView['default'].create({ template: templateFor("{{#EACH|view.items|item}}{{view.title}} {{item.name}}{{/each}}", useBlockParams), title: "My Cool Each Test", items: native_array.A([{ name: 1 }, { name: 2 }]) }); utils.runAppend(view); equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2"); }); QUnit.test("#each accepts 'this' as the right hand side", function() { view = EmberView['default'].create({ template: templateFor("{{#EACH|this|item}}{{view.title}} {{item.name}}{{/each}}", useBlockParams), title: "My Cool Each Test", controller: native_array.A([{ name: 1 }, { name: 2 }]) }); utils.runAppend(view); equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2"); }); if (!useBlockParams) { QUnit.test("views inside #each preserve the new context [DEPRECATED]", function() { var controller = native_array.A([{ name: "Adam" }, { name: "Steve" }]); view = EmberView['default'].create({ container: container, controller: controller, template: templateFor('{{#each controller}}{{#view}}{{name}}{{/view}}{{/each}}', useBlockParams) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "AdamSteve"); }); } QUnit.test("controller is assignable inside an #each", function() { var controller = ArrayController['default'].create({ model: native_array.A([{ name: "Adam" }, { name: "Steve" }]) }); view = EmberView['default'].create({ container: container, controller: controller, template: templateFor('{{#EACH|this|personController}}{{#view controllerBinding="personController"}}{{name}}{{/view}}{{/each}}', useBlockParams) }); utils.runAppend(view); equal(view.$().text(), "AdamSteve"); }); QUnit.test("it doesn't assert when the morph tags have the same parent", function() { view = EmberView['default'].create({ controller: native_array.A(['Cyril', 'David']), template: templateFor('{{#EACH|this|name}}{{/each}}
    {{name}}
    ', useBlockParams) }); utils.runAppend(view); ok(true, "No assertion from valid template"); }); QUnit.test("itemController specified in template with name binding does not change context", function() { var Controller = controllers__controller["default"].extend({ controllerName: computed.computed(function() { return "controller:"+this.get('model.name'); }) }); registry = new system__container.Registry(); container = registry.container(); people = native_array.A([{ name: "Steve Holt" }, { name: "Annabelle" }]); var parentController = { container: container, people: people, controllerName: 'controller:parentController' }; registry.register('controller:array', ArrayController['default'].extend()); view = EmberView['default'].create({ container: container, template: templateFor('{{#EACH|people|person|itemController="person"}}{{controllerName}} - {{person.controllerName}} - {{/each}}', useBlockParams), controller: parentController }); registry.register('controller:person', Controller); utils.runAppend(view); equal(view.$().text(), "controller:parentController - controller:Steve Holt - controller:parentController - controller:Annabelle - "); run['default'](function() { people.pushObject({ name: "Yehuda Katz" }); }); assertText(view, "controller:parentController - controller:Steve Holt - controller:parentController - controller:Annabelle - controller:parentController - controller:Yehuda Katz - "); run['default'](function() { property_set.set(parentController, 'people', native_array.A([{ name: "Trek Glowacki" }, { name: "Geoffrey Grosenbach" }])); }); assertText(view, "controller:parentController - controller:Trek Glowacki - controller:parentController - controller:Geoffrey Grosenbach - "); strictEqual(view._childViews[0]._arrayController.get('target'), parentController, "the target property of the child controllers are set correctly"); }); QUnit.test("itemController specified in ArrayController with name binding does not change context", function() { people = native_array.A([{ name: "Steve Holt" }, { name: "Annabelle" }]); var PersonController = controllers__controller["default"].extend({ controllerName: computed.computed(function() { return "controller:" + property_get.get(this, 'model.name') + ' of ' + property_get.get(this, 'parentController.company'); }) }); var PeopleController = ArrayController['default'].extend({ model: people, itemController: 'person', company: 'Yapp', controllerName: 'controller:people' }); registry = new system__container.Registry(); container = registry.container(); registry.register('controller:people', PeopleController); registry.register('controller:person', PersonController); view = EmberView['default'].create({ container: container, template: templateFor('{{#EACH|this|person}}{{controllerName}} - {{person.controllerName}} - {{/each}}', useBlockParams), controller: container.lookup('controller:people') }); utils.runAppend(view); equal(view.$().text(), "controller:people - controller:Steve Holt of Yapp - controller:people - controller:Annabelle of Yapp - "); }); if (!useBlockParams) { QUnit.test("{{each}} without arguments [DEPRECATED]", function() { expect(2); view = EmberView['default'].create({ controller: native_array.A([{ name: "Adam" }, { name: "Steve" }]), template: templateFor('{{#each}}{{name}}{{/each}}', useBlockParams) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "AdamSteve"); }); QUnit.test("{{each this}} without keyword [DEPRECATED]", function() { expect(2); view = EmberView['default'].create({ controller: native_array.A([{ name: "Adam" }, { name: "Steve" }]), template: templateFor('{{#each this}}{{name}}{{/each}}', useBlockParams) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(view.$().text(), "AdamSteve"); }); } if (useBlockParams) { QUnit.test("the index is passed as the second parameter to #each blocks", function() { expect(3); var adam = { name: "Adam" }; view = EmberView['default'].create({ controller: native_array.A([adam, { name: "Steve" }]), template: templateFor('{{#each this as |person index|}}{{index}}. {{person.name}}{{/each}}', true) }); utils.runAppend(view); equal(view.$().text(), "0. Adam1. Steve"); run['default'](function() { view.get('controller').unshiftObject({ name: "Bob" }); }); equal(view.$().text(), "0. Bob1. Adam2. Steve"); run['default'](function() { view.get('controller').removeObject(adam); }); equal(view.$().text(), "0. Bob1. Steve"); }); } } testEachWithItem("{{#each foo in bar}}", false); testEachWithItem("{{#each bar as |foo|}}", true); }); enifed('ember-htmlbars/tests/helpers/each_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/each_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/each_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/each_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/each_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/each_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/if_unless_test', ['ember-metal/run_loop', 'ember-runtime/system/namespace', 'ember-runtime/system/container', 'ember-views/views/view', 'ember-runtime/system/object_proxy', 'ember-runtime/system/object', 'ember-views/views/metamorph_view', 'ember-template-compiler/system/compile', 'ember-metal/property_set', 'ember-runtime/system/string', 'ember-metal/utils', 'ember-metal/enumerable_utils', 'ember-runtime/tests/utils'], function (run, Namespace, system__container, EmberView, ObjectProxy, EmberObject, _MetamorphView, compile, property_set, string, utils, enumerable_utils, tests__utils) { 'use strict'; var originalLookup = Ember.lookup; var view, lookup, registry, container, TemplateTests; QUnit.module("ember-htmlbars: {{#if}} and {{#unless}} helpers", { setup: function() { Ember.lookup = lookup = {}; lookup.TemplateTests = TemplateTests = Namespace['default'].create(); registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { tests__utils.runDestroy(container); tests__utils.runDestroy(view); registry = container = view = null; Ember.lookup = lookup = originalLookup; TemplateTests = null; } }); QUnit.test("unless should keep the current context (#784) [DEPRECATED]", function() { view = EmberView['default'].create({ o: EmberObject['default'].create({ foo: '42' }), template: compile['default']('{{#with view.o}}{{#view}}{{#unless view.doesNotExist}}foo: {{foo}}{{/unless}}{{/view}}{{/with}}') }); expectDeprecation(function() { tests__utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal(view.$().text(), 'foo: 42'); }); QUnit.test("The `if` helper tests for `isTruthy` if available", function() { view = EmberView['default'].create({ truthy: EmberObject['default'].create({ isTruthy: true }), falsy: EmberObject['default'].create({ isTruthy: false }), template: compile['default']('{{#if view.truthy}}Yep{{/if}}{{#if view.falsy}}Nope{{/if}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'Yep'); }); QUnit.test("The `if` helper does not error on undefined", function() { view = EmberView['default'].create({ undefinedValue: undefined, template: compile['default']('{{#if view.undefinedValue}}Yep{{/if}}{{#unbound if view.undefinedValue}}Yep{{/unbound}}') }); tests__utils.runAppend(view); equal(view.$().text(), ''); }); QUnit.test("The `unless` helper does not error on undefined", function() { view = EmberView['default'].create({ undefinedValue: undefined, template: compile['default']('{{#unless view.undefinedValue}}YepBound{{/unless}}{{#unbound unless view.undefinedValue}}YepUnbound{{/unbound}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'YepBoundYepUnbound'); }); QUnit.test("The `if` helper does not print the contents for an object proxy without content", function() { view = EmberView['default'].create({ truthy: ObjectProxy['default'].create({ content: {} }), falsy: ObjectProxy['default'].create({ content: null }), template: compile['default']('{{#if view.truthy}}Yep{{/if}}{{#if view.falsy}}Nope{{/if}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'Yep'); }); QUnit.test("The `if` helper updates if an object proxy gains or loses context", function() { view = EmberView['default'].create({ proxy: ObjectProxy['default'].create({ content: null }), template: compile['default']('{{#if view.proxy}}Yep{{/if}}') }); tests__utils.runAppend(view); equal(view.$().text(), ''); run['default'](function() { view.set('proxy.content', {}); }); equal(view.$().text(), 'Yep'); run['default'](function() { view.set('proxy.content', null); }); equal(view.$().text(), ''); }); QUnit.test("The `if` helper updates if an array is empty or not", function() { view = EmberView['default'].create({ array: Ember.A(), template: compile['default']('{{#if view.array}}Yep{{/if}}') }); tests__utils.runAppend(view); equal(view.$().text(), ''); run['default'](function() { view.get('array').pushObject(1); }); equal(view.$().text(), 'Yep'); run['default'](function() { view.get('array').removeObject(1); }); equal(view.$().text(), ''); }); QUnit.test("The `if` helper updates when the value changes", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{#if view.conditional}}Yep{{/if}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'Yep'); run['default'](function() { view.set('conditional', false); }); equal(view.$().text(), ''); }); QUnit.test("The `unbound if` helper does not update when the value changes", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{#unbound if view.conditional}}Yep{{/unbound}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'Yep'); run['default'](function() { view.set('conditional', false); }); equal(view.$().text(), 'Yep'); }); QUnit.test("The `unless` helper updates when the value changes", function() { view = EmberView['default'].create({ conditional: false, template: compile['default']('{{#unless view.conditional}}Nope{{/unless}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'Nope'); run['default'](function() { view.set('conditional', true); }); equal(view.$().text(), ''); }); QUnit.test("The `unbound if` helper does not update when the value changes", function() { view = EmberView['default'].create({ conditional: false, template: compile['default']('{{#unbound unless view.conditional}}Nope{{/unbound}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'Nope'); run['default'](function() { view.set('conditional', true); }); equal(view.$().text(), 'Nope'); }); QUnit.test("The `unbound if` helper should work when its inverse is not present", function() { view = EmberView['default'].create({ conditional: false, template: compile['default']('{{#unbound if view.conditional}}Yep{{/unbound}}') }); tests__utils.runAppend(view); equal(view.$().text(), ''); }); QUnit.test("The `if` helper ignores a controller option", function() { var lookupCalled = false; view = EmberView['default'].create({ container: { lookup: function() { lookupCalled = true; } }, truthy: true, template: compile['default']('{{#if view.truthy controller="foo"}}Yep{{/if}}') }); tests__utils.runAppend(view); equal(lookupCalled, false, 'controller option should NOT be used'); }); QUnit.test('should not rerender if truthiness does not change', function() { var renderCount = 0; view = EmberView['default'].create({ template: compile['default']('

    {{#if view.shouldDisplay}}{{view view.InnerViewClass}}{{/if}}

    '), shouldDisplay: true, InnerViewClass: EmberView['default'].extend({ template: compile['default']('bam'), render: function() { renderCount++; return this._super.apply(this, arguments); } }) }); tests__utils.runAppend(view); equal(renderCount, 1, 'precond - should have rendered once'); equal(view.$('#first').text(), 'bam', 'renders block when condition is true'); run['default'](function() { property_set.set(view, 'shouldDisplay', 1); }); equal(renderCount, 1, 'should not have rerendered'); equal(view.$('#first').text(), 'bam', 'renders block when condition is true'); }); QUnit.test('should update the block when object passed to #unless helper changes', function() { registry.register('template:advice', compile['default']('

    {{#unless view.onDrugs}}{{view.doWellInSchool}}{{/unless}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'advice', onDrugs: true, doWellInSchool: 'Eat your vegetables' }); tests__utils.runAppend(view); equal(view.$('h1').text(), '', 'hides block if true'); var tests = [false, null, undefined, [], '', 0]; enumerable_utils.forEach(tests, function(val) { run['default'](function() { property_set.set(view, 'onDrugs', val); }); equal(view.$('h1').text(), 'Eat your vegetables', string.fmt('renders block when conditional is "%@"; %@', [String(val), utils.typeOf(val)])); run['default'](function() { property_set.set(view, 'onDrugs', true); }); equal(view.$('h1').text(), '', 'precond - hides block when conditional is true'); }); }); QUnit.test('properties within an if statement should not fail on re-render', function() { view = EmberView['default'].create({ template: compile['default']('{{#if view.value}}{{view.value}}{{/if}}'), value: null }); tests__utils.runAppend(view); equal(view.$().text(), ''); run['default'](function() { view.set('value', 'test'); }); equal(view.$().text(), 'test'); run['default'](function() { view.set('value', null); }); equal(view.$().text(), ''); }); QUnit.test('should update the block when object passed to #if helper changes', function() { registry.register('template:menu', compile['default']('

    {{#if view.inception}}{{view.INCEPTION}}{{/if}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'menu', INCEPTION: 'BOOOOOOOONG doodoodoodoodooodoodoodoo', inception: 'OOOOoooooOOOOOOooooooo' }); tests__utils.runAppend(view); equal(view.$('h1').text(), 'BOOOOOOOONG doodoodoodoodooodoodoodoo', 'renders block if a string'); var tests = [false, null, undefined, [], '', 0]; enumerable_utils.forEach(tests, function(val) { run['default'](function() { property_set.set(view, 'inception', val); }); equal(view.$('h1').text(), '', string.fmt('hides block when conditional is "%@"', [String(val)])); run['default'](function() { property_set.set(view, 'inception', true); }); equal(view.$('h1').text(), 'BOOOOOOOONG doodoodoodoodooodoodoodoo', 'precond - renders block when conditional is true'); }); }); QUnit.test('should update the block when object passed to #if helper changes and an inverse is supplied', function() { registry.register('template:menu', compile['default']('

    {{#if view.inception}}{{view.INCEPTION}}{{else}}{{view.SAD}}{{/if}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'menu', INCEPTION: 'BOOOOOOOONG doodoodoodoodooodoodoodoo', inception: false, SAD: 'BOONG?' }); tests__utils.runAppend(view); equal(view.$('h1').text(), 'BOONG?', 'renders alternate if false'); run['default'](function() { property_set.set(view, 'inception', true); }); var tests = [false, null, undefined, [], '', 0]; enumerable_utils.forEach(tests, function(val) { run['default'](function() { property_set.set(view, 'inception', val); }); equal(view.$('h1').text(), 'BOONG?', string.fmt('renders alternate if %@', [String(val)])); run['default'](function() { property_set.set(view, 'inception', true); }); equal(view.$('h1').text(), 'BOOOOOOOONG doodoodoodoodooodoodoodoo', 'precond - renders block when conditional is true'); }); }); QUnit.test('views within an if statement should be sane on re-render', function() { view = EmberView['default'].create({ template: compile['default']('{{#if view.display}}{{input}}{{/if}}'), display: false }); tests__utils.runAppend(view); equal(view.$('input').length, 0); run['default'](function() { // Setting twice will trigger the observer twice, this is intentional view.set('display', true); view.set('display', 'yes'); }); var textfield = view.$('input'); equal(textfield.length, 1); // Make sure the view is still registered in View.views ok(EmberView['default'].views[textfield.attr('id')]); }); QUnit.test('the {{this}} helper should not fail on removal', function() { view = EmberView['default'].create({ context: 'abc', template: compile['default']('{{#if view.show}}{{this}}{{/if}}'), show: true }); tests__utils.runAppend(view); equal(view.$().text(), 'abc', 'should start property - precond'); run['default'](function() { view.set('show', false); }); equal(view.$().text(), ''); }); QUnit.test('should update the block when object passed to #unless helper changes', function() { registry.register('template:advice', compile['default']('

    {{#unless view.onDrugs}}{{view.doWellInSchool}}{{/unless}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'advice', onDrugs: true, doWellInSchool: 'Eat your vegetables' }); tests__utils.runAppend(view); equal(view.$('h1').text(), '', 'hides block if true'); var tests = [false, null, undefined, [], '', 0]; enumerable_utils.forEach(tests, function(val) { run['default'](function() { property_set.set(view, 'onDrugs', val); }); equal(view.$('h1').text(), 'Eat your vegetables', string.fmt('renders block when conditional is "%@"; %@', [String(val), utils.typeOf(val)])); run['default'](function() { property_set.set(view, 'onDrugs', true); }); equal(view.$('h1').text(), '', 'precond - hides block when conditional is true'); }); }); QUnit.test('properties within an if statement should not fail on re-render', function() { view = EmberView['default'].create({ template: compile['default']('{{#if view.value}}{{view.value}}{{/if}}'), value: null }); tests__utils.runAppend(view); equal(view.$().text(), ''); run['default'](function() { view.set('value', 'test'); }); equal(view.$().text(), 'test'); run['default'](function() { view.set('value', null); }); equal(view.$().text(), ''); }); QUnit.test('should update the block when object passed to #if helper changes', function() { registry.register('template:menu', compile['default']('

    {{#if view.inception}}{{view.INCEPTION}}{{/if}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'menu', INCEPTION: 'BOOOOOOOONG doodoodoodoodooodoodoodoo', inception: 'OOOOoooooOOOOOOooooooo' }); tests__utils.runAppend(view); equal(view.$('h1').text(), 'BOOOOOOOONG doodoodoodoodooodoodoodoo', 'renders block if a string'); var tests = [false, null, undefined, [], '', 0]; enumerable_utils.forEach(tests, function(val) { run['default'](function() { property_set.set(view, 'inception', val); }); equal(view.$('h1').text(), '', string.fmt('hides block when conditional is "%@"', [String(val)])); run['default'](function() { property_set.set(view, 'inception', true); }); equal(view.$('h1').text(), 'BOOOOOOOONG doodoodoodoodooodoodoodoo', 'precond - renders block when conditional is true'); }); }); QUnit.test('should update the block when object passed to #if helper changes and an inverse is supplied', function() { registry.register('template:menu', compile['default']('

    {{#if view.inception}}{{view.INCEPTION}}{{else}}{{view.SAD}}{{/if}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'menu', INCEPTION: 'BOOOOOOOONG doodoodoodoodooodoodoodoo', inception: false, SAD: 'BOONG?' }); tests__utils.runAppend(view); equal(view.$('h1').text(), 'BOONG?', 'renders alternate if false'); run['default'](function() { property_set.set(view, 'inception', true); }); var tests = [false, null, undefined, [], '', 0]; enumerable_utils.forEach(tests, function(val) { run['default'](function() { property_set.set(view, 'inception', val); }); equal(view.$('h1').text(), 'BOONG?', string.fmt('renders alternate if %@', [String(val)])); run['default'](function() { property_set.set(view, 'inception', true); }); equal(view.$('h1').text(), 'BOOOOOOOONG doodoodoodoodooodoodoodoo', 'precond - renders block when conditional is true'); }); }); QUnit.test('the {{this}} helper should not fail on removal', function() { view = EmberView['default'].create({ context: 'abc', template: compile['default']('{{#if view.show}}{{this}}{{/if}}'), show: true }); tests__utils.runAppend(view); equal(view.$().text(), 'abc', 'should start property - precond'); run['default'](function() { view.set('show', false); }); equal(view.$().text(), ''); }); QUnit.test('edge case: child conditional should not render children if parent conditional becomes false', function() { var childCreated = false; var child = null; view = EmberView['default'].create({ cond1: true, cond2: false, viewClass: EmberView['default'].extend({ init: function() { this._super.apply(this, arguments); childCreated = true; child = this; } }), template: compile['default']('{{#if view.cond1}}{{#if view.cond2}}{{#view view.viewClass}}test{{/view}}{{/if}}{{/if}}') }); tests__utils.runAppend(view); ok(!childCreated, 'precondition'); run['default'](function() { // The order of these sets is important for the test view.set('cond2', true); view.set('cond1', false); }); // TODO: Priority Queue, for now ensure correct result. //ok(!childCreated, 'child should not be created'); ok(child.isDestroyed, 'child should be gone'); equal(view.$().text(), ''); }); QUnit.test('edge case: rerender appearance of inner virtual view', function() { view = EmberView['default'].create({ tagName: '', cond2: false, template: compile['default']('{{#if view.cond2}}test{{/if}}') }); tests__utils.runAppend(view); equal(Ember.$('#qunit-fixture').text(), ''); run['default'](function() { view.set('cond2', true); }); equal(Ember.$('#qunit-fixture').text(), 'test'); }); QUnit.test("`if` helper with inline form: renders the second argument when conditional is truthy", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'truthy'); }); QUnit.test("`if` helper with inline form: renders the third argument when conditional is falsy", function() { view = EmberView['default'].create({ conditional: false, template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'falsy'); }); QUnit.test("`if` helper with inline form: can omit the falsy argument", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{if view.conditional "truthy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'truthy'); }); QUnit.test("`if` helper with inline form: can omit the falsy argument and renders nothing when conditional is falsy", function() { view = EmberView['default'].create({ conditional: false, template: compile['default']('{{if view.conditional "truthy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), ''); }); QUnit.test("`if` helper with inline form: truthy and falsy arguments are changed if conditional changes", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'truthy'); run['default'](function() { view.set('conditional', false); }); equal(view.$().text(), 'falsy'); }); QUnit.test("`if` helper with inline form: can use truthy param as binding", function() { view = EmberView['default'].create({ truthy: 'ok', conditional: true, template: compile['default']('{{if view.conditional view.truthy}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'ok'); run['default'](function() { view.set('truthy', 'yes'); }); equal(view.$().text(), 'yes'); }); QUnit.test("`if` helper with inline form: can use falsy param as binding", function() { view = EmberView['default'].create({ truthy: 'ok', falsy: 'boom', conditional: false, template: compile['default']('{{if view.conditional view.truthy view.falsy}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'boom'); run['default'](function() { view.set('falsy', 'no'); }); equal(view.$().text(), 'no'); }); QUnit.test("`if` helper with inline form: raises when using more than three arguments", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{if one two three four}}') }); expectAssertion(function() { tests__utils.runAppend(view); }, /The inline form of the `if` and `unless` helpers expect two or three arguments/); }); QUnit.test("`if` helper with inline form: raises when using less than two arguments", function() { view = EmberView['default'].create({ conditional: true, template: compile['default']('{{if one}}') }); expectAssertion(function() { tests__utils.runAppend(view); }, /The inline form of the `if` and `unless` helpers expect two or three arguments/); }); QUnit.test("`if` helper with inline form: works when used in a sub expression", function() { view = EmberView['default'].create({ conditional: true, innerConditional: true, template: compile['default']('{{if view.conditional (if view.innerConditional "truthy" )}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'truthy'); }); QUnit.test("`if` helper with inline form: updates if condition changes in a sub expression", function() { view = EmberView['default'].create({ conditional: true, innerConditional: true, template: compile['default']('{{if view.conditional (if view.innerConditional "innerTruthy" "innerFalsy")}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'innerTruthy'); run['default'](function() { view.set('innerConditional', false); }); equal(view.$().text(), 'innerFalsy'); }); QUnit.test("`if` helper with inline form: can use truthy param as binding in a sub expression", function() { view = EmberView['default'].create({ conditional: true, innerConditional: true, innerTruthy: "innerTruthy", template: compile['default']('{{if view.conditional (if view.innerConditional view.innerTruthy)}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'innerTruthy'); run['default'](function() { view.set('innerTruthy', 'innerOk'); }); equal(view.$().text(), 'innerOk'); }); QUnit.test("`if` helper with inline form: respects isTruthy when object changes", function() { view = EmberView['default'].create({ conditional: Ember.Object.create({ isTruthy: false }), template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'falsy'); run['default'](function() { view.set('conditional', Ember.Object.create({ isTruthy: true })); }); equal(view.$().text(), 'truthy'); run['default'](function() { view.set('conditional', Ember.Object.create({ isTruthy: false })); }); equal(view.$().text(), 'falsy'); }); QUnit.test("`if` helper with inline form: respects isTruthy when property changes", function() { var candidate = Ember.Object.create({ isTruthy: false }); view = EmberView['default'].create({ conditional: candidate, template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'falsy'); run['default'](function() { candidate.set('isTruthy', true); }); equal(view.$().text(), 'truthy'); run['default'](function() { candidate.set('isTruthy', false); }); equal(view.$().text(), 'falsy'); }); QUnit.test("`if` helper with inline form: respects length test when list content changes", function() { var list = Ember.A(); view = EmberView['default'].create({ conditional: list, template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'falsy'); run['default'](function() { list.pushObject(1); }); equal(view.$().text(), 'truthy'); run['default'](function() { list.replace(0, 1); }); equal(view.$().text(), 'falsy'); }); QUnit.test("`if` helper with inline form: respects length test when list itself", function() { view = EmberView['default'].create({ conditional: [], template: compile['default']('{{if view.conditional "truthy" "falsy"}}') }); tests__utils.runAppend(view); equal(view.$().text(), 'falsy'); run['default'](function() { view.set('conditional', [1]); }); equal(view.$().text(), 'truthy'); run['default'](function() { view.set('conditional', []); }); equal(view.$().text(), 'falsy'); }); }); enifed('ember-htmlbars/tests/helpers/if_unless_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/if_unless_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/if_unless_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/if_unless_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/if_unless_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/if_unless_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/input_test', ['ember-metal/run_loop', 'ember-metal/property_set', 'ember-views/views/view', 'ember-runtime/tests/utils', 'ember-template-compiler/system/compile'], function (run, property_set, View, utils, compile) { 'use strict'; var view; var controller; QUnit.module("{{input type='text'}}", { setup: function() { controller = { val: "hello", place: "Enter some text", name: "some-name", max: 30, size: 30, tab: 5 }; view = View['default'].extend({ controller: controller, template: compile['default']('{{input type="text" disabled=disabled value=val placeholder=place name=name maxlength=max size=size tabindex=tab}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should insert a text field into DOM", function() { equal(view.$('input').length, 1, "A single text field was inserted"); }); QUnit.test("should become disabled if the disabled attribute is true", function() { ok(view.$('input').is(':not(:disabled)'), "There are no disabled text fields"); run['default'](null, property_set.set, controller, 'disabled', true); ok(view.$('input').is(':disabled'), "The text field is disabled"); run['default'](null, property_set.set, controller, 'disabled', false); ok(view.$('input').is(':not(:disabled)'), "There are no disabled text fields"); }); QUnit.test("input value is updated when setting value property of view", function() { equal(view.$('input').val(), "hello", "renders text field with value"); run['default'](null, property_set.set, controller, 'val', 'bye!'); equal(view.$('input').val(), "bye!", "updates text field after value changes"); }); QUnit.test("input placeholder is updated when setting placeholder property of view", function() { equal(view.$('input').attr('placeholder'), "Enter some text", "renders text field with placeholder"); run['default'](null, property_set.set, controller, 'place', 'Text, please enter it'); equal(view.$('input').attr('placeholder'), "Text, please enter it", "updates text field after placeholder changes"); }); QUnit.test("input name is updated when setting name property of view", function() { equal(view.$('input').attr('name'), "some-name", "renders text field with name"); run['default'](null, property_set.set, controller, 'name', 'other-name'); equal(view.$('input').attr('name'), "other-name", "updates text field after name changes"); }); QUnit.test("input maxlength is updated when setting maxlength property of view", function() { equal(view.$('input').attr('maxlength'), "30", "renders text field with maxlength"); run['default'](null, property_set.set, controller, 'max', 40); equal(view.$('input').attr('maxlength'), "40", "updates text field after maxlength changes"); }); QUnit.test("input size is updated when setting size property of view", function() { equal(view.$('input').attr('size'), "30", "renders text field with size"); run['default'](null, property_set.set, controller, 'size', 40); equal(view.$('input').attr('size'), "40", "updates text field after size changes"); }); QUnit.test("input tabindex is updated when setting tabindex property of view", function() { equal(view.$('input').attr('tabindex'), "5", "renders text field with the tabindex"); run['default'](null, property_set.set, controller, 'tab', 3); equal(view.$('input').attr('tabindex'), "3", "updates text field after tabindex changes"); }); QUnit.module("{{input type='text'}} - static values", { setup: function() { controller = {}; view = View['default'].extend({ controller: controller, template: compile['default']('{{input type="text" disabled=true value="hello" placeholder="Enter some text" name="some-name" maxlength=30 size=30 tabindex=5}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should insert a text field into DOM", function() { equal(view.$('input').length, 1, "A single text field was inserted"); }); QUnit.test("should become disabled if the disabled attribute is true", function() { ok(view.$('input').is(':disabled'), "The text field is disabled"); }); QUnit.test("input value is updated when setting value property of view", function() { equal(view.$('input').val(), "hello", "renders text field with value"); }); QUnit.test("input placeholder is updated when setting placeholder property of view", function() { equal(view.$('input').attr('placeholder'), "Enter some text", "renders text field with placeholder"); }); QUnit.test("input name is updated when setting name property of view", function() { equal(view.$('input').attr('name'), "some-name", "renders text field with name"); }); QUnit.test("input maxlength is updated when setting maxlength property of view", function() { equal(view.$('input').attr('maxlength'), "30", "renders text field with maxlength"); }); QUnit.test("input size is updated when setting size property of view", function() { equal(view.$('input').attr('size'), "30", "renders text field with size"); }); QUnit.test("input tabindex is updated when setting tabindex property of view", function() { equal(view.$('input').attr('tabindex'), "5", "renders text field with the tabindex"); }); QUnit.module("{{input type='text'}} - dynamic type", { setup: function() { controller = { someProperty: 'password' }; view = View['default'].extend({ controller: controller, template: compile['default']('{{input type=someProperty}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should insert a text field into DOM", function() { equal(view.$('input').attr('type'), 'password', "a bound property can be used to determine type."); }); QUnit.module("{{input}} - default type", { setup: function() { controller = {}; view = View['default'].extend({ controller: controller, template: compile['default']('{{input}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should have the default type", function() { equal(view.$('input').attr('type'), 'text', "Has a default text type"); }); QUnit.module("{{input type='checkbox'}}", { setup: function() { controller = { tab: 6, name: 'hello', val: false }; view = View['default'].extend({ controller: controller, template: compile['default']('{{input type="checkbox" disabled=disabled tabindex=tab name=name checked=val}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should append a checkbox", function() { equal(view.$('input[type=checkbox]').length, 1, "A single checkbox is added"); }); QUnit.test("should begin disabled if the disabled attribute is true", function() { ok(view.$('input').is(':not(:disabled)'), "The checkbox isn't disabled"); run['default'](null, property_set.set, controller, 'disabled', true); ok(view.$('input').is(':disabled'), "The checkbox is now disabled"); }); QUnit.test("should support the tabindex property", function() { equal(view.$('input').prop('tabindex'), '6', 'the initial checkbox tabindex is set in the DOM'); run['default'](null, property_set.set, controller, 'tab', 3); equal(view.$('input').prop('tabindex'), '3', 'the checkbox tabindex changes when it is changed in the view'); }); QUnit.test("checkbox name is updated", function() { equal(view.$('input').attr('name'), "hello", "renders checkbox with the name"); run['default'](null, property_set.set, controller, 'name', 'bye'); equal(view.$('input').attr('name'), "bye", "updates checkbox after name changes"); }); QUnit.test("checkbox checked property is updated", function() { equal(view.$('input').prop('checked'), false, "the checkbox isn't checked yet"); run['default'](null, property_set.set, controller, 'val', true); equal(view.$('input').prop('checked'), true, "the checkbox is checked now"); }); QUnit.module("{{input type='checkbox'}} - prevent value= usage", { setup: function() { view = View['default'].extend({ controller: controller, template: compile['default']('{{input type="checkbox" disabled=disabled tabindex=tab name=name value=val}}') }).create(); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("It asserts the presence of checked=", function() { expectAssertion(function() { utils.runAppend(view); }, /you must use `checked=/); }); QUnit.module("{{input type=boundType}}", { setup: function() { controller = { inputType: "checkbox", isChecked: true }; view = View['default'].extend({ controller: controller, template: compile['default']('{{input type=inputType checked=isChecked}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should append a checkbox", function() { equal(view.$('input[type=checkbox]').length, 1, "A single checkbox is added"); }); // Checking for the checked property is a good way to verify that the correct // view was used. QUnit.test("checkbox checked property is updated", function() { equal(view.$('input').prop('checked'), true, "the checkbox is checked"); }); QUnit.module("{{input type='checkbox'}} - static values", { setup: function() { controller = { tab: 6, name: 'hello', val: false }; view = View['default'].extend({ controller: controller, template: compile['default']('{{input type="checkbox" disabled=true tabindex=6 name="hello" checked=false}}') }).create(); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); } }); QUnit.test("should begin disabled if the disabled attribute is true", function() { ok(view.$().is(':not(:disabled)'), "The checkbox isn't disabled"); }); QUnit.test("should support the tabindex property", function() { equal(view.$('input').prop('tabindex'), '6', 'the initial checkbox tabindex is set in the DOM'); }); QUnit.test("checkbox name is updated", function() { equal(view.$('input').attr('name'), "hello", "renders checkbox with the name"); }); QUnit.test("checkbox checked property is updated", function() { equal(view.$('input').prop('checked'), false, "the checkbox isn't checked yet"); }); }); enifed('ember-htmlbars/tests/helpers/input_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/input_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/input_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/input_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/input_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/input_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/loc_test', ['ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (EmberView, compile, utils) { 'use strict'; function buildView(template, context) { return EmberView['default'].create({ template: compile['default'](template), context: (context || {}) }); } var oldString; QUnit.module('ember-htmlbars: {{#loc}} helper', { setup: function() { oldString = Ember.STRINGS; Ember.STRINGS = { '_Howdy Friend': 'Hallo Freund' }; }, teardown: function() { Ember.STRINGS = oldString; } }); QUnit.test('let the original value through by default', function() { var view = buildView('{{loc "Hiya buddy!"}}'); utils.runAppend(view); equal(view.$().text(), 'Hiya buddy!'); utils.runDestroy(view); }); QUnit.test('localize a simple string', function() { var view = buildView('{{loc "_Howdy Friend"}}'); utils.runAppend(view); equal(view.$().text(), 'Hallo Freund'); utils.runDestroy(view); }); QUnit.test('localize takes passed formats into an account', function() { var view = buildView('{{loc "%@, %@" "Hello" "Mr. Pitkin"}}'); utils.runAppend(view); equal(view.$().text(), 'Hello, Mr. Pitkin', 'the value of localizationKey is correct'); utils.runDestroy(view); }); // jscs:disable validateIndentation QUnit.test('localize throws an assertion if the second parameter is a binding', function() { var view = buildView('{{loc "Hello %@" name}}', { name: 'Bob Foster' }); expectAssertion(function() { utils.runAppend(view); }, /You cannot pass bindings to `loc` helper/); utils.runDestroy(view); }); QUnit.test('localize a binding throws an assertion', function() { var view = buildView('{{loc localizationKey}}', { localizationKey: 'villain' }); expectAssertion(function() { utils.runAppend(view); }, /You cannot pass bindings to `loc` helper/); utils.runDestroy(view); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/helpers/loc_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/loc_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/loc_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/loc_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/loc_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/loc_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/log_test', ['ember-metal/core', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (Ember, EmberView, compile, utils) { 'use strict'; var originalLookup, originalLog, logCalls, lookup, view; QUnit.module('ember-htmlbars: {{#log}} helper', { setup: function() { Ember['default'].lookup = lookup = { Ember: Ember['default'] }; originalLog = Ember['default'].Logger.log; logCalls = []; Ember['default'].Logger.log = function(arg) { logCalls.push(arg); }; }, teardown: function() { utils.runDestroy(view); view = null; Ember['default'].Logger.log = originalLog; Ember['default'].lookup = originalLookup; } }); QUnit.test('should be able to log a property', function() { var context = { value: 'one' }; view = EmberView['default'].create({ context: context, template: compile['default']('{{log value}}') }); utils.runAppend(view); equal(view.$().text(), '', 'shouldn\'t render any text'); equal(logCalls[0], 'one', 'should call log with value'); }); QUnit.test('should be able to log a view property', function() { view = EmberView['default'].create({ template: compile['default']('{{log view.value}}'), value: 'one' }); utils.runAppend(view); equal(view.$().text(), '', 'shouldn\'t render any text'); equal(logCalls[0], 'one', 'should call log with value'); }); QUnit.test('should be able to log `this`', function() { view = EmberView['default'].create({ context: 'one', template: compile['default']('{{log this}}') }); utils.runAppend(view); equal(view.$().text(), '', 'shouldn\'t render any text'); equal(logCalls[0], 'one', 'should call log with item one'); }); }); enifed('ember-htmlbars/tests/helpers/log_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/log_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/log_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/log_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/log_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/log_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/partial_test', ['ember-runtime/system/object', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/system/jquery', 'ember-runtime/system/container', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (EmberObject, run, EmberView, jQuery, system__container, compile, utils) { 'use strict'; var trim = jQuery['default'].trim; var MyApp, lookup, view, registry, container; var originalLookup = Ember.lookup; QUnit.module("Support for {{partial}} helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; MyApp = lookup.MyApp = EmberObject['default'].create({}); registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("should render other templates registered with the container", function() { registry.register('template:_subTemplateFromContainer', compile['default']('sub-template')); view = EmberView['default'].create({ container: container, template: compile['default']('This {{partial "subTemplateFromContainer"}} is pretty great.') }); utils.runAppend(view); equal(trim(view.$().text()), "This sub-template is pretty great."); }); QUnit.test("should render other slash-separated templates registered with the container", function() { registry.register('template:child/_subTemplateFromContainer', compile['default']("sub-template")); view = EmberView['default'].create({ container: container, template: compile['default']('This {{partial "child/subTemplateFromContainer"}} is pretty great.') }); utils.runAppend(view); equal(trim(view.$().text()), "This sub-template is pretty great."); }); QUnit.test("should use the current view's context", function() { registry.register('template:_person_name', compile['default']("{{firstName}} {{lastName}}")); view = EmberView['default'].create({ container: container, template: compile['default']('Who is {{partial "person_name"}}?') }); view.set('controller', EmberObject['default'].create({ firstName: 'Kris', lastName: 'Selden' })); utils.runAppend(view); equal(trim(view.$().text()), "Who is Kris Selden?"); }); QUnit.test("Quoteless parameters passed to {{template}} perform a bound property lookup of the partial name", function() { registry.register('template:_subTemplate', compile['default']("sub-template")); registry.register('template:_otherTemplate', compile['default']("other-template")); view = EmberView['default'].create({ container: container, template: compile['default']('This {{partial view.partialName}} is pretty {{partial nonexistent}}great.'), partialName: 'subTemplate' }); utils.runAppend(view); equal(trim(view.$().text()), "This sub-template is pretty great."); run['default'](function() { view.set('partialName', 'otherTemplate'); }); equal(trim(view.$().text()), "This other-template is pretty great."); run['default'](function() { view.set('partialName', null); }); equal(trim(view.$().text()), "This is pretty great."); }); }); enifed('ember-htmlbars/tests/helpers/partial_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/partial_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/partial_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/partial_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/partial_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/partial_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/sanitized_bind_attr_test', ['ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-metal/run_loop', 'ember-htmlbars/utils/string', 'ember-runtime/tests/utils', 'ember-metal/environment'], function (EmberView, compile, run, string, utils, environment) { 'use strict'; /* jshint scripturl:true */ var view; QUnit.module("ember-htmlbars: sanitized attribute", { teardown: function() { utils.runDestroy(view); } }); var badTags = [ { tag: 'a', attr: 'href', template: compile['default']('') }, { tag: 'body', attr: 'background', // IE8 crashes when setting background with // a javascript: protocol skip: (environment['default'].hasDOM && document.documentMode && document.documentMode <= 8), template: compile['default']('') }, { tag: 'link', attr: 'href', template: compile['default']('') }, { tag: 'img', attr: 'src', template: compile['default']('') } ]; for (var i=0, l=badTags.length; i{{#each person in view.people}}{{person.name}}{{/each}}'), people: native_array.A([{ name: 'Bob', url: 'javascript:bob-is-cool' }, { name: 'James', url: 'vbscript:james-is-cool' }, { name: 'Richard', url: 'javascript:richard-is-cool' }]) }); utils.runAppend(view); var links = view.$('a'); for (var i = 0, l = links.length; i < l; i++) { var link = links[i]; equal(link.protocol, 'unsafe:', 'properly escaped'); } }); QUnit.module("ember-htmlbars: {{#unbound boundHelper arg1 arg2... argN}} form: render unbound helper invocations", { setup: function() { Ember['default'].lookup = lookup = { Ember: Ember['default'] }; expectDeprecationInHTMLBars(); registerBoundHelper['default']('surround', function(prefix, value, suffix) { return prefix + '-' + value + '-' + suffix; }); registerBoundHelper['default']('capitalize', function(value) { return value.toUpperCase(); }); registerBoundHelper['default']('capitalizeName', function(value) { return property_get.get(value, 'firstName').toUpperCase(); }, 'firstName'); registerBoundHelper['default']('fauxconcat', function(value) { return [].slice.call(arguments, 0, -1).join(''); }); registerBoundHelper['default']('concatNames', function(value) { return property_get.get(value, 'firstName') + property_get.get(value, 'lastName'); }, 'firstName', 'lastName'); }, teardown: function() { delete helpers['default']['surround']; delete helpers['default']['capitalize']; delete helpers['default']['capitalizeName']; delete helpers['default']['fauxconcat']; delete helpers['default']['concatNames']; utils.runDestroy(view); Ember['default'].lookup = originalLookup; } }); QUnit.test("should be able to render an unbound helper invocation", function() { try { registerBoundHelper['default']('repeat', function(value, options) { var count = options.hash.count; var a = []; while (a.length < count) { a.push(value); } return a.join(''); }); view = EmberView['default'].create({ template: compile['default']('{{unbound repeat foo countBinding="bar"}} {{repeat foo countBinding="bar"}} {{unbound repeat foo count=2}} {{repeat foo count=4}}'), context: EmberObject['default'].create({ foo: "X", numRepeatsBinding: "bar", bar: 5 }) }); utils.runAppend(view); equal(view.$().text(), "XXXXX XXXXX XX XXXX", "first render is correct"); run['default'](function() { property_set.set(view, 'context.bar', 1); }); equal(view.$().text(), "XXXXX X XX XXXX", "only unbound bound options changed"); } finally { delete helpers['default']['repeat']; } }); QUnit.test("should be able to render an bound helper invocation mixed with static values", function() { view = EmberView['default'].create({ template: compile['default']('{{unbound surround prefix value "bar"}} {{surround prefix value "bar"}} {{unbound surround "bar" value suffix}} {{surround "bar" value suffix}}'), context: EmberObject['default'].create({ prefix: "before", value: "core", suffix: "after" }) }); utils.runAppend(view); equal(view.$().text(), "before-core-bar before-core-bar bar-core-after bar-core-after", "first render is correct"); run['default'](function() { property_set.set(view, 'context.prefix', 'beforeChanged'); property_set.set(view, 'context.value', 'coreChanged'); property_set.set(view, 'context.suffix', 'afterChanged'); }); equal(view.$().text(), "before-core-bar beforeChanged-coreChanged-bar bar-core-after bar-coreChanged-afterChanged", "only bound values change"); }); QUnit.test("should be able to render unbound forms of multi-arg helpers", function() { view = EmberView['default'].create({ template: compile['default']("{{fauxconcat foo bar bing}} {{unbound fauxconcat foo bar bing}}"), context: EmberObject['default'].create({ foo: "a", bar: "b", bing: "c" }) }); utils.runAppend(view); equal(view.$().text(), "abc abc", "first render is correct"); run['default'](function() { property_set.set(view, 'context.bar', 'X'); }); equal(view.$().text(), "aXc abc", "unbound helpers/properties stayed the same"); }); QUnit.test("should be able to render an unbound helper invocation for helpers with dependent keys", function() { view = EmberView['default'].create({ template: compile['default']("{{capitalizeName person}} {{unbound capitalizeName person}} {{concatNames person}} {{unbound concatNames person}}"), context: EmberObject['default'].create({ person: EmberObject['default'].create({ firstName: 'shooby', lastName: 'taylor' }) }) }); utils.runAppend(view); equal(view.$().text(), "SHOOBY SHOOBY shoobytaylor shoobytaylor", "first render is correct"); run['default'](function() { property_set.set(view, 'context.person.firstName', 'sally'); }); equal(view.$().text(), "SALLY SHOOBY sallytaylor shoobytaylor", "only bound values change"); }); QUnit.test("should be able to render an unbound helper invocation in #each helper", function() { view = EmberView['default'].create({ template: compile['default']( ["{{#each person in people}}", "{{capitalize person.firstName}} {{unbound capitalize person.firstName}}", "{{/each}}"].join("")), context: { people: Ember['default'].A([ { firstName: 'shooby', lastName: 'taylor' }, { firstName: 'cindy', lastName: 'taylor' } ]) } }); utils.runAppend(view); equal(view.$().text(), "SHOOBY SHOOBYCINDY CINDY", "unbound rendered correctly"); }); QUnit.test("should be able to render an unbound helper invocation with bound hash options", function() { try { Ember['default'].Handlebars.registerBoundHelper('repeat', function(value) { return [].slice.call(arguments, 0, -1).join(''); }); view = EmberView['default'].create({ template: compile['default']("{{capitalizeName person}} {{unbound capitalizeName person}} {{concatNames person}} {{unbound concatNames person}}"), context: EmberObject['default'].create({ person: EmberObject['default'].create({ firstName: 'shooby', lastName: 'taylor' }) }) }); utils.runAppend(view); equal(view.$().text(), "SHOOBY SHOOBY shoobytaylor shoobytaylor", "first render is correct"); run['default'](function() { property_set.set(view, 'context.person.firstName', 'sally'); }); equal(view.$().text(), "SALLY SHOOBY sallytaylor shoobytaylor", "only bound values change"); } finally { delete Ember['default'].Handlebars.registerBoundHelper['repeat']; } }); QUnit.test("should be able to render bound form of a helper inside unbound form of same helper", function() { view = EmberView['default'].create({ template: compile['default']( ["{{#unbound if foo}}", "{{#if bar}}true{{/if}}", "{{#unless bar}}false{{/unless}}", "{{/unbound}}", "{{#unbound unless notfoo}}", "{{#if bar}}true{{/if}}", "{{#unless bar}}false{{/unless}}", "{{/unbound}}"].join("")), context: EmberObject['default'].create({ foo: true, notfoo: false, bar: true }) }); utils.runAppend(view); equal(view.$().text(), "truetrue", "first render is correct"); run['default'](function() { property_set.set(view, 'context.bar', false); }); equal(view.$().text(), "falsefalse", "bound if and unless inside unbound if/unless are updated"); }); QUnit.module("ember-htmlbars: {{#unbound}} helper -- Container Lookup", { setup: function() { Ember['default'].lookup = lookup = { Ember: Ember['default'] }; registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('helper', { instantiate: false }); }, teardown: function() { utils.runDestroy(view); utils.runDestroy(container); Ember['default'].lookup = originalLookup; registry = container = view = null; } }); QUnit.test("should lookup helpers in the container", function() { expectDeprecationInHTMLBars(); registry.register('helper:up-case', makeBoundHelper['default'](function(value) { return value.toUpperCase(); })); view = EmberView['default'].create({ template: compile['default']("{{unbound up-case displayText}}"), container: container, context: { displayText: 'such awesome' } }); utils.runAppend(view); equal(view.$().text(), "SUCH AWESOME", "proper values were rendered"); run['default'](function() { property_set.set(view, 'context.displayText', 'no changes'); }); equal(view.$().text(), "SUCH AWESOME", "only bound values change"); }); QUnit.test("should be able to output a property without binding", function() { var context = { content: EmberObject['default'].create({ anUnboundString: "No spans here, son." }) }; view = EmberView['default'].create({ context: context, template: compile['default']('
    {{unbound content.anUnboundString}}
    ') }); utils.runAppend(view); equal(view.$('#first').html(), "No spans here, son."); }); QUnit.test("should be able to use unbound helper in #each helper", function() { view = EmberView['default'].create({ items: native_array.A(['a', 'b', 'c', 1, 2, 3]), template: compile['default']('
      {{#each item in view.items}}
    • {{unbound item}}
    • {{/each}}
    ') }); utils.runAppend(view); equal(view.$().text(), 'abc123'); equal(view.$('li').children().length, 0, 'No markers'); }); QUnit.test("should be able to use unbound helper in #each helper (with objects)", function() { view = EmberView['default'].create({ items: native_array.A([{ wham: 'bam' }, { wham: 1 }]), template: compile['default']('
      {{#each item in view.items}}
    • {{unbound item.wham}}
    • {{/each}}
    ') }); utils.runAppend(view); equal(view.$().text(), 'bam1'); equal(view.$('li').children().length, 0, 'No markers'); }); QUnit.test('should work properly with attributes', function() { expect(4); view = EmberView['default'].create({ template: compile['default']('
      {{#each person in view.people}}
    • {{person.name}}
    • {{/each}}
    '), people: native_array.A([{ name: 'Bob', cool: 'not-cool' }, { name: 'James', cool: 'is-cool' }, { name: 'Richard', cool: 'is-cool' }]) }); utils.runAppend(view); equal(view.$('li.not-cool').length, 1, 'correct number of not cool people'); equal(view.$('li.is-cool').length, 2, 'correct number of cool people'); run['default'](function() { property_set.set(view, 'people.firstObject.cool', 'is-cool'); }); equal(view.$('li.not-cool').length, 1, 'correct number of not cool people'); equal(view.$('li.is-cool').length, 2, 'correct number of cool people'); }); }); enifed('ember-htmlbars/tests/helpers/unbound_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/unbound_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/unbound_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/unbound_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/unbound_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/unbound_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/view_test', ['ember-metal/property_set', 'ember-views/views/view', 'container/registry', 'ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/text_field', 'ember-runtime/system/namespace', 'ember-runtime/system/object', 'ember-views/views/container_view', 'ember-views/views/metamorph_view', 'htmlbars-util/safe-string', 'ember-template-compiler/compat/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template', 'ember-metal/observer', 'ember-runtime/controllers/controller', 'ember-runtime/tests/utils', 'ember-metal/property_get', 'ember-metal/computed'], function (property_set, EmberView, Registry, run, jQuery, TextField, Namespace, EmberObject, ContainerView, _MetamorphView, SafeString, precompile, compile, template, observer, Controller, utils, property_get, computed) { 'use strict'; /*globals EmberDev */ var view, originalLookup, registry, container, lookup; var trim = jQuery['default'].trim; function firstGrandchild(view) { return property_get.get(property_get.get(view, 'childViews').objectAt(0), 'childViews').objectAt(0); } function nthChild(view, nth) { return property_get.get(view, 'childViews').objectAt(nth || 0); } function viewClass(options) { options.container = options.container || container; return EmberView['default'].extend(options); } var firstChild = nthChild; QUnit.module("ember-htmlbars: {{#view}} helper", { setup: function() { originalLookup = Ember.lookup; Ember.lookup = lookup = {}; registry = new Registry['default'](); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; Ember.lookup = lookup = originalLookup; } }); // https://github.com/emberjs/ember.js/issues/120 QUnit.test("should not enter an infinite loop when binding an attribute in Handlebars", function() { var LinkView = EmberView['default'].extend({ classNames: ['app-link'], tagName: 'a', attributeBindings: ['href'], href: '#none', click: function() { return false; } }); var parentView = EmberView['default'].create({ linkView: LinkView, test: EmberObject['default'].create({ href: 'test' }), template: compile['default']('{{#view view.linkView href=view.test.href}} Test {{/view}}') }); utils.runAppend(parentView); // Use match, since old IE appends the whole URL var href = parentView.$('a').attr('href'); ok(href.match(/(^|\/)test$/), 'Expected href to be \'test\' but got "'+href+'"'); utils.runDestroy(parentView); }); QUnit.test("By default view:toplevel is used", function() { var DefaultView = viewClass({ elementId: 'toplevel-view', template: compile['default']('hello world') }); function lookupFactory(fullName) { equal(fullName, 'view:toplevel'); return DefaultView; } var container = { lookupFactory: lookupFactory }; view = EmberView['default'].extend({ template: compile['default']('{{view}}'), container: container }).create(); utils.runAppend(view); equal(jQuery['default']('#toplevel-view').text(), 'hello world'); }); QUnit.test("By default, without a container, EmberView is used", function() { view = EmberView['default'].extend({ template: compile['default']('{{view tagName="span"}}') }).create(); utils.runAppend(view); ok(jQuery['default']('#qunit-fixture').html().toUpperCase().match(/{{view.borf}}

    {{/view}}"), snork: "nerd" }).create(); utils.runAppend(view); equal(jQuery['default']('#lol').text(), "nerd", "awkward mixed syntax treated like binding"); Ember.warn = oldWarn; }); QUnit.test('"Binding"-suffixed bindings are runloop-synchronized', function() { expect(5); var subview; var Subview = EmberView['default'].extend({ init: function() { subview = this; return this._super.apply(this, arguments); }, template: compile['default']('
    {{view.color}}
    ') }); var View = EmberView['default'].extend({ color: "mauve", Subview: Subview, template: compile['default']('

    {{view view.Subview colorBinding="view.color"}}

    ') }); view = View.create(); utils.runAppend(view); equal(view.$('h1 .color').text(), 'mauve', 'renders bound value'); run['default'](function() { run['default'].schedule('sync', function() { equal(property_get.get(subview, 'color'), 'mauve', 'bound property is correctly scheduled into the sync queue'); }); view.set('color', 'persian rose'); run['default'].schedule('sync', function() { equal(property_get.get(subview, 'color'), 'persian rose', 'bound property is correctly scheduled into the sync queue'); }); equal(property_get.get(subview, 'color'), 'mauve', 'bound property does not update immediately'); }); equal(property_get.get(subview, 'color'), 'persian rose', 'bound property is updated after runloop flush'); }); QUnit.test('Non-"Binding"-suffixed bindings are runloop-synchronized', function() { expect(5); var subview; var Subview = EmberView['default'].extend({ init: function() { subview = this; return this._super.apply(this, arguments); }, template: compile['default']('
    {{view.color}}
    ') }); var View = EmberView['default'].extend({ color: "mauve", Subview: Subview, template: compile['default']('

    {{view view.Subview color=view.color}}

    ') }); view = View.create(); utils.runAppend(view); equal(view.$('h1 .color').text(), 'mauve', 'renders bound value'); run['default'](function() { run['default'].schedule('sync', function() { equal(property_get.get(subview, 'color'), 'mauve', 'bound property is correctly scheduled into the sync queue'); }); view.set('color', 'persian rose'); run['default'].schedule('sync', function() { equal(property_get.get(subview, 'color'), 'persian rose', 'bound property is correctly scheduled into the sync queue'); }); equal(property_get.get(subview, 'color'), 'mauve', 'bound property does not update immediately'); }); equal(property_get.get(subview, 'color'), 'persian rose', 'bound property is updated after runloop flush'); }); QUnit.test("allows you to pass attributes that will be assigned to the class instance, like class=\"foo\"", function() { expect(4); registry = new Registry['default'](); container = registry.container(); registry.register('view:toplevel', EmberView['default'].extend()); view = EmberView['default'].extend({ template: compile['default']('{{view id="foo" tagName="h1" class="foo"}}{{#view id="bar" class="bar"}}Bar{{/view}}'), container: container }).create(); utils.runAppend(view); ok(jQuery['default']('#foo').hasClass('foo')); ok(jQuery['default']('#foo').is('h1')); ok(jQuery['default']('#bar').hasClass('bar')); equal(jQuery['default']('#bar').text(), 'Bar'); }); QUnit.test("Should apply class without condition always", function() { view = EmberView['default'].create({ controller: Ember.Object.create(), template: compile['default']('{{#view id="foo" classBinding=":foo"}} Foo{{/view}}') }); utils.runAppend(view); ok(jQuery['default']('#foo').hasClass('foo'), "Always applies classbinding without condition"); }); QUnit.test("Should apply classes when bound controller.* property specified", function() { view = EmberView['default'].create({ controller: { someProp: 'foo' }, template: compile['default']('{{#view id="foo" class=controller.someProp}} Foo{{/view}}') }); utils.runAppend(view); ok(jQuery['default']('#foo').hasClass('foo'), "Always applies classbinding without condition"); }); QUnit.test("Should apply classes when bound property specified", function() { view = EmberView['default'].create({ controller: { someProp: 'foo' }, template: compile['default']('{{#view id="foo" class=someProp}} Foo{{/view}}') }); utils.runAppend(view); ok(jQuery['default']('#foo').hasClass('foo'), "Always applies classbinding without condition"); }); QUnit.test("Should not apply classes when bound property specified is false", function() { view = EmberView['default'].create({ controller: { someProp: false }, template: compile['default']('{{#view id="foo" class=someProp}} Foo{{/view}}') }); utils.runAppend(view); ok(!jQuery['default']('#foo').hasClass('some-prop'), "does not add class when value is falsey"); }); QUnit.test("Should apply classes of the dasherized property name when bound property specified is true", function() { view = EmberView['default'].create({ controller: { someProp: true }, template: compile['default']('{{#view id="foo" class=someProp}} Foo{{/view}}') }); utils.runAppend(view); ok(jQuery['default']('#foo').hasClass('some-prop'), "adds dasherized class when value is true"); }); QUnit.test("Should update classes from a bound property", function() { var controller = { someProp: true }; view = EmberView['default'].create({ controller: controller, template: compile['default']('{{#view id="foo" class=someProp}} Foo{{/view}}') }); utils.runAppend(view); ok(jQuery['default']('#foo').hasClass('some-prop'), "adds dasherized class when value is true"); run['default'](function() { property_set.set(controller, 'someProp', false); }); ok(!jQuery['default']('#foo').hasClass('some-prop'), "does not add class when value is falsey"); run['default'](function() { property_set.set(controller, 'someProp', 'fooBar'); }); ok(jQuery['default']('#foo').hasClass('fooBar'), "changes property to string value (but does not dasherize)"); }); QUnit.test("bound properties should be available in the view", function() { var FuView = viewClass({ elementId: 'fu', template: compile['default']("{{view.foo}}") }); function lookupFactory(fullName) { return FuView; } var container = { lookupFactory: lookupFactory }; view = EmberView['default'].extend({ template: compile['default']("{{view 'fu' foo=view.someProp}}"), container: container, someProp: 'initial value' }).create(); utils.runAppend(view); equal(view.$('#fu').text(), 'initial value'); run['default'](function() { property_set.set(view, 'someProp', 'second value'); }); equal(view.$('#fu').text(), 'second value'); }); QUnit.test('should escape HTML in normal mustaches', function() { view = EmberView['default'].create({ template: compile['default']('{{view.output}}'), output: 'you need to be more bold' }); utils.runAppend(view); equal(view.$('b').length, 0, 'does not create an element'); equal(view.$().text(), 'you need to be more bold', 'inserts entities, not elements'); run['default'](function() { property_set.set(view, 'output', 'you are so super'); }); equal(view.$().text(), 'you are so super', 'updates with entities, not elements'); equal(view.$('i').length, 0, 'does not create an element when value is updated'); }); QUnit.test('should not escape HTML in triple mustaches', function() { view = EmberView['default'].create({ template: compile['default']('{{{view.output}}}'), output: 'you need to be more bold' }); utils.runAppend(view); equal(view.$('b').length, 1, 'creates an element'); run['default'](function() { property_set.set(view, 'output', 'you are so super'); }); equal(view.$('i').length, 1, 'creates an element when value is updated'); }); QUnit.test('should not escape HTML if string is a Handlebars.SafeString', function() { view = EmberView['default'].create({ template: compile['default']('{{view.output}}'), output: new SafeString['default']('you need to be more bold') }); utils.runAppend(view); equal(view.$('b').length, 1, 'creates an element'); run['default'](function() { property_set.set(view, 'output', new SafeString['default']('you are so super')); }); equal(view.$('i').length, 1, 'creates an element when value is updated'); }); QUnit.test('should teardown observers from bound properties on rerender', function() { view = EmberView['default'].create({ template: compile['default']('{{view.foo}}'), foo: 'bar' }); utils.runAppend(view); equal(observer.observersFor(view, 'foo').length, 1); run['default'](function() { view.rerender(); }); equal(observer.observersFor(view, 'foo').length, 1); }); QUnit.test('should update bound values after the view is removed and then re-appended', function() { view = EmberView['default'].create({ template: compile['default']('{{#if view.showStuff}}{{view.boundValue}}{{else}}Not true.{{/if}}'), showStuff: true, boundValue: 'foo' }); utils.runAppend(view); equal(trim(view.$().text()), 'foo'); run['default'](function() { property_set.set(view, 'showStuff', false); }); equal(trim(view.$().text()), 'Not true.'); run['default'](function() { property_set.set(view, 'showStuff', true); }); equal(trim(view.$().text()), 'foo'); run['default'](function() { view.remove(); property_set.set(view, 'showStuff', false); }); run['default'](function() { property_set.set(view, 'showStuff', true); }); utils.runAppend(view); run['default'](function() { property_set.set(view, 'boundValue', 'bar'); }); equal(trim(view.$().text()), 'bar'); }); QUnit.test('views set the template of their children to a passed block', function() { registry.register('template:parent', compile['default']('

    {{#view}}It worked!{{/view}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'parent' }); utils.runAppend(view); ok(view.$('h1:has(span)').length === 1, "renders the passed template inside the parent template"); }); QUnit.test('{{view}} should not override class bindings defined on a child view', function() { var LabelView = EmberView['default'].extend({ container: container, classNameBindings: ['something'], something: 'visible' }); registry.register('controller:label', Controller['default'], { instantiate: true }); registry.register('view:label', LabelView); registry.register('template:label', compile['default']('
    ')); registry.register('template:nester', compile['default']('{{render "label"}}')); view = EmberView['default'].create({ container: container, templateName: 'nester', controller: Controller['default'].create({ container: container }) }); utils.runAppend(view); ok(view.$('.visible').length > 0, 'class bindings are not overriden'); }); QUnit.test('child views can be inserted using the {{view}} helper', function() { registry.register('template:nester', compile['default']('

    Hello {{world}}

    {{view view.labelView}}')); registry.register('template:nested', compile['default']('
    Goodbye {{cruel}} {{world}}
    ')); var context = { world: 'world!' }; var LabelView = EmberView['default'].extend({ container: container, tagName: 'aside', templateName: 'nested' }); view = EmberView['default'].create({ labelView: LabelView, container: container, templateName: 'nester', context: context }); property_set.set(context, 'cruel', 'cruel'); utils.runAppend(view); ok(view.$('#hello-world:contains("Hello world!")').length, 'The parent view renders its contents'); ok(view.$('#child-view:contains("Goodbye cruel world!")').length === 1, 'The child view renders its content once'); ok(view.$().text().match(/Hello world!.*Goodbye cruel world\!/), 'parent view should appear before the child view'); }); QUnit.test('should be able to explicitly set a view\'s context', function() { var context = EmberObject['default'].create({ test: 'test' }); var CustomContextView = EmberView['default'].extend({ context: context, template: compile['default']('{{test}}') }); view = EmberView['default'].create({ customContextView: CustomContextView, template: compile['default']('{{view view.customContextView}}') }); utils.runAppend(view); equal(view.$().text(), 'test'); }); QUnit.test('Template views add an elementId to child views created using the view helper', function() { registry.register('template:parent', compile['default']('
    {{view view.childView}}
    ')); registry.register('template:child', compile['default']('I can\'t believe it\'s not butter.')); var ChildView = EmberView['default'].extend({ container: container, templateName: 'child' }); view = EmberView['default'].create({ container: container, childView: ChildView, templateName: 'parent' }); utils.runAppend(view); var childView = property_get.get(view, 'childViews.firstObject'); equal(view.$().children().first().children().first().attr('id'), property_get.get(childView, 'elementId')); }); QUnit.test('Child views created using the view helper should have their parent view set properly', function() { var template = '{{#view}}{{#view}}{{view}}{{/view}}{{/view}}'; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); var childView = firstGrandchild(view); equal(childView, property_get.get(firstChild(childView), 'parentView'), 'parent view is correct'); }); QUnit.test('Child views created using the view helper should have their IDs registered for events', function() { var template = '{{view}}{{view id="templateViewTest"}}'; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); var childView = firstChild(view); var id = childView.$()[0].id; equal(EmberView['default'].views[id], childView, 'childView without passed ID is registered with View.views so that it can properly receive events from EventDispatcher'); childView = nthChild(view, 1); id = childView.$()[0].id; equal(id, 'templateViewTest', 'precond -- id of childView should be set correctly'); equal(EmberView['default'].views[id], childView, 'childView with passed ID is registered with View.views so that it can properly receive events from EventDispatcher'); }); QUnit.test('Child views created using the view helper and that have a viewName should be registered as properties on their parentView', function() { var template = '{{#view}}{{view viewName="ohai"}}{{/view}}'; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); var parentView = firstChild(view); var childView = firstGrandchild(view); equal(property_get.get(parentView, 'ohai'), childView); }); QUnit.test('{{view}} id attribute should set id on layer', function() { registry.register('template:foo', compile['default']('{{#view view.idView id="bar"}}baz{{/view}}')); var IdView = EmberView['default']; view = EmberView['default'].create({ idView: IdView, container: container, templateName: 'foo' }); utils.runAppend(view); equal(view.$('#bar').length, 1, 'adds id attribute to layer'); equal(view.$('#bar').text(), 'baz', 'emits content'); }); QUnit.test('{{view}} tag attribute should set tagName of the view', function() { registry.register('template:foo', compile['default']('{{#view view.tagView tag="span"}}baz{{/view}}')); var TagView = EmberView['default']; view = EmberView['default'].create({ tagView: TagView, container: container, templateName: 'foo' }); utils.runAppend(view); equal(view.$('span').length, 1, 'renders with tag name'); equal(view.$('span').text(), 'baz', 'emits content'); }); QUnit.test('{{view}} class attribute should set class on layer', function() { registry.register('template:foo', compile['default']('{{#view view.idView class="bar"}}baz{{/view}}')); var IdView = EmberView['default']; view = EmberView['default'].create({ idView: IdView, container: container, templateName: 'foo' }); utils.runAppend(view); equal(view.$('.bar').length, 1, 'adds class attribute to layer'); equal(view.$('.bar').text(), 'baz', 'emits content'); }); QUnit.test('{{view}} should not allow attributeBindings to be set', function() { expectAssertion(function() { view = EmberView['default'].create({ template: compile['default']('{{view attributeBindings="one two"}}') }); utils.runAppend(view); }, /Setting 'attributeBindings' via template helpers is not allowed/); }); QUnit.test('{{view}} should be able to point to a local view', function() { view = EmberView['default'].create({ template: compile['default']('{{view view.common}}'), common: EmberView['default'].extend({ template: compile['default']('common') }) }); utils.runAppend(view); equal(view.$().text(), 'common', 'tries to look up view name locally'); }); QUnit.test('{{view}} should evaluate class bindings set to global paths DEPRECATED', function() { var App; run['default'](function() { lookup.App = App = Namespace['default'].create({ isApp: true, isGreat: true, directClass: 'app-direct', isEnabled: true }); }); view = EmberView['default'].create({ textField: TextField['default'], template: compile['default']('{{view view.textField class="unbound" classBinding="App.isGreat:great App.directClass App.isApp App.isEnabled:enabled:disabled"}}') }); expectDeprecation(function() { utils.runAppend(view); }); ok(view.$('input').hasClass('unbound'), 'sets unbound classes directly'); ok(view.$('input').hasClass('great'), 'evaluates classes bound to global paths'); ok(view.$('input').hasClass('app-direct'), 'evaluates classes bound directly to global paths'); ok(view.$('input').hasClass('is-app'), 'evaluates classes bound directly to booleans in global paths - dasherizes and sets class when true'); ok(view.$('input').hasClass('enabled'), 'evaluates ternary operator in classBindings'); ok(!view.$('input').hasClass('disabled'), 'evaluates ternary operator in classBindings'); run['default'](function() { App.set('isApp', false); App.set('isEnabled', false); }); ok(!view.$('input').hasClass('is-app'), 'evaluates classes bound directly to booleans in global paths - removes class when false'); ok(!view.$('input').hasClass('enabled'), 'evaluates ternary operator in classBindings'); ok(view.$('input').hasClass('disabled'), 'evaluates ternary operator in classBindings'); utils.runDestroy(lookup.App); }); QUnit.test('{{view}} should evaluate class bindings set in the current context', function() { view = EmberView['default'].create({ isView: true, isEditable: true, directClass: 'view-direct', isEnabled: true, textField: TextField['default'], template: compile['default']('{{view view.textField class="unbound" classBinding="view.isEditable:editable view.directClass view.isView view.isEnabled:enabled:disabled"}}') }); utils.runAppend(view); ok(view.$('input').hasClass('unbound'), 'sets unbound classes directly'); ok(view.$('input').hasClass('editable'), 'evaluates classes bound in the current context'); ok(view.$('input').hasClass('view-direct'), 'evaluates classes bound directly in the current context'); ok(view.$('input').hasClass('is-view'), 'evaluates classes bound directly to booleans in the current context - dasherizes and sets class when true'); ok(view.$('input').hasClass('enabled'), 'evaluates ternary operator in classBindings'); ok(!view.$('input').hasClass('disabled'), 'evaluates ternary operator in classBindings'); run['default'](function() { view.set('isView', false); view.set('isEnabled', false); }); ok(!view.$('input').hasClass('is-view'), 'evaluates classes bound directly to booleans in the current context - removes class when false'); ok(!view.$('input').hasClass('enabled'), 'evaluates ternary operator in classBindings'); ok(view.$('input').hasClass('disabled'), 'evaluates ternary operator in classBindings'); }); QUnit.test('{{view}} should evaluate class bindings set with either classBinding or classNameBindings from globals DEPRECATED', function() { var App; run['default'](function() { lookup.App = App = Namespace['default'].create({ isGreat: true, isEnabled: true }); }); view = EmberView['default'].create({ textField: TextField['default'], template: compile['default']('{{view view.textField class="unbound" classBinding="App.isGreat:great App.isEnabled:enabled:disabled" classNameBindings="App.isGreat:really-great App.isEnabled:really-enabled:really-disabled"}}') }); expectDeprecation(function() { utils.runAppend(view); }); ok(view.$('input').hasClass('unbound'), 'sets unbound classes directly'); ok(view.$('input').hasClass('great'), 'evaluates classBinding'); ok(view.$('input').hasClass('really-great'), 'evaluates classNameBinding'); ok(view.$('input').hasClass('enabled'), 'evaluates ternary operator in classBindings'); ok(view.$('input').hasClass('really-enabled'), 'evaluates ternary operator in classBindings'); ok(!view.$('input').hasClass('disabled'), 'evaluates ternary operator in classBindings'); ok(!view.$('input').hasClass('really-disabled'), 'evaluates ternary operator in classBindings'); run['default'](function() { App.set('isEnabled', false); }); ok(!view.$('input').hasClass('enabled'), 'evaluates ternary operator in classBindings'); ok(!view.$('input').hasClass('really-enabled'), 'evaluates ternary operator in classBindings'); ok(view.$('input').hasClass('disabled'), 'evaluates ternary operator in classBindings'); ok(view.$('input').hasClass('really-disabled'), 'evaluates ternary operator in classBindings'); utils.runDestroy(lookup.App); }); QUnit.test('{{view}} should evaluate other attribute bindings set to global paths', function() { run['default'](function() { lookup.App = Namespace['default'].create({ name: 'myApp' }); }); view = EmberView['default'].create({ textField: TextField['default'], template: compile['default']('{{view view.textField valueBinding="App.name"}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Global lookup of App.name from a Handlebars template is deprecated.'); equal(view.$('input').val(), 'myApp', 'evaluates attributes bound to global paths'); utils.runDestroy(lookup.App); }); QUnit.test('{{view}} should evaluate other attributes bindings set in the current context', function() { view = EmberView['default'].create({ name: 'myView', textField: TextField['default'], template: compile['default']('{{view view.textField valueBinding="view.name"}}') }); utils.runAppend(view); equal(view.$('input').val(), 'myView', 'evaluates attributes bound in the current context'); }); QUnit.test('{{view}} should be able to bind class names to truthy properties', function() { registry.register('template:template', compile['default']('{{#view view.classBindingView classBinding="view.number:is-truthy"}}foo{{/view}}')); var ClassBindingView = EmberView['default'].extend(); view = EmberView['default'].create({ classBindingView: ClassBindingView, container: container, number: 5, templateName: 'template' }); utils.runAppend(view); equal(view.$('.is-truthy').length, 1, 'sets class name'); run['default'](function() { property_set.set(view, 'number', 0); }); equal(view.$('.is-truthy').length, 0, 'removes class name if bound property is set to falsey'); }); QUnit.test('{{view}} should be able to bind class names to truthy or falsy properties', function() { registry.register('template:template', compile['default']('{{#view view.classBindingView classBinding="view.number:is-truthy:is-falsy"}}foo{{/view}}')); var ClassBindingView = EmberView['default'].extend(); view = EmberView['default'].create({ classBindingView: ClassBindingView, container: container, number: 5, templateName: 'template' }); utils.runAppend(view); equal(view.$('.is-truthy').length, 1, 'sets class name to truthy value'); equal(view.$('.is-falsy').length, 0, 'doesn\'t set class name to falsy value'); run['default'](function() { property_set.set(view, 'number', 0); }); equal(view.$('.is-truthy').length, 0, "doesn't set class name to truthy value"); equal(view.$('.is-falsy').length, 1, "sets class name to falsy value"); }); QUnit.test('a view helper\'s bindings are to the parent context', function() { var Subview = EmberView['default'].extend({ classNameBindings: ['color'], controller: EmberObject['default'].create({ color: 'green', name: 'bar' }), template: compile['default']('{{view.someController.name}} {{name}}') }); var View = EmberView['default'].extend({ controller: EmberObject['default'].create({ color: 'mauve', name: 'foo' }), Subview: Subview, template: compile['default']('

    {{view view.Subview colorBinding="color" someControllerBinding="this"}}

    ') }); view = View.create(); utils.runAppend(view); equal(view.$('h1 .mauve').length, 1, 'renders property on helper declaration from parent context'); equal(view.$('h1 .mauve').text(), 'foo bar', 'renders property bound in template from subview context'); }); QUnit.test('should expose a controller keyword when present on the view', function() { var templateString = '{{controller.foo}}{{#view}}{{controller.baz}}{{/view}}'; view = EmberView['default'].create({ container: container, controller: EmberObject['default'].create({ foo: 'bar', baz: 'bang' }), template: compile['default'](templateString) }); utils.runAppend(view); equal(view.$().text(), 'barbang', 'renders values from controller and parent controller'); var controller = property_get.get(view, 'controller'); run['default'](function() { controller.set('foo', 'BAR'); controller.set('baz', 'BLARGH'); }); equal(view.$().text(), 'BARBLARGH', 'updates the DOM when a bound value is updated'); utils.runDestroy(view); view = EmberView['default'].create({ controller: 'aString', template: compile['default']('{{controller}}') }); utils.runAppend(view); equal(view.$().text(), 'aString', 'renders the controller itself if no additional path is specified'); }); QUnit.test('should expose a controller keyword that can be used in conditionals', function() { var templateString = '{{#view}}{{#if controller}}{{controller.foo}}{{/if}}{{/view}}'; view = EmberView['default'].create({ container: container, controller: EmberObject['default'].create({ foo: 'bar' }), template: compile['default'](templateString) }); utils.runAppend(view); equal(view.$().text(), 'bar', 'renders values from controller and parent controller'); run['default'](function() { view.set('controller', null); }); equal(view.$().text(), '', 'updates the DOM when the controller is changed'); }); QUnit.test('should expose a controller keyword that persists through Ember.ContainerView', function() { var templateString = '{{view view.containerView}}'; view = EmberView['default'].create({ containerView: ContainerView['default'], container: container, controller: EmberObject['default'].create({ foo: 'bar' }), template: compile['default'](templateString) }); utils.runAppend(view); var containerView = property_get.get(view, 'childViews.firstObject'); var viewInstanceToBeInserted = EmberView['default'].create({ template: compile['default']('{{controller.foo}}') }); run['default'](function() { containerView.pushObject(viewInstanceToBeInserted); }); equal(trim(viewInstanceToBeInserted.$().text()), 'bar', 'renders value from parent\'s controller'); }); QUnit.test('should work with precompiled templates', function() { var templateString = precompile['default']('{{view.value}}'); var compiledTemplate = template['default'](eval(templateString)); view = EmberView['default'].create({ value: 'rendered', template: compiledTemplate }); utils.runAppend(view); equal(view.$().text(), 'rendered', 'the precompiled template was rendered'); run['default'](function() { view.set('value', 'updated'); }); equal(view.$().text(), 'updated', 'the precompiled template was updated'); }); QUnit.test('bindings should be relative to the current context', function() { view = EmberView['default'].create({ museumOpen: true, museumDetails: EmberObject['default'].create({ name: 'SFMoMA', price: 20 }), museumView: EmberView['default'].extend({ template: compile['default']('Name: {{view.name}} Price: ${{view.dollars}}') }), template: compile['default']('{{#if view.museumOpen}} {{view view.museumView nameBinding="view.museumDetails.name" dollarsBinding="view.museumDetails.price"}} {{/if}}') }); utils.runAppend(view); equal(trim(view.$().text()), 'Name: SFMoMA Price: $20', 'should print baz twice'); }); QUnit.test('bindings should respect keywords', function() { view = EmberView['default'].create({ museumOpen: true, controller: { museumOpen: true, museumDetails: EmberObject['default'].create({ name: 'SFMoMA', price: 20 }) }, museumView: EmberView['default'].extend({ template: compile['default']('Name: {{view.name}} Price: ${{view.dollars}}') }), template: compile['default']('{{#if view.museumOpen}}{{view view.museumView nameBinding="controller.museumDetails.name" dollarsBinding="controller.museumDetails.price"}}{{/if}}') }); utils.runAppend(view); equal(trim(view.$().text()), 'Name: SFMoMA Price: $20', 'should print baz twice'); }); QUnit.test('should bind to the property if no registered helper found for a mustache without parameters', function() { view = EmberView['default'].createWithMixins({ template: compile['default']('{{view.foobarProperty}}'), foobarProperty: computed.computed(function() { return 'foobarProperty'; }) }); utils.runAppend(view); ok(view.$().text() === 'foobarProperty', 'Property was bound to correctly'); }); QUnit.test('{{view}} should be able to point to a local instance of view', function() { view = EmberView['default'].create({ template: compile['default']("{{view view.common}}"), common: EmberView['default'].create({ template: compile['default']("common") }) }); utils.runAppend(view); equal(view.$().text(), "common", "tries to look up view name locally"); }); QUnit.test("{{view}} should be able to point to a local instance of subclass of view", function() { var MyView = EmberView['default'].extend(); view = EmberView['default'].create({ template: compile['default']("{{view view.subclassed}}"), subclassed: MyView.create({ template: compile['default']("subclassed") }) }); utils.runAppend(view); equal(view.$().text(), "subclassed", "tries to look up view name locally"); }); QUnit.test("{{view}} asserts that a view class is present", function() { var MyView = EmberObject['default'].extend(); view = EmberView['default'].create({ template: compile['default']("{{view view.notView}}"), notView: MyView.extend({ template: compile['default']("notView") }) }); expectAssertion(function() { utils.runAppend(view); }, /must be a subclass or an instance of Ember.View/); }); QUnit.test("{{view}} asserts that a view class is present off controller", function() { var MyView = EmberObject['default'].extend(); view = EmberView['default'].create({ template: compile['default']("{{view notView}}"), controller: EmberObject['default'].create({ notView: MyView.extend({ template: compile['default']("notView") }) }) }); expectAssertion(function() { utils.runAppend(view); }, /must be a subclass or an instance of Ember.View/); }); QUnit.test("{{view}} asserts that a view instance is present", function() { var MyView = EmberObject['default'].extend(); view = EmberView['default'].create({ template: compile['default']("{{view view.notView}}"), notView: MyView.create({ template: compile['default']("notView") }) }); expectAssertion(function() { utils.runAppend(view); }, /must be a subclass or an instance of Ember.View/); }); QUnit.test("{{view}} asserts that a view subclass instance is present off controller", function() { var MyView = EmberObject['default'].extend(); view = EmberView['default'].create({ template: compile['default']("{{view notView}}"), controller: EmberObject['default'].create({ notView: MyView.create({ template: compile['default']("notView") }) }) }); expectAssertion(function() { utils.runAppend(view); }, /must be a subclass or an instance of Ember.View/); }); }); enifed('ember-htmlbars/tests/helpers/view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/view_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/view_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/view_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/view_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/view_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/with_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-metal/computed', 'ember-metal/property_set', 'ember-metal/property_get', 'ember-runtime/controllers/object_controller', 'ember-runtime/controllers/controller', 'ember-runtime/system/container', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (EmberView, run, EmberObject, computed, property_set, property_get, object_controller, EmberController, system__container, compile, utils) { 'use strict'; /*jshint newcap:false*/ var view, lookup; var originalLookup = Ember.lookup; function testWithAs(moduleName, templateString) { QUnit.module(moduleName, { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView['default'].create({ template: compile['default'](templateString), context: { title: "Señor Engineer", person: { name: "Tom Dale" } } }); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("it should support #with-as syntax", function() { equal(view.$().text(), "Señor Engineer: Tom Dale", "should be properly scoped"); }); QUnit.test("updating the context should update the alias", function() { run['default'](function() { view.set('context.person', { name: "Yehuda Katz" }); }); equal(view.$().text(), "Señor Engineer: Yehuda Katz", "should be properly scoped after updating"); }); QUnit.test("updating a property on the context should update the HTML", function() { equal(view.$().text(), "Señor Engineer: Tom Dale", "precond - should be properly scoped after updating"); run['default'](function() { property_set.set(view, 'context.person.name', "Yehuda Katz"); }); equal(view.$().text(), "Señor Engineer: Yehuda Katz", "should be properly scoped after updating"); }); QUnit.test("updating a property on the view should update the HTML", function() { run['default'](function() { view.set('context.title', "Señorette Engineer"); }); equal(view.$().text(), "Señorette Engineer: Tom Dale", "should be properly scoped after updating"); }); } testWithAs("ember-htmlbars: {{#with}} helper", "{{#with person as tom}}{{title}}: {{tom.name}}{{/with}}"); QUnit.module("Multiple Handlebars {{with foo as bar}} helpers", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView['default'].create({ template: compile['default']("Admin: {{#with admin as person}}{{person.name}}{{/with}} User: {{#with user as person}}{{person.name}}{{/with}}"), context: { admin: { name: "Tom Dale" }, user: { name: "Yehuda Katz" } } }); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("re-using the same variable with different #with blocks does not override each other", function() { equal(view.$().text(), "Admin: Tom Dale User: Yehuda Katz", "should be properly scoped"); }); QUnit.test("the scoped variable is not available outside the {{with}} block.", function() { run['default'](function() { view.set('template', compile['default']("{{name}}-{{#with other as name}}{{name}}{{/with}}-{{name}}")); view.set('context', { name: 'Stef', other: 'Yehuda' }); }); equal(view.$().text(), "Stef-Yehuda-Stef", "should be properly scoped after updating"); }); QUnit.test("nested {{with}} blocks shadow the outer scoped variable properly.", function() { run['default'](function() { view.set('template', compile['default']("{{#with first as ring}}{{ring}}-{{#with fifth as ring}}{{ring}}-{{#with ninth as ring}}{{ring}}-{{/with}}{{ring}}-{{/with}}{{ring}}{{/with}}")); view.set('context', { first: 'Limbo', fifth: 'Wrath', ninth: 'Treachery' }); }); equal(view.$().text(), "Limbo-Wrath-Treachery-Wrath-Limbo", "should be properly scoped after updating"); }); QUnit.module("Handlebars {{#with}} globals helper [DEPRECATED]", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; lookup.Foo = { bar: 'baz' }; view = EmberView['default'].create({ template: compile['default']("{{#with Foo.bar as qux}}{{qux}}{{/with}}") }); }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("it should support #with Foo.bar as qux [DEPRECATED]", function() { expectDeprecation(function() { utils.runAppend(view); }, /Global lookup of Foo.bar from a Handlebars template is deprecated/); equal(view.$().text(), "baz", "should be properly scoped"); run['default'](function() { property_set.set(lookup.Foo, 'bar', 'updated'); }); equal(view.$().text(), "updated", "should update"); }); QUnit.module("Handlebars {{#with keyword as foo}}"); QUnit.test("it should support #with view as foo", function() { var view = EmberView['default'].create({ template: compile['default']("{{#with view as myView}}{{myView.name}}{{/with}}"), name: "Sonics" }); utils.runAppend(view); equal(view.$().text(), "Sonics", "should be properly scoped"); run['default'](function() { property_set.set(view, 'name', "Thunder"); }); equal(view.$().text(), "Thunder", "should update"); utils.runDestroy(view); }); QUnit.test("it should support #with name as food, then #with foo as bar", function() { var view = EmberView['default'].create({ template: compile['default']("{{#with name as foo}}{{#with foo as bar}}{{bar}}{{/with}}{{/with}}"), context: { name: "caterpillar" } }); utils.runAppend(view); equal(view.$().text(), "caterpillar", "should be properly scoped"); run['default'](function() { property_set.set(view, 'context.name', "butterfly"); }); equal(view.$().text(), "butterfly", "should update"); utils.runDestroy(view); }); QUnit.module("Handlebars {{#with this as foo}}"); QUnit.test("it should support #with this as qux", function() { var view = EmberView['default'].create({ template: compile['default']("{{#with this as person}}{{person.name}}{{/with}}"), controller: EmberObject['default'].create({ name: "Los Pivots" }) }); utils.runAppend(view); equal(view.$().text(), "Los Pivots", "should be properly scoped"); run['default'](function() { property_set.set(view, 'controller.name', "l'Pivots"); }); equal(view.$().text(), "l'Pivots", "should update"); utils.runDestroy(view); }); QUnit.module("Handlebars {{#with foo}} with defined controller"); QUnit.test("it should wrap context with object controller [DEPRECATED]", function() { expectDeprecation(object_controller.objectControllerDeprecation); var Controller = object_controller["default"].extend({ controllerName: computed.computed(function() { return "controller:"+this.get('model.name') + ' and ' + this.get('parentController.name'); }) }); var person = EmberObject['default'].create({ name: 'Steve Holt' }); var registry = new system__container.Registry(); var container = registry.container(); var parentController = EmberObject['default'].create({ container: container, name: 'Bob Loblaw' }); view = EmberView['default'].create({ container: container, template: compile['default']('{{#with view.person controller="person"}}{{controllerName}}{{/with}}'), person: person, controller: parentController }); registry.register('controller:person', Controller); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal(view.$().text(), "controller:Steve Holt and Bob Loblaw"); run['default'](function() { view.rerender(); }); equal(view.$().text(), "controller:Steve Holt and Bob Loblaw"); run['default'](function() { parentController.set('name', 'Carl Weathers'); view.rerender(); }); equal(view.$().text(), "controller:Steve Holt and Carl Weathers"); run['default'](function() { person.set('name', 'Gob'); view.rerender(); }); equal(view.$().text(), "controller:Gob and Carl Weathers"); strictEqual(view._childViews[0].get('controller.target'), parentController, "the target property of the child controllers are set correctly"); utils.runDestroy(view); }); /* requires each QUnit.test("it should still have access to original parentController within an {{#each}}", function() { var Controller = ObjectController.extend({ controllerName: computed(function() { return "controller:"+this.get('model.name') + ' and ' + this.get('parentController.name'); }) }); var people = A([{ name: "Steve Holt" }, { name: "Carl Weathers" }]); var registry = new Registry(); var container = registry.container(); var parentController = EmberObject.create({ container: container, name: 'Bob Loblaw', people: people }); view = EmberView.create({ container: container, template: compile('{{#each person in people}}{{#with person controller="person"}}{{controllerName}}{{/with}}{{/each}}'), controller: parentController }); registry.register('controller:person', Controller); runAppend(view); equal(view.$().text(), "controller:Steve Holt and Bob Loblawcontroller:Carl Weathers and Bob Loblaw"); runDestroy(view); }); */ QUnit.test("it should wrap keyword with object controller [DEPRECATED]", function() { expectDeprecation(object_controller.objectControllerDeprecation); var PersonController = object_controller["default"].extend({ name: computed.computed('model.name', function() { return property_get.get(this, 'model.name').toUpperCase(); }) }); var person = EmberObject['default'].create({ name: 'Steve Holt' }); var registry = new system__container.Registry(); var container = registry.container(); var parentController = EmberObject['default'].create({ container: container, person: person, name: 'Bob Loblaw' }); view = EmberView['default'].create({ container: container, template: compile['default']('{{#with person as steve controller="person"}}{{name}} - {{steve.name}}{{/with}}'), controller: parentController }); registry.register('controller:person', PersonController); utils.runAppend(view); equal(view.$().text(), "Bob Loblaw - STEVE HOLT"); run['default'](function() { view.rerender(); }); equal(view.$().text(), "Bob Loblaw - STEVE HOLT"); run['default'](function() { parentController.set('name', 'Carl Weathers'); view.rerender(); }); equal(view.$().text(), "Carl Weathers - STEVE HOLT"); run['default'](function() { person.set('name', 'Gob'); view.rerender(); }); equal(view.$().text(), "Carl Weathers - GOB"); utils.runDestroy(view); }); QUnit.test("destroys the controller generated with {{with foo controller='blah'}} [DEPRECATED]", function() { var destroyed = false; var Controller = EmberController['default'].extend({ willDestroy: function() { this._super.apply(this, arguments); destroyed = true; } }); var person = EmberObject['default'].create({ name: 'Steve Holt' }); var registry = new system__container.Registry(); var container = registry.container(); var parentController = EmberObject['default'].create({ container: container, person: person, name: 'Bob Loblaw' }); view = EmberView['default'].create({ container: container, template: compile['default']('{{#with person controller="person"}}{{controllerName}}{{/with}}'), controller: parentController }); registry.register('controller:person', Controller); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); utils.runDestroy(view); ok(destroyed, 'controller was destroyed properly'); }); QUnit.test("destroys the controller generated with {{with foo as bar controller='blah'}}", function() { var destroyed = false; var Controller = EmberController['default'].extend({ willDestroy: function() { this._super.apply(this, arguments); destroyed = true; } }); var person = EmberObject['default'].create({ name: 'Steve Holt' }); var registry = new system__container.Registry(); var container = registry.container(); var parentController = EmberObject['default'].create({ container: container, person: person, name: 'Bob Loblaw' }); view = EmberView['default'].create({ container: container, template: compile['default']('{{#with person as steve controller="person"}}{{controllerName}}{{/with}}'), controller: parentController }); registry.register('controller:person', Controller); utils.runAppend(view); utils.runDestroy(view); ok(destroyed, 'controller was destroyed properly'); }); QUnit.module("{{#with}} helper binding to view keyword", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView['default'].create({ template: compile['default']("We have: {{#with view.thing as fromView}}{{fromView.name}} and {{fromContext.name}}{{/with}}"), thing: { name: 'this is from the view' }, context: { fromContext: { name: "this is from the context" } } }); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("{{with}} helper can bind to keywords with 'as'", function() { equal(view.$().text(), "We have: this is from the view and this is from the context", "should render"); }); testWithAs("ember-htmlbars: {{#with x as |y|}}", "{{#with person as |tom|}}{{title}}: {{tom.name}}{{/with}}"); QUnit.module("Multiple Handlebars {{with foo as |bar|}} helpers", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView['default'].create({ template: compile['default']("Admin: {{#with admin as |person|}}{{person.name}}{{/with}} User: {{#with user as |person|}}{{person.name}}{{/with}}"), context: { admin: { name: "Tom Dale" }, user: { name: "Yehuda Katz" } } }); utils.runAppend(view); }, teardown: function() { utils.runDestroy(view); Ember.lookup = originalLookup; } }); QUnit.test("re-using the same variable with different #with blocks does not override each other", function() { equal(view.$().text(), "Admin: Tom Dale User: Yehuda Katz", "should be properly scoped"); }); QUnit.test("the scoped variable is not available outside the {{with}} block.", function() { run['default'](function() { view.set('template', compile['default']("{{name}}-{{#with other as |name|}}{{name}}{{/with}}-{{name}}")); view.set('context', { name: 'Stef', other: 'Yehuda' }); }); equal(view.$().text(), "Stef-Yehuda-Stef", "should be properly scoped after updating"); }); QUnit.test("nested {{with}} blocks shadow the outer scoped variable properly.", function() { run['default'](function() { view.set('template', compile['default']("{{#with first as |ring|}}{{ring}}-{{#with fifth as |ring|}}{{ring}}-{{#with ninth as |ring|}}{{ring}}-{{/with}}{{ring}}-{{/with}}{{ring}}{{/with}}")); view.set('context', { first: 'Limbo', fifth: 'Wrath', ninth: 'Treachery' }); }); equal(view.$().text(), "Limbo-Wrath-Treachery-Wrath-Limbo", "should be properly scoped after updating"); }); }); enifed('ember-htmlbars/tests/helpers/with_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/with_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/with_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/with_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/with_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/with_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/helpers/yield_test', ['ember-metal/run_loop', 'ember-views/views/view', 'ember-metal/computed', 'ember-runtime/system/container', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-runtime/system/native_array', 'ember-views/views/component', 'ember-metal/error', 'ember-htmlbars/helpers', 'ember-htmlbars/system/make-view-helper', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (run, EmberView, computed, system__container, property_get, property_set, native_array, Component, EmberError, helpers, makeViewHelper, compile, utils) { 'use strict'; /*jshint newcap:false*/ var view, registry, container; QUnit.module("ember-htmlbars: Support for {{yield}} helper", { setup: function() { registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); }, teardown: function() { run['default'](function() { Ember.TEMPLATES = {}; }); utils.runDestroy(view); utils.runDestroy(container); registry = container = view = null; } }); QUnit.test("a view with a layout set renders its template where the {{yield}} helper appears", function() { var ViewWithLayout = EmberView['default'].extend({ layout: compile['default']('

    {{title}}

    {{yield}}
    ') }); view = EmberView['default'].create({ withLayout: ViewWithLayout, template: compile['default']('{{#view view.withLayout title="My Fancy Page"}}
    Show something interesting here
    {{/view}}') }); utils.runAppend(view); equal(view.$('div.wrapper div.page-body').length, 1, 'page-body is embedded within wrapping my-page'); }); QUnit.test("block should work properly even when templates are not hard-coded", function() { registry.register('template:nester', compile['default']('

    {{title}}

    {{yield}}
    ')); registry.register('template:nested', compile['default']('{{#view "with-layout" title="My Fancy Page"}}
    Show something interesting here
    {{/view}}')); registry.register('view:with-layout', EmberView['default'].extend({ container: container, layoutName: 'nester' })); view = EmberView['default'].create({ container: container, templateName: 'nested' }); utils.runAppend(view); equal(view.$('div.wrapper div.page-body').length, 1, 'page-body is embedded within wrapping my-page'); }); QUnit.test("templates should yield to block, when the yield is embedded in a hierarchy of virtual views", function() { var TimesView = EmberView['default'].extend({ layout: compile['default']('
    {{#each item in view.index}}{{yield}}{{/each}}
    '), n: null, index: computed.computed(function() { var n = property_get.get(this, 'n'); var indexArray = native_array.A(); for (var i=0; i < n; i++) { indexArray[i] = i; } return indexArray; }) }); view = EmberView['default'].create({ timesView: TimesView, template: compile['default']('
    Counting to 5
    {{#view view.timesView n=5}}
    Hello
    {{/view}}
    ') }); utils.runAppend(view); equal(view.$('div#container div.times-item').length, 5, 'times-item is embedded within wrapping container 5 times, as expected'); }); QUnit.test("templates should yield to block, when the yield is embedded in a hierarchy of non-virtual views", function() { var NestingView = EmberView['default'].extend({ layout: compile['default']('{{#view tagName="div" classNames="nesting"}}{{yield}}{{/view}}') }); view = EmberView['default'].create({ nestingView: NestingView, template: compile['default']('
    {{#view view.nestingView}}
    Hello
    {{/view}}
    ') }); utils.runAppend(view); equal(view.$('div#container div.nesting div#block').length, 1, 'nesting view yields correctly even within a view hierarchy in the nesting view'); }); QUnit.test("block should not be required", function() { var YieldingView = EmberView['default'].extend({ layout: compile['default']('{{#view tagName="div" classNames="yielding"}}{{yield}}{{/view}}') }); view = EmberView['default'].create({ yieldingView: YieldingView, template: compile['default']('
    {{view view.yieldingView}}
    ') }); utils.runAppend(view); equal(view.$('div#container div.yielding').length, 1, 'yielding view is rendered as expected'); }); QUnit.test("yield uses the outer context", function() { var component = Component['default'].extend({ boundText: "inner", layout: compile['default']("

    {{boundText}}

    {{yield}}

    ") }); view = EmberView['default'].create({ controller: { boundText: "outer", component: component }, template: compile['default']('{{#view component}}{{boundText}}{{/view}}') }); utils.runAppend(view); equal(view.$('div p:contains(inner) + p:contains(outer)').length, 1, "Yield points at the right context"); }); QUnit.test("yield inside a conditional uses the outer context [DEPRECATED]", function() { var component = Component['default'].extend({ boundText: "inner", truthy: true, obj: {}, layout: compile['default']("

    {{boundText}}

    {{#if truthy}}{{#with obj}}{{yield}}{{/with}}{{/if}}

    ") }); view = EmberView['default'].create({ controller: { boundText: "outer", truthy: true, obj: { component: component, truthy: true, boundText: 'insideWith' } }, template: compile['default']('{{#with obj}}{{#if truthy}}{{#view component}}{{#if truthy}}{{boundText}}{{/if}}{{/view}}{{/if}}{{/with}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal(view.$('div p:contains(inner) + p:contains(insideWith)').length, 1, "Yield points at the right context"); }); QUnit.test("outer keyword doesn't mask inner component property", function () { var component = Component['default'].extend({ item: "inner", layout: compile['default']("

    {{item}}

    {{yield}}

    ") }); view = EmberView['default'].create({ controller: { boundText: "outer", component: component }, template: compile['default']('{{#with boundText as item}}{{#view component}}{{item}}{{/view}}{{/with}}') }); utils.runAppend(view); equal(view.$('div p:contains(inner) + p:contains(outer)').length, 1, "inner component property isn't masked by outer keyword"); }); QUnit.test("inner keyword doesn't mask yield property", function() { var component = Component['default'].extend({ boundText: "inner", layout: compile['default']("{{#with boundText as item}}

    {{item}}

    {{yield}}

    {{/with}}") }); view = EmberView['default'].create({ controller: { item: "outer", component: component }, template: compile['default']('{{#view component}}{{item}}{{/view}}') }); utils.runAppend(view); equal(view.$('div p:contains(inner) + p:contains(outer)').length, 1, "outer property isn't masked by inner keyword"); }); QUnit.test("can bind a keyword to a component and use it in yield", function() { var component = Component['default'].extend({ content: null, layout: compile['default']("

    {{content}}

    {{yield}}

    ") }); view = EmberView['default'].create({ controller: { boundText: "outer", component: component }, template: compile['default']('{{#with boundText as item}}{{#view component contentBinding="item"}}{{item}}{{/view}}{{/with}}') }); utils.runAppend(view); equal(view.$('div p:contains(outer) + p:contains(outer)').length, 1, "component and yield have keyword"); run['default'](function() { view.set('controller.boundText', 'update'); }); equal(view.$('div p:contains(update) + p:contains(update)').length, 1, "keyword has correctly propagated update"); }); if (!Ember.FEATURES.isEnabled('ember-htmlbars')) { // jscs:disable validateIndentation QUnit.test("yield uses the layout context for non component [DEPRECATED]", function() { view = EmberView['default'].create({ controller: { boundText: "outer", inner: { boundText: "inner" } }, layout: compile['default']("

    {{boundText}}

    {{#with inner}}

    {{yield}}

    {{/with}}"), template: compile['default']('{{boundText}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal('outerinner', view.$('p').text(), "Yield points at the right context"); }); // jscs:enable validateIndentation } QUnit.test("yield view should be a virtual view", function() { var component = Component['default'].extend({ isParentComponent: true, layout: compile['default']('{{yield}}') }); view = EmberView['default'].create({ template: compile['default']('{{#view component}}{{view includedComponent}}{{/view}}'), controller: { component: component, includedComponent: Component['default'].extend({ didInsertElement: function() { var parentView = this.get('parentView'); ok(parentView.get('isParentComponent'), "parent view is the parent component"); } }) } }); utils.runAppend(view); }); QUnit.test("adding a layout should not affect the context of normal views", function() { var parentView = EmberView['default'].create({ context: "ParentContext" }); view = EmberView['default'].create({ template: compile['default']("View context: {{this}}"), context: "ViewContext", _parentView: parentView }); run['default'](function() { view.createElement(); }); equal(view.$().text(), "View context: ViewContext"); property_set.set(view, 'layout', compile['default']("Layout: {{yield}}")); run['default'](function() { view.destroyElement(); view.createElement(); }); equal(view.$().text(), "Layout: View context: ViewContext"); utils.runDestroy(parentView); }); QUnit.test("yield should work for views even if _parentView is null", function() { view = EmberView['default'].create({ layout: compile['default']('Layout: {{yield}}'), template: compile['default']("View Content") }); run['default'](function() { view.createElement(); }); equal(view.$().text(), "Layout: View Content"); }); QUnit.module("ember-htmlbars: Component {{yield}}", { setup: function() {}, teardown: function() { utils.runDestroy(view); delete helpers["default"]['inner-component']; delete helpers["default"]['outer-component']; } }); QUnit.test("yield with nested components (#3220)", function() { var count = 0; var InnerComponent = Component['default'].extend({ layout: compile['default']("{{yield}}"), _yield: function (context, options, morph) { count++; if (count > 1) { throw new EmberError['default']('is looping'); } return this._super(context, options, morph); } }); helpers.registerHelper('inner-component', makeViewHelper['default'](InnerComponent)); var OuterComponent = Component['default'].extend({ layout: compile['default']("{{#inner-component}}{{yield}}{{/inner-component}}") }); helpers.registerHelper('outer-component', makeViewHelper['default'](OuterComponent)); view = EmberView['default'].create({ template: compile['default']( "{{#outer-component}}Hello world{{/outer-component}}" ) }); utils.runAppend(view); equal(view.$('div > span').text(), "Hello world"); }); QUnit.test("yield works inside a conditional in a component that has Ember._Metamorph mixed in", function() { var component = Component['default'].extend(Ember._Metamorph, { item: "inner", layout: compile['default']("

    {{item}}

    {{#if item}}

    {{yield}}

    {{/if}}") }); view = Ember.View.create({ controller: { item: "outer", component: component }, template: compile['default']('{{#view component}}{{item}}{{/view}}') }); utils.runAppend(view); equal(view.$().text(), 'innerouter', "{{yield}} renders yielded content inside metamorph component"); }); QUnit.test("view keyword works inside component yield", function () { var component = Component['default'].extend({ layout: compile['default']("

    {{yield}}

    ") }); view = EmberView['default'].create({ dummyText: 'hello', component: component, template: compile['default']('{{#view view.component}}{{view.dummyText}}{{/view}}') }); utils.runAppend(view); equal(view.$('div > p').text(), "hello", "view keyword inside component yield block should refer to the correct view"); }); }); enifed('ember-htmlbars/tests/helpers/yield_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/yield_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/helpers/yield_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/helpers/yield_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/helpers'); test('ember-htmlbars/tests/helpers/yield_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/helpers/yield_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/hooks/component_test', ['ember-views/component_lookup', 'container/registry', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (ComponentLookup, Registry, EmberView, compile, utils) { 'use strict'; var view, registry, container; // this is working around a bug in defeatureify that prevents nested flags // from being stripped var componentGenerationEnabled = false; if (Ember.FEATURES.isEnabled('ember-htmlbars-component-generation')) { componentGenerationEnabled = true; } if (componentGenerationEnabled) { QUnit.module("ember-htmlbars: component hook", { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.register('component-lookup:main', ComponentLookup['default']); }, teardown: function() { utils.runDestroy(view); utils.runDestroy(container); registry = container = view = null; } }); QUnit.test("component is looked up from the container", function() { registry.register('template:components/foo-bar', compile['default']('yippie!')); view = EmberView['default'].create({ container: container, template: compile['default']("") }); utils.runAppend(view); equal(view.$().text(), 'yippie!', 'component was looked up and rendered'); }); QUnit.test("asserts if component is not found", function() { view = EmberView['default'].create({ container: container, template: compile['default']("") }); expectAssertion(function() { utils.runAppend(view); }, 'You specified `foo-bar` in your template, but a component for `foo-bar` could not be found.'); }); } }); enifed('ember-htmlbars/tests/hooks/component_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/hooks'); test('ember-htmlbars/tests/hooks/component_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/hooks/component_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/hooks/component_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/hooks'); test('ember-htmlbars/tests/hooks/component_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/hooks/component_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/hooks/element_test', ['ember-views/views/view', 'ember-htmlbars/helpers', 'ember-runtime/tests/utils', 'ember-template-compiler/system/compile'], function (EmberView, helpers, utils, compile) { 'use strict'; var view; QUnit.module('ember-htmlbars: element hook', { teardown: function() { utils.runDestroy(view); delete helpers["default"].test; } }); QUnit.test('allows unbound usage within an element', function() { expect(4); function someHelper(params, hash, options, env) { equal(params[0], 'blammo'); equal(params[1], 'blazzico'); return "class='foo'"; } helpers.registerHelper('test', someHelper); view = EmberView['default'].create({ controller: { value: 'foo' }, template: compile['default']('
    Bar
    ') }); expectDeprecation(function() { utils.runAppend(view); }, 'Returning a string of attributes from a helper inside an element is deprecated.'); equal(view.$('.foo').length, 1, 'class attribute was added by helper'); }); QUnit.test('allows unbound usage within an element from property', function() { expect(2); view = EmberView['default'].create({ controller: { someProp: 'class="foo"' }, template: compile['default']('
    Bar
    ') }); expectDeprecation(function() { utils.runAppend(view); }, 'Returning a string of attributes from a helper inside an element is deprecated.'); equal(view.$('.foo').length, 1, 'class attribute was added by helper'); }); QUnit.test('allows unbound usage within an element creating multiple attributes', function() { expect(2); view = EmberView['default'].create({ controller: { someProp: 'class="foo" data-foo="bar"' }, template: compile['default']('
    Bar
    ') }); expectDeprecation(function() { utils.runAppend(view); }, 'Returning a string of attributes from a helper inside an element is deprecated.'); equal(view.$('.foo[data-foo="bar"]').length, 1, 'attributes added by helper'); }); }); enifed('ember-htmlbars/tests/hooks/element_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/hooks'); test('ember-htmlbars/tests/hooks/element_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/hooks/element_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/hooks/element_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/hooks'); test('ember-htmlbars/tests/hooks/element_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/hooks/element_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/hooks/text_node_test', ['ember-views/views/view', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-template-compiler/system/compile', 'htmlbars-test-helpers', 'ember-runtime/tests/utils'], function (EmberView, run, EmberObject, compile, htmlbars_test_helpers, utils) { 'use strict'; var view; QUnit.module("ember-htmlbars: hooks/text_node_test", { teardown: function() { utils.runDestroy(view); } }); QUnit.test("property is output", function() { view = EmberView['default'].create({ context: { name: 'erik' }, template: compile['default']("ohai {{name}}") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, 'ohai erik', "property is output"); }); QUnit.test("path is output", function() { view = EmberView['default'].create({ context: { name: { firstName: 'erik' } }, template: compile['default']("ohai {{name.firstName}}") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, 'ohai erik', "path is output"); }); QUnit.test("changed property updates", function() { var context = EmberObject['default'].create({ name: 'erik' }); view = EmberView['default'].create({ context: context, template: compile['default']("ohai {{name}}") }); utils.runAppend(view); htmlbars_test_helpers.equalInnerHTML(view.element, 'ohai erik', "precond - original property is output"); run['default'](context, context.set, 'name', 'mmun'); htmlbars_test_helpers.equalInnerHTML(view.element, 'ohai mmun', "new property is output"); }); }); enifed('ember-htmlbars/tests/hooks/text_node_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/hooks'); test('ember-htmlbars/tests/hooks/text_node_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/hooks/text_node_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/hooks/text_node_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/hooks'); test('ember-htmlbars/tests/hooks/text_node_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/hooks/text_node_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/htmlbars_test', ['ember-template-compiler/system/compile', 'ember-htmlbars/env', 'htmlbars-test-helpers', 'ember-metal/merge'], function (compile, defaultEnv, htmlbars_test_helpers, merge) { 'use strict'; QUnit.module("ember-htmlbars: main"); QUnit.test("HTMLBars is present and can be executed", function() { var template = compile['default']("ohai"); var env = merge['default']({ dom: defaultEnv.domHelper }, defaultEnv['default']); var output = template.render({}, env, document.body); htmlbars_test_helpers.equalHTML(output, "ohai"); }); }); enifed('ember-htmlbars/tests/htmlbars_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests'); test('ember-htmlbars/tests/htmlbars_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/htmlbars_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/htmlbars_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests'); test('ember-htmlbars/tests/htmlbars_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/htmlbars_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/binding_integration_test', ['ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-metal/binding', 'ember-runtime/system/object', 'ember-metal/computed', 'ember-views/views/container_view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils', 'ember-htmlbars/helpers', 'ember-metal/property_set'], function (run, jQuery, EmberView, binding, EmberObject, computed, ContainerView, compile, utils, helpers, property_set) { 'use strict'; var view, MyApp, originalLookup, lookup; var trim = jQuery['default'].trim; QUnit.module('ember-htmlbars: binding integration', { setup: function() { originalLookup = Ember.lookup; Ember.lookup = lookup = {}; MyApp = lookup.MyApp = EmberObject['default'].create({}); }, teardown: function() { Ember.lookup = originalLookup; utils.runDestroy(view); view = null; MyApp = null; } }); QUnit.test('should call a registered helper for mustache without parameters', function() { helpers.registerHelper('foobar', function() { return 'foobar'; }); view = EmberView['default'].create({ template: compile['default']('{{foobar}}') }); utils.runAppend(view); ok(view.$().text() === 'foobar', 'Regular helper was invoked correctly'); }); QUnit.test('should bind to the property if no registered helper found for a mustache without parameters', function() { view = EmberView['default'].createWithMixins({ template: compile['default']('{{view.foobarProperty}}'), foobarProperty: computed.computed(function() { return 'foobarProperty'; }) }); utils.runAppend(view); ok(view.$().text() === 'foobarProperty', 'Property was bound to correctly'); }); QUnit.test("should be able to update when bound property updates", function() { MyApp.set('controller', EmberObject['default'].create({ name: 'first' })); var View = EmberView['default'].extend({ template: compile['default']('{{view.value.name}}, {{view.computed}}'), valueBinding: 'MyApp.controller', computed: computed.computed(function() { return this.get('value.name') + ' - computed'; }).property('value') }); run['default'](function() { view = View.create(); }); utils.runAppend(view); run['default'](function() { MyApp.set('controller', EmberObject['default'].create({ name: 'second' })); }); equal(view.get('computed'), "second - computed", "view computed properties correctly update"); equal(view.$('i').text(), 'second, second - computed', "view rerenders when bound properties change"); }); QUnit.test('should cleanup bound properties on rerender', function() { view = EmberView['default'].create({ controller: EmberObject['default'].create({ name: 'wycats' }), template: compile['default']('{{name}}') }); utils.runAppend(view); equal(view.$().text(), 'wycats', 'rendered binding'); run['default'](view, 'rerender'); equal(view._childViews.length, 1); }); QUnit.test("should update bound values after view's parent is removed and then re-appended", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); var controller = EmberObject['default'].create(); var parentView = ContainerView['default'].create({ childViews: ['testView'], controller: controller, testView: EmberView['default'].create({ template: compile['default']("{{#if showStuff}}{{boundValue}}{{else}}Not true.{{/if}}") }) }); controller.setProperties({ showStuff: true, boundValue: "foo" }); utils.runAppend(parentView); view = parentView.get('testView'); equal(trim(view.$().text()), "foo"); run['default'](function() { property_set.set(controller, 'showStuff', false); }); equal(trim(view.$().text()), "Not true."); run['default'](function() { property_set.set(controller, 'showStuff', true); }); equal(trim(view.$().text()), "foo"); run['default'](function() { parentView.remove(); property_set.set(controller, 'showStuff', false); }); run['default'](function() { property_set.set(controller, 'showStuff', true); }); utils.runAppend(parentView); run['default'](function() { property_set.set(controller, 'boundValue', "bar"); }); equal(trim(view.$().text()), "bar"); utils.runDestroy(parentView); }); QUnit.test('should accept bindings as a string or an Ember.Binding', function() { var ViewWithBindings = EmberView['default'].extend({ oneWayBindingTestBinding: binding.Binding.oneWay('context.direction'), twoWayBindingTestBinding: binding.Binding.from('context.direction'), stringBindingTestBinding: 'context.direction', template: compile['default']( "one way: {{view.oneWayBindingTest}}, " + "two way: {{view.twoWayBindingTest}}, " + "string: {{view.stringBindingTest}}" ) }); view = EmberView['default'].create({ viewWithBindingsClass: ViewWithBindings, context: EmberObject['default'].create({ direction: "down" }), template: compile['default']("{{view view.viewWithBindingsClass}}") }); utils.runAppend(view); equal(trim(view.$().text()), "one way: down, two way: down, string: down"); }); }); enifed('ember-htmlbars/tests/integration/binding_integration_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/binding_integration_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/binding_integration_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/binding_integration_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/binding_integration_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/binding_integration_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/block_params_test', ['container/registry', 'ember-metal/run_loop', 'ember-views/component_lookup', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-htmlbars/helpers', 'ember-runtime/tests/utils'], function (Registry, run, ComponentLookup, View, compile, helpers, utils) { 'use strict'; var registry, container, view; function aliasHelper(params, hash, options, env) { this.appendChild(View['default'], { isVirtual: true, _morph: options.morph, template: options.template, _blockArguments: params }); } // jscs:disable validateIndentation QUnit.module("ember-htmlbars: block params", { setup: function() { helpers.registerHelper('alias', aliasHelper); registry = new Registry['default'](); container = registry.container(); registry.optionsForType('component', { singleton: false }); registry.optionsForType('view', { singleton: false }); registry.optionsForType('template', { instantiate: false }); registry.optionsForType('helper', { instantiate: false }); registry.register('component-lookup:main', ComponentLookup['default']); }, teardown: function() { delete helpers['default'].alias; utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; } }); QUnit.test("should raise error if helper not available", function() { view = View['default'].create({ template: compile['default']('{{#shouldfail}}{{/shouldfail}}') }); expectAssertion(function() { utils.runAppend(view); }, 'A helper named `shouldfail` could not be found'); }); QUnit.test("basic block params usage", function() { view = View['default'].create({ committer: { name: "rwjblue" }, template: compile['default']('{{#alias view.committer.name as |name|}}name: {{name}}, length: {{name.length}}{{/alias}}') }); utils.runAppend(view); equal(view.$().text(), "name: rwjblue, length: 7"); run['default'](function() { view.set('committer.name', "krisselden"); }); equal(view.$().text(), "name: krisselden, length: 10"); }); QUnit.test("nested block params shadow correctly", function() { view = View['default'].create({ context: { name: "ebryn" }, committer1: { name: "trek" }, committer2: { name: "machty" }, template: compile['default']( '{{name}}' + '{{#alias view.committer1.name as |name|}}' + '[{{name}}' + '{{#alias view.committer2.name as |name|}}' + '[{{name}}]' + '{{/alias}}' + '{{name}}]' + '{{/alias}}' + '{{name}}' + '{{#alias view.committer2.name as |name|}}' + '[{{name}}' + '{{#alias view.committer1.name as |name|}}' + '[{{name}}]' + '{{/alias}}' + '{{name}}]' + '{{/alias}}' + '{{name}}' ) }); utils.runAppend(view); equal(view.$().text(), "ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn"); }); QUnit.test("components can yield values", function() { registry.register('template:components/x-alias', compile['default']('{{yield param.name}}')); view = View['default'].create({ container: container, context: { name: "ebryn" }, committer1: { name: "trek" }, committer2: { name: "machty" }, template: compile['default']( '{{name}}' + '{{#x-alias param=view.committer1 as |name|}}' + '[{{name}}' + '{{#x-alias param=view.committer2 as |name|}}' + '[{{name}}]' + '{{/x-alias}}' + '{{name}}]' + '{{/x-alias}}' + '{{name}}' + '{{#x-alias param=view.committer2 as |name|}}' + '[{{name}}' + '{{#x-alias param=view.committer1 as |name|}}' + '[{{name}}]' + '{{/x-alias}}' + '{{name}}]' + '{{/x-alias}}' + '{{name}}' ) }); utils.runAppend(view); equal(view.$().text(), "ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn"); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/integration/block_params_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/block_params_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/block_params_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/block_params_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/block_params_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/block_params_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/component_invocation_test', ['ember-views/views/view', 'container/registry', 'ember-views/system/jquery', 'ember-template-compiler/system/compile', 'ember-views/component_lookup', 'ember-runtime/tests/utils'], function (EmberView, Registry, jQuery, compile, ComponentLookup, utils) { 'use strict'; var registry, container, view; QUnit.module('component - invocation', { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('component', { singleton: false }); registry.optionsForType('view', { singleton: false }); registry.optionsForType('template', { instantiate: false }); registry.optionsForType('helper', { instantiate: false }); registry.register('component-lookup:main', ComponentLookup['default']); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; } }); QUnit.test('non-block without properties', function() { expect(1); registry.register('template:components/non-block', compile['default']('In layout')); view = EmberView['default'].extend({ template: compile['default']('{{non-block}}'), container: container }).create(); utils.runAppend(view); equal(jQuery['default']('#qunit-fixture').text(), 'In layout'); }); QUnit.test('block without properties', function() { expect(1); registry.register('template:components/with-block', compile['default']('In layout - {{yield}}')); view = EmberView['default'].extend({ template: compile['default']('{{#with-block}}In template{{/with-block}}'), container: container }).create(); utils.runAppend(view); equal(jQuery['default']('#qunit-fixture').text(), 'In layout - In template'); }); QUnit.test('non-block with properties', function() { expect(1); registry.register('template:components/non-block', compile['default']('In layout - someProp: {{someProp}}')); view = EmberView['default'].extend({ template: compile['default']('{{non-block someProp="something here"}}'), container: container }).create(); utils.runAppend(view); equal(jQuery['default']('#qunit-fixture').text(), 'In layout - someProp: something here'); }); QUnit.test('block with properties', function() { expect(1); registry.register('template:components/with-block', compile['default']('In layout - someProp: {{someProp}} - {{yield}}')); view = EmberView['default'].extend({ template: compile['default']('{{#with-block someProp="something here"}}In template{{/with-block}}'), container: container }).create(); utils.runAppend(view); equal(jQuery['default']('#qunit-fixture').text(), 'In layout - someProp: something here - In template'); }); }); enifed('ember-htmlbars/tests/integration/component_invocation_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/component_invocation_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/component_invocation_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/component_invocation_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/component_invocation_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/component_invocation_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/escape_integration_test', ['ember-metal/run_loop', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-metal/property_set', 'ember-metal/platform/create', 'ember-runtime/tests/utils'], function (run, EmberView, compile, property_set, o_create, utils) { 'use strict'; var view; QUnit.module('ember-htmlbars: Integration with Globals', { teardown: function() { utils.runDestroy(view); view = null; } }); QUnit.test('should read from a global-ish simple local path without deprecation', function() { view = EmberView['default'].create({ context: { NotGlobal: 'Gwar' }, template: compile['default']('{{NotGlobal}}') }); expectNoDeprecation(); utils.runAppend(view); equal(view.$().text(), 'Gwar'); }); QUnit.test('should read a number value', function() { var context = { aNumber: 1 }; view = EmberView['default'].create({ context: context, template: compile['default']('{{aNumber}}') }); utils.runAppend(view); equal(view.$().text(), '1'); run['default'](function() { property_set.set(context, 'aNumber', 2); }); equal(view.$().text(), '2'); }); QUnit.test('should read an escaped number value', function() { var context = { aNumber: 1 }; view = EmberView['default'].create({ context: context, template: compile['default']('{{{aNumber}}}') }); utils.runAppend(view); equal(view.$().text(), '1'); run['default'](function() { property_set.set(context, 'aNumber', 2); }); equal(view.$().text(), '2'); }); QUnit.test('should read from an Object.create(null)', function() { // Use ember's polyfill for Object.create var nullObject = o_create['default'](null); nullObject['foo'] = 'bar'; view = EmberView['default'].create({ context: { nullObject: nullObject }, template: compile['default']('{{nullObject.foo}}') }); utils.runAppend(view); equal(view.$().text(), 'bar'); run['default'](function() { property_set.set(nullObject, 'foo', 'baz'); }); equal(view.$().text(), 'baz'); }); QUnit.test('should escape HTML in primitive value contexts when using normal mustaches', function() { view = EmberView['default'].create({ context: 'MaxJames', template: compile['default']('{{this}}') }); utils.runAppend(view); equal(view.$('b').length, 0, 'does not create an element'); equal(view.$().text(), 'MaxJames', 'inserts entities, not elements'); run['default'](function() { property_set.set(view, 'context', 'MaxJames'); }); equal(view.$().text(), 'MaxJames', 'updates with entities, not elements'); equal(view.$('i').length, 0, 'does not create an element when value is updated'); }); QUnit.test('should not escape HTML in primitive value contexts when using triple mustaches', function() { view = EmberView['default'].create({ context: 'MaxJames', template: compile['default']('{{{this}}}') }); utils.runAppend(view); equal(view.$('b').length, 2, 'creates an element'); run['default'](function() { property_set.set(view, 'context', 'MaxJames'); }); equal(view.$('i').length, 2, 'creates an element when value is updated'); }); }); enifed('ember-htmlbars/tests/integration/escape_integration_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/escape_integration_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/escape_integration_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/escape_integration_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/escape_integration_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/escape_integration_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/globals_integration_test', ['ember-metal/core', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (Ember, EmberView, compile, utils) { 'use strict'; var view, originalLookup, lookup; var originalLookup = Ember['default'].lookup; QUnit.module('ember-htmlbars: Integration with Globals', { setup: function() { Ember['default'].lookup = lookup = {}; }, teardown: function() { utils.runDestroy(view); view = null; Ember['default'].lookup = lookup = originalLookup; } }); QUnit.test('should read from globals (DEPRECATED)', function() { Ember['default'].lookup.Global = 'Klarg'; view = EmberView['default'].create({ template: compile['default']('{{Global}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Global lookup of Global from a Handlebars template is deprecated.'); equal(view.$().text(), Ember['default'].lookup.Global); }); QUnit.test('should read from globals with a path (DEPRECATED)', function() { Ember['default'].lookup.Global = { Space: 'Klarg' }; view = EmberView['default'].create({ template: compile['default']('{{Global.Space}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Global lookup of Global.Space from a Handlebars template is deprecated.'); equal(view.$().text(), Ember['default'].lookup.Global.Space); }); QUnit.test('with context, should read from globals (DEPRECATED)', function() { Ember['default'].lookup.Global = 'Klarg'; view = EmberView['default'].create({ context: {}, template: compile['default']('{{Global}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Global lookup of Global from a Handlebars template is deprecated.'); equal(view.$().text(), Ember['default'].lookup.Global); }); QUnit.test('with context, should read from globals with a path (DEPRECATED)', function() { Ember['default'].lookup.Global = { Space: 'Klarg' }; view = EmberView['default'].create({ context: {}, template: compile['default']('{{Global.Space}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Global lookup of Global.Space from a Handlebars template is deprecated.'); equal(view.$().text(), Ember['default'].lookup.Global.Space); }); }); enifed('ember-htmlbars/tests/integration/globals_integration_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/globals_integration_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/globals_integration_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/globals_integration_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/globals_integration_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/globals_integration_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/select_in_template_test', ['ember-runtime/system/object', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/system/event_dispatcher', 'ember-metal/computed', 'ember-runtime/system/namespace', 'ember-runtime/controllers/array_controller', 'ember-runtime/system/array_proxy', 'ember-views/views/select', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils'], function (EmberObject, run, EmberView, EventDispatcher, computed, Namespace, ArrayController, ArrayProxy, SelectView, compile, utils) { 'use strict'; var dispatcher, view; QUnit.module("ember-htmlbars: Ember.Select - usage inside templates", { setup: function() { dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); }, teardown: function() { utils.runDestroy(dispatcher); utils.runDestroy(view); } }); QUnit.test("works from a template with bindings", function() { var Person = EmberObject['default'].extend({ id: null, firstName: null, lastName: null, fullName: computed.computed(function() { return this.get('firstName') + " " + this.get('lastName'); }).property('firstName', 'lastName') }); var erik = Person.create({ id: 4, firstName: 'Erik', lastName: 'Bryn' }); var application = Namespace['default'].create(); application.peopleController = ArrayController['default'].create({ content: Ember.A([ Person.create({ id: 1, firstName: 'Yehuda', lastName: 'Katz' }), Person.create({ id: 2, firstName: 'Tom', lastName: 'Dale' }), Person.create({ id: 3, firstName: 'Peter', lastName: 'Wagenet' }), erik ]) }); application.selectedPersonController = EmberObject['default'].create({ person: null }); view = EmberView['default'].create({ app: application, selectView: SelectView['default'], template: compile['default']( '{{view view.selectView viewName="select"' + ' contentBinding="view.app.peopleController"' + ' optionLabelPath="content.fullName"' + ' optionValuePath="content.id"' + ' prompt="Pick a person:"' + ' selectionBinding="view.app.selectedPersonController.person"}}' ) }); utils.runAppend(view); var select = view.get('select'); ok(select.$().length, "Select was rendered"); equal(select.$('option').length, 5, "Options were rendered"); equal(select.$().text(), "Pick a person:Yehuda KatzTom DalePeter WagenetErik Bryn\n", "Option values were rendered"); equal(select.get('selection'), null, "Nothing has been selected"); run['default'](function() { application.selectedPersonController.set('person', erik); }); equal(select.get('selection'), erik, "Selection was updated through binding"); run['default'](function() { application.peopleController.pushObject(Person.create({ id: 5, firstName: "James", lastName: "Rosen" })); }); equal(select.$('option').length, 6, "New option was added"); equal(select.get('selection'), erik, "Selection was maintained after new option was added"); }); QUnit.test("upon content change, the DOM should reflect the selection (#481)", function() { var userOne = { name: 'Mike', options: Ember.A(['a', 'b']), selectedOption: 'a' }; var userTwo = { name: 'John', options: Ember.A(['c', 'd']), selectedOption: 'd' }; view = EmberView['default'].create({ user: userOne, selectView: SelectView['default'], template: compile['default']( '{{view view.selectView viewName="select"' + ' contentBinding="view.user.options"' + ' selectionBinding="view.user.selectedOption"}}' ) }); utils.runAppend(view); var select = view.get('select'); var selectEl = select.$()[0]; equal(select.get('selection'), 'a', "Precond: Initial selection is correct"); equal(selectEl.selectedIndex, 0, "Precond: The DOM reflects the correct selection"); run['default'](function() { view.set('user', userTwo); }); equal(select.get('selection'), 'd', "Selection was properly set after content change"); equal(selectEl.selectedIndex, 1, "The DOM reflects the correct selection"); }); QUnit.test("upon content change with Array-like content, the DOM should reflect the selection", function() { var tom = { id: 4, name: 'Tom' }; var sylvain = { id: 5, name: 'Sylvain' }; var proxy = ArrayProxy['default'].create({ content: Ember.A(), selectedOption: sylvain }); view = EmberView['default'].create({ proxy: proxy, selectView: SelectView['default'], template: compile['default']( '{{view view.selectView viewName="select"' + ' contentBinding="view.proxy"' + ' selectionBinding="view.proxy.selectedOption"}}' ) }); utils.runAppend(view); var select = view.get('select'); var selectEl = select.$()[0]; equal(selectEl.selectedIndex, -1, "Precond: The DOM reflects the lack of selection"); run['default'](function() { proxy.set('content', Ember.A([tom, sylvain])); }); equal(select.get('selection'), sylvain, "Selection was properly set after content change"); equal(selectEl.selectedIndex, 1, "The DOM reflects the correct selection"); }); function testValueBinding(templateString) { view = EmberView['default'].create({ collection: Ember.A([{ name: 'Wes', value: 'w' }, { name: 'Gordon', value: 'g' }]), val: 'g', selectView: SelectView['default'], template: compile['default'](templateString) }); utils.runAppend(view); var select = view.get('select'); var selectEl = select.$()[0]; equal(view.get('val'), 'g', "Precond: Initial bound property is correct"); equal(select.get('value'), 'g', "Precond: Initial selection is correct"); equal(selectEl.selectedIndex, 2, "Precond: The DOM reflects the correct selection"); select.$('option:eq(2)').removeAttr('selected'); select.$('option:eq(1)').prop('selected', true); select.$().trigger('change'); equal(view.get('val'), 'w', "Updated bound property is correct"); equal(select.get('value'), 'w', "Updated selection is correct"); equal(selectEl.selectedIndex, 1, "The DOM is updated to reflect the new selection"); } QUnit.test("select element should correctly initialize and update selectedIndex and bound properties when using valueBinding (old xBinding='' syntax)", function() { testValueBinding( '{{view view.selectView viewName="select"' + ' contentBinding="view.collection"' + ' optionLabelPath="content.name"' + ' optionValuePath="content.value"' + ' prompt="Please wait..."' + ' valueBinding="view.val"}}' ); }); QUnit.test("select element should correctly initialize and update selectedIndex and bound properties when using valueBinding (new quoteless binding shorthand)", function() { testValueBinding( '{{view view.selectView viewName="select"' + ' content=view.collection' + ' optionLabelPath="content.name"' + ' optionValuePath="content.value"' + ' prompt="Please wait..."' + ' value=view.val}}' ); }); function testSelectionBinding(templateString) { view = EmberView['default'].create({ collection: Ember.A([{ name: 'Wes', value: 'w' }, { name: 'Gordon', value: 'g' }]), selection: { name: 'Gordon', value: 'g' }, selectView: SelectView['default'], template: compile['default'](templateString) }); utils.runAppend(view); var select = view.get('select'); var selectEl = select.$()[0]; equal(view.get('selection.value'), 'g', "Precond: Initial bound property is correct"); equal(select.get('selection.value'), 'g', "Precond: Initial selection is correct"); equal(selectEl.selectedIndex, 2, "Precond: The DOM reflects the correct selection"); equal(select.$('option:eq(2)').prop('selected'), true, "Precond: selected property is set to proper option"); select.$('option:eq(2)').removeAttr('selected'); select.$('option:eq(1)').prop('selected', true); select.$().trigger('change'); equal(view.get('selection.value'), 'w', "Updated bound property is correct"); equal(select.get('selection.value'), 'w', "Updated selection is correct"); equal(selectEl.selectedIndex, 1, "The DOM is updated to reflect the new selection"); equal(select.$('option:eq(1)').prop('selected'), true, "Selected property is set to proper option"); } QUnit.test("select element should correctly initialize and update selectedIndex and bound properties when using selectionBinding (old xBinding='' syntax)", function() { testSelectionBinding( '{{view view.selectView viewName="select"' + ' contentBinding="view.collection"' + ' optionLabelPath="content.name"' + ' optionValuePath="content.value"' + ' prompt="Please wait..."' + ' selectionBinding="view.selection"}}' ); }); QUnit.test("select element should correctly initialize and update selectedIndex and bound properties when using selectionBinding (new quoteless binding shorthand)", function() { testSelectionBinding( '{{view view.selectView viewName="select"' + ' content=view.collection' + ' optionLabelPath="content.name"' + ' optionValuePath="content.value"' + ' prompt="Please wait..."' + ' selection=view.selection}}' ); }); QUnit.test("select element should correctly initialize and update selectedIndex and bound properties when using selectionBinding and optionValuePath with custom path", function() { var templateString = '{{view view.selectView viewName="select"' + ' content=view.collection' + ' optionLabelPath="content.name"' + ' optionValuePath="content.val"' + ' prompt="Please wait..."' + ' selection=view.selection}}'; view = EmberView['default'].create({ collection: Ember.A([{ name: 'Wes', val: 'w' }, { name: 'Gordon', val: 'g' }]), selection: { name: 'Gordon', val: 'g' }, selectView: SelectView['default'], template: Ember.Handlebars.compile(templateString) }); run['default'](function() { view.appendTo('#qunit-fixture'); }); var select = view.get('select'); var selectEl = select.$()[0]; equal(view.get('selection.val'), 'g', "Precond: Initial bound property is correct"); equal(select.get('selection.val'), 'g', "Precond: Initial selection is correct"); equal(selectEl.selectedIndex, 2, "Precond: The DOM reflects the correct selection"); equal(select.$('option:eq(1)').prop('selected'), false, "Precond: selected property is set to proper option"); select.$('option:eq(2)').removeAttr('selected'); select.$('option:eq(1)').prop('selected', true); select.$().trigger('change'); equal(view.get('selection.val'), 'w', "Updated bound property is correct"); equal(select.get('selection.val'), 'w', "Updated selection is correct"); equal(selectEl.selectedIndex, 1, "The DOM is updated to reflect the new selection"); equal(select.$('option:eq(1)').prop('selected'), true, "selected property is set to proper option"); }); }); enifed('ember-htmlbars/tests/integration/select_in_template_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/select_in_template_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/select_in_template_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/select_in_template_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/select_in_template_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/select_in_template_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/tagless_views_rerender_test', ['ember-metal/run_loop', 'ember-views/views/view', 'ember-htmlbars/compat', 'ember-runtime/tests/utils'], function (run, EmberView, EmberHandlebars, utils) { 'use strict'; var view; var compile = EmberHandlebars['default'].compile; QUnit.module("ember-htmlbars: tagless views should be able to add/remove child views", { teardown: function() { utils.runDestroy(view); } }); QUnit.test("can insert new child views after initial tagless view rendering", function() { view = EmberView['default'].create({ shouldShow: false, array: Ember.A([1]), template: compile('{{#if view.shouldShow}}{{#each item in view.array}}{{item}}{{/each}}{{/if}}') }); utils.runAppend(view); equal(view.$().text(), ''); run['default'](function() { view.set('shouldShow', true); }); equal(view.$().text(), '1'); run['default'](function() { view.get('array').pushObject(2); }); equal(view.$().text(), '12'); }); QUnit.test("can remove child views after initial tagless view rendering", function() { view = EmberView['default'].create({ shouldShow: false, array: Ember.A([]), template: compile('{{#if view.shouldShow}}{{#each item in view.array}}{{item}}{{/each}}{{/if}}') }); utils.runAppend(view); equal(view.$().text(), ''); run['default'](function() { view.set('shouldShow', true); view.get('array').pushObject(1); }); equal(view.$().text(), '1'); run['default'](function() { view.get('array').removeObject(1); }); equal(view.$().text(), ''); }); }); enifed('ember-htmlbars/tests/integration/tagless_views_rerender_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/tagless_views_rerender_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/tagless_views_rerender_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/tagless_views_rerender_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/tagless_views_rerender_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/tagless_views_rerender_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/integration/with_view_test', ['ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-runtime/system/container', 'ember-runtime/system/object', 'ember-views/views/metamorph_view', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils', 'ember-metal/property_set'], function (run, jQuery, EmberView, system__container, EmberObject, _MetamorphView, compile, utils, property_set) { 'use strict'; var view, registry, container; var trim = jQuery['default'].trim; QUnit.module('ember-htmlbars: {{#with}} and {{#view}} integration', { setup: function() { registry = new system__container.Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.register('view:default', _MetamorphView['default']); registry.register('view:toplevel', EmberView['default'].extend()); }, teardown: function() { utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; } }); QUnit.test('View should update when the property used with the #with helper changes [DEPRECATED]', function() { registry.register('template:foo', compile['default']('

    {{#with view.content}}{{wham}}{{/with}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'foo', content: EmberObject['default'].create({ wham: 'bam', thankYou: "ma'am" }) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal(view.$('#first').text(), 'bam', 'precond - view renders Handlebars template'); run['default'](function() { property_set.set(view, 'content', EmberObject['default'].create({ wham: 'bazam' })); }); equal(view.$('#first').text(), 'bazam', 'view updates when a bound property changes'); }); QUnit.test('should expose a view keyword [DEPRECATED]', function() { var templateString = '{{#with view.differentContent}}{{view.foo}}{{#view baz="bang"}}{{view.baz}}{{/view}}{{/with}}'; view = EmberView['default'].create({ container: container, differentContent: { view: { foo: 'WRONG', baz: 'WRONG' } }, foo: 'bar', template: compile['default'](templateString) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal(view.$().text(), 'barbang', 'renders values from view and child view'); }); QUnit.test('bindings can be `this`, in which case they *are* the current context [DEPRECATED]', function() { view = EmberView['default'].create({ museumOpen: true, museumDetails: EmberObject['default'].create({ name: 'SFMoMA', price: 20, museumView: EmberView['default'].extend({ template: compile['default']('Name: {{view.museum.name}} Price: ${{view.museum.price}}') }) }), template: compile['default']('{{#if view.museumOpen}} {{#with view.museumDetails}}{{view museumView museum=this}} {{/with}}{{/if}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); equal(trim(view.$().text()), 'Name: SFMoMA Price: $20', 'should print baz twice'); }); QUnit.test('child views can be inserted inside a bind block', function() { registry.register('template:nester', compile['default']('

    Hello {{world}}

    {{view view.bqView}}')); registry.register('template:nested', compile['default']('
    Goodbye {{#with content as thing}}{{thing.blah}} {{view view.otherView}}{{/with}} {{world}}
    ')); registry.register('template:other', compile['default']('cruel')); var context = { world: 'world!' }; var OtherView = EmberView['default'].extend({ container: container, templateName: 'other' }); var BQView = EmberView['default'].extend({ container: container, otherView: OtherView, tagName: 'blockquote', templateName: 'nested' }); view = EmberView['default'].create({ container: container, bqView: BQView, context: context, templateName: 'nester' }); property_set.set(context, 'content', EmberObject['default'].create({ blah: 'wot' })); utils.runAppend(view); ok(view.$('#hello-world:contains("Hello world!")').length, 'The parent view renders its contents'); ok(view.$('blockquote').text().match(/Goodbye.*wot.*cruel.*world\!/), 'The child view renders its content once'); ok(view.$().text().match(/Hello world!.*Goodbye.*wot.*cruel.*world\!/), 'parent view should appear before the child view'); }); QUnit.test('views render their template in the context of the parent view\'s context', function() { registry.register('template:parent', compile['default']('

    {{#with content as person}}{{#view}}{{person.firstName}} {{person.lastName}}{{/view}}{{/with}}

    ')); var context = { content: { firstName: 'Lana', lastName: 'del Heeeyyyyyy' } }; view = EmberView['default'].create({ container: container, templateName: 'parent', context: context }); utils.runAppend(view); equal(view.$('h1').text(), 'Lana del Heeeyyyyyy', 'renders properties from parent context'); }); QUnit.test('views make a view keyword available that allows template to reference view context', function() { registry.register('template:parent', compile['default']('

    {{#with view.content as person}}{{#view person.subview}}{{view.firstName}} {{person.lastName}}{{/view}}{{/with}}

    ')); view = EmberView['default'].create({ container: container, templateName: 'parent', content: { subview: EmberView['default'].extend({ firstName: 'Brodele' }), firstName: 'Lana', lastName: 'del Heeeyyyyyy' } }); utils.runAppend(view); equal(view.$('h1').text(), 'Brodele del Heeeyyyyyy', 'renders properties from parent context'); }); }); enifed('ember-htmlbars/tests/integration/with_view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/with_view_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/integration/with_view_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/integration/with_view_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/integration'); test('ember-htmlbars/tests/integration/with_view_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/integration/with_view_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/system/bootstrap_test', ['ember-views/system/jquery', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-runtime/tests/utils', 'ember-htmlbars/system/bootstrap'], function (jQuery, run, EmberView, utils, bootstrap) { 'use strict'; var trim = jQuery['default'].trim; var originalLookup = Ember.lookup; var lookup, App, view; QUnit.module("ember-htmlbars: bootstrap", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; }, teardown: function() { Ember.TEMPLATES = {}; Ember.lookup = originalLookup; utils.runDestroy(App); utils.runDestroy(view); } }); function checkTemplate(templateName) { run['default'](function() { bootstrap['default'](jQuery['default']('#qunit-fixture')); }); var template = Ember.TEMPLATES[templateName]; ok(template, 'template is available on Ember.TEMPLATES'); equal(jQuery['default']('#qunit-fixture script').length, 0, 'script removed'); var view = EmberView['default'].create({ template: template, context: { firstName: 'Tobias', drug: 'teamocil' } }); run['default'](function() { view.createElement(); }); equal(trim(view.$().text()), 'Tobias takes teamocil', 'template works'); utils.runDestroy(view); } QUnit.test('template with data-template-name should add a new template to Ember.TEMPLATES', function() { jQuery['default']('#qunit-fixture').html(''); checkTemplate('funkyTemplate'); }); QUnit.test('template with id instead of data-template-name should add a new template to Ember.TEMPLATES', function() { jQuery['default']('#qunit-fixture').html(''); checkTemplate('funkyTemplate'); }); QUnit.test('template without data-template-name or id should default to application', function() { jQuery['default']('#qunit-fixture').html(''); checkTemplate('application'); }); if (typeof Handlebars === 'object') { QUnit.test('template with type text/x-raw-handlebars should be parsed', function() { jQuery['default']('#qunit-fixture').html(''); run['default'](function() { bootstrap['default'](jQuery['default']('#qunit-fixture')); }); ok(Ember.TEMPLATES['funkyTemplate'], 'template with name funkyTemplate available'); // This won't even work with Ember templates equal(trim(Ember.TEMPLATES['funkyTemplate']({ name: 'Tobias' })), "Tobias"); }); } QUnit.test('duplicated default application templates should throw exception', function() { jQuery['default']('#qunit-fixture').html(''); throws(function () { bootstrap['default'](jQuery['default']('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); QUnit.test('default application template and id application template present should throw exception', function() { jQuery['default']('#qunit-fixture').html(''); throws(function () { bootstrap['default'](jQuery['default']('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); QUnit.test('default application template and data-template-name application template present should throw exception', function() { jQuery['default']('#qunit-fixture').html(''); throws(function () { bootstrap['default'](jQuery['default']('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); QUnit.test('duplicated template id should throw exception', function() { jQuery['default']('#qunit-fixture').html(''); throws(function () { bootstrap['default'](jQuery['default']('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); QUnit.test('duplicated template data-template-name should throw exception', function() { jQuery['default']('#qunit-fixture').html(''); throws(function () { bootstrap['default'](jQuery['default']('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); if (Ember.component) { QUnit.test('registerComponents initializer', function() { Ember.TEMPLATES['components/x-apple'] = 'asdf'; App = run['default'](Ember.Application, 'create'); ok(Ember.Handlebars.helpers['x-apple'], 'x-apple helper is present'); ok(App.__container__.has('component:x-apple'), 'the container is aware of x-apple'); }); QUnit.test('registerComponents and generated components', function() { Ember.TEMPLATES['components/x-apple'] = 'asdf'; App = run['default'](Ember.Application, 'create'); view = App.__container__.lookup('component:x-apple'); equal(view.get('layoutName'), 'components/x-apple', 'has correct layout name'); }); QUnit.test('registerComponents and non-generated components', function() { Ember.TEMPLATES['components/x-apple'] = 'asdf'; run['default'](function() { App = Ember.Application.create(); // currently Component code must be loaded before initializers // this is mostly due to how they are bootstrapped. We will hopefully // sort this out soon. App.XAppleComponent = Ember.Component.extend({ isCorrect: true }); }); view = App.__container__.lookup('component:x-apple'); equal(view.get('layoutName'), 'components/x-apple', 'has correct layout name'); ok(view.get('isCorrect'), 'ensure a non-generated component'); }); } }); enifed('ember-htmlbars/tests/system/bootstrap_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/bootstrap_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/system/bootstrap_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/system/bootstrap_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/bootstrap_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/system/bootstrap_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/system/lookup-helper_test', ['ember-htmlbars/system/lookup-helper', 'ember-views/component_lookup', 'container/registry', 'ember-views/views/component'], function (lookupHelper, ComponentLookup, Registry, Component) { 'use strict'; function generateEnv(helpers) { return { helpers: (helpers ? helpers : {}) }; } function generateContainer() { var registry = new Registry['default'](); var container = registry.container(); registry.optionsForType('helper', { instantiate: false }); registry.register('component-lookup:main', ComponentLookup['default']); return container; } QUnit.module('ember-htmlbars: lookupHelper hook'); QUnit.test('looks for helpers in the provided `env.helpers`', function() { var env = generateEnv({ 'flubarb': function() { } }); var actual = lookupHelper['default']('flubarb', null, env); equal(actual, env.helpers.flubarb, 'helpers are looked up on env'); }); QUnit.test('returns undefined if no container exists (and helper is not found in env)', function() { var env = generateEnv(); var view = {}; var actual = lookupHelper['default']('flubarb', view, env); equal(actual, undefined, 'does not blow up if view does not have a container'); }); QUnit.test('does not lookup in the container if the name does not contain a dash (and helper is not found in env)', function() { var env = generateEnv(); var view = { container: { lookup: function() { ok(false, 'should not lookup in the container'); } } }; var actual = lookupHelper['default']('flubarb', view, env); equal(actual, undefined, 'does not blow up if view does not have a container'); }); QUnit.test('does a lookup in the container if the name contains a dash (and helper is not found in env)', function() { var env = generateEnv(); var view = { container: generateContainer() }; function someName() {} someName.isHTMLBars = true; view.container._registry.register('helper:some-name', someName); var actual = lookupHelper['default']('some-name', view, env); equal(actual, someName, 'does not wrap provided function if `isHTMLBars` is truthy'); }); QUnit.test('wraps helper from container in a Handlebars compat helper', function() { expect(2); var env = generateEnv(); var view = { container: generateContainer() }; var called; function someName() { called = true; } view.container._registry.register('helper:some-name', someName); var actual = lookupHelper['default']('some-name', view, env); ok(actual.isHTMLBars, 'wraps provided helper in an HTMLBars compatible helper'); var fakeParams = []; var fakeHash = {}; var fakeOptions = { morph: { update: function() { } } }; var fakeEnv = {}; actual.helperFunction(fakeParams, fakeHash, fakeOptions, fakeEnv); ok(called, 'HTMLBars compatible wrapper is wraping the provided function'); }); QUnit.test('asserts if component-lookup:main cannot be found', function() { var env = generateEnv(); var view = { container: generateContainer() }; view.container._registry.unregister('component-lookup:main'); expectAssertion(function() { lookupHelper['default']('some-name', view, env); }, 'Could not find \'component-lookup:main\' on the provided container, which is necessary for performing component lookups'); }); QUnit.test('registers a helper in the container if component is found', function() { var env = generateEnv(); var view = { container: generateContainer() }; view.container._registry.register('component:some-name', Component['default']); lookupHelper['default']('some-name', view, env); ok(view.container.lookup('helper:some-name'), 'new helper was registered'); }); }); enifed('ember-htmlbars/tests/system/lookup-helper_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/lookup-helper_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/system/lookup-helper_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/system/lookup-helper_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/lookup-helper_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/system/lookup-helper_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/system/make_bound_helper_test', ['ember-views/views/view', 'ember-metal/run_loop', 'container/registry', 'ember-htmlbars/system/make_bound_helper', 'ember-template-compiler/system/compile', 'ember-runtime/tests/utils', 'ember-runtime/system/string', 'ember-views/views/simple_bound_view', 'ember-runtime/system/object'], function (EmberView, run, Registry, makeBoundHelper, compile, utils, string, SimpleBoundView, EmberObject) { 'use strict'; var view, registry, container; function registerRepeatHelper() { registry.register('helper:x-repeat', makeBoundHelper['default'](function(params, hash, options, env) { var times = hash.times || 1; return new Array(times + 1).join(params[0]); })); } // jscs:disable validateIndentation QUnit.module("ember-htmlbars: makeBoundHelper", { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('helper', { instantiate: false }); }, teardown: function() { utils.runDestroy(view); utils.runDestroy(container); registry = container = view = null; } }); QUnit.test("should update bound helpers in a subexpression when properties change", function() { registry.register('helper:x-dasherize', makeBoundHelper['default'](function(params, hash, options, env) { return string.dasherize(params[0]); })); view = EmberView['default'].create({ container: container, controller: { prop: "isThing" }, template: compile['default']("
    {{prop}}
    ") }); utils.runAppend(view); equal(view.$('div[data-foo="is-thing"]').text(), 'isThing', "helper output is correct"); run['default'](view, 'set', 'controller.prop', 'notThing'); equal(view.$('div[data-foo="not-thing"]').text(), 'notThing', "helper output is correct"); }); QUnit.test("should update bound helpers when properties change", function() { registry.register('helper:x-capitalize', makeBoundHelper['default'](function(params, hash, options, env) { return params[0].toUpperCase(); })); view = EmberView['default'].create({ container: container, controller: { name: "Brogrammer" }, template: compile['default']("{{x-capitalize name}}") }); utils.runAppend(view); equal(view.$().text(), 'BROGRAMMER', "helper output is correct"); run['default'](view, 'set', 'controller.name', 'wes'); equal(view.$().text(), 'WES', "helper output updated"); }); QUnit.test("should update bound helpers when hash properties change", function() { registerRepeatHelper(); view = EmberView['default'].create({ container: container, controller: { phrase: "Yo", repeatCount: 1 }, template: compile['default']("{{x-repeat phrase times=repeatCount}}") }); utils.runAppend(view); equal(view.$().text(), 'Yo', "initial helper output is correct"); run['default'](view, 'set', 'controller.repeatCount', 5); equal(view.$().text(), 'YoYoYoYoYo', "helper output updated"); }); QUnit.test("bound helpers should support keywords", function() { registry.register('helper:x-capitalize', makeBoundHelper['default'](function(params, hash, options, env) { return params[0].toUpperCase(); })); view = EmberView['default'].create({ container: container, text: 'ab', template: compile['default']("{{x-capitalize view.text}}") }); utils.runAppend(view); equal(view.$().text(), 'AB', "helper output is correct"); }); QUnit.test("bound helpers should not process `fooBinding` style hash properties", function() { registry.register('helper:x-repeat', makeBoundHelper['default'](function(params, hash, options, env) { equal(hash.timesBinding, "numRepeats"); })); view = EmberView['default'].create({ container: container, controller: { text: 'ab', numRepeats: 3 }, template: compile['default']('{{x-repeat text timesBinding="numRepeats"}}') }); utils.runAppend(view); }); QUnit.test("bound helpers should support multiple bound properties", function() { registry.register('helper:x-combine', makeBoundHelper['default'](function(params, hash, options, env) { return params.join(''); })); view = EmberView['default'].create({ container: container, controller: { thing1: 'ZOID', thing2: 'BERG' }, template: compile['default']('{{x-combine thing1 thing2}}') }); utils.runAppend(view); equal(view.$().text(), 'ZOIDBERG', "helper output is correct"); run['default'](view, 'set', 'controller.thing2', "NERD"); equal(view.$().text(), 'ZOIDNERD', "helper correctly re-rendered after second bound helper property changed"); run['default'](function() { view.set('controller.thing1', 'WOOT'); view.set('controller.thing2', 'YEAH'); }); equal(view.$().text(), 'WOOTYEAH', "helper correctly re-rendered after both bound helper properties changed"); }); QUnit.test("bound helpers can be invoked with zero args", function() { registry.register('helper:x-troll', makeBoundHelper['default'](function(params, hash) { return hash.text || "TROLOLOL"; })); view = EmberView['default'].create({ container: container, controller: { trollText: "yumad" }, template: compile['default']('{{x-troll}} and {{x-troll text="bork"}}') }); utils.runAppend(view); equal(view.$().text(), 'TROLOLOL and bork', "helper output is correct"); }); QUnit.test("bound helpers should not be invoked with blocks", function() { registerRepeatHelper(); view = EmberView['default'].create({ container: container, controller: {}, template: compile['default']("{{#x-repeat}}Sorry, Charlie{{/x-repeat}}") }); expectAssertion(function() { utils.runAppend(view); }, /makeBoundHelper generated helpers do not support use with blocks/i); }); QUnit.test("shouldn't treat raw numbers as bound paths", function() { registry.register('helper:x-sum', makeBoundHelper['default'](function(params) { return params[0] + params[1]; })); view = EmberView['default'].create({ container: container, controller: { aNumber: 1 }, template: compile['default']("{{x-sum aNumber 1}} {{x-sum 0 aNumber}} {{x-sum 5 6}}") }); utils.runAppend(view); equal(view.$().text(), '2 1 11', "helper output is correct"); run['default'](view, 'set', 'controller.aNumber', 5); equal(view.$().text(), '6 5 11', "helper still updates as expected"); }); QUnit.test("should have correct argument types", function() { registry.register('helper:get-type', makeBoundHelper['default'](function(params) { return typeof params[0]; })); view = EmberView['default'].create({ container: container, controller: {}, template: compile['default']('{{get-type null}}, {{get-type undefProp}}, {{get-type "string"}}, {{get-type 1}}, {{get-type this}}') }); utils.runAppend(view); equal(view.$().text(), 'undefined, undefined, string, number, object', "helper output is correct"); }); QUnit.test("when no parameters are bound, no new views are created", function() { registerRepeatHelper(); var originalRender = SimpleBoundView['default'].prototype.render; var renderWasCalled = false; SimpleBoundView['default'].prototype.render = function() { renderWasCalled = true; return originalRender.apply(this, arguments); }; try { view = EmberView['default'].create({ template: compile['default']('{{x-repeat "a"}}'), controller: EmberObject['default'].create(), container: container }); utils.runAppend(view); } finally { SimpleBoundView['default'].prototype.render = originalRender; } ok(!renderWasCalled, 'simple bound view should not have been created and rendered'); equal(view.$().text(), 'a'); }); QUnit.test('when no hash parameters are bound, no new views are created', function() { registerRepeatHelper(); var originalRender = SimpleBoundView['default'].prototype.render; var renderWasCalled = false; SimpleBoundView['default'].prototype.render = function() { renderWasCalled = true; return originalRender.apply(this, arguments); }; try { view = EmberView['default'].create({ template: compile['default']('{{x-repeat "a" times=3}}'), controller: EmberObject['default'].create(), container: container }); utils.runAppend(view); } finally { SimpleBoundView['default'].prototype.render = originalRender; } ok(!renderWasCalled, 'simple bound view should not have been created and rendered'); equal(view.$().text(), 'aaa'); }); // jscs:enable validateIndentation }); enifed('ember-htmlbars/tests/system/make_bound_helper_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/make_bound_helper_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/system/make_bound_helper_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/system/make_bound_helper_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/make_bound_helper_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/system/make_bound_helper_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/system/make_view_helper_test', ['ember-htmlbars/system/make-view-helper'], function (makeViewHelper) { 'use strict'; QUnit.module("ember-htmlbars: makeViewHelper"); QUnit.test("makes helpful assertion when called with invalid arguments", function() { var viewClass = { toString: function() { return 'Some Random Class'; } }; var helper = makeViewHelper['default'](viewClass); expectAssertion(function() { helper.helperFunction(['foo'], {}, {}, {}); }, "You can only pass attributes (such as name=value) not bare values to a helper for a View found in 'Some Random Class'"); }); }); enifed('ember-htmlbars/tests/system/make_view_helper_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/make_view_helper_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/system/make_view_helper_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/system/make_view_helper_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/make_view_helper_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/system/make_view_helper_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/system/render_view_test', ['ember-runtime/tests/utils', 'ember-views/views/view', 'ember-htmlbars/env', 'ember-metal/keys'], function (utils, EmberView, defaultEnv, keys) { 'use strict'; var view; QUnit.module('ember-htmlbars: renderView', { teardown: function() { utils.runDestroy(view); } }); QUnit.test('default environment values are passed through', function() { var keyNames = keys['default'](defaultEnv['default']); expect(keyNames.length); view = EmberView['default'].create({ template: { isHTMLBars: true, render: function(view, env, contextualElement, blockArguments) { for (var i = 0, l = keyNames.length; i < l; i++) { var keyName = keyNames[i]; deepEqual(env[keyName], defaultEnv['default'][keyName], 'passes ' + keyName + ' from the default env'); } } } }); utils.runAppend(view); }); }); enifed('ember-htmlbars/tests/system/render_view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/render_view_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/system/render_view_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/system/render_view_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/system'); test('ember-htmlbars/tests/system/render_view_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/system/render_view_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/tests/utils/string_test', ['htmlbars-util/safe-string', 'ember-htmlbars/utils/string'], function (SafeString, string) { 'use strict'; QUnit.module('ember-htmlbars: SafeString'); QUnit.test("htmlSafe should return an instance of SafeString", function() { var safeString = string.htmlSafe("you need to be more bold"); ok(safeString instanceof SafeString['default'], "should return SafeString"); }); QUnit.test("htmlSafe should return an empty string for null", function() { equal(string.htmlSafe(null).toString(), "", "should return an empty string"); }); QUnit.test("htmlSafe should return an empty string for undefined", function() { equal(string.htmlSafe().toString(), "", "should return an empty string"); }); }); enifed('ember-htmlbars/tests/utils/string_test.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/tests/utils'); test('ember-htmlbars/tests/utils/string_test.js should pass jscs', function() { ok(true, 'ember-htmlbars/tests/utils/string_test.js should pass jscs.'); }); }); enifed('ember-htmlbars/tests/utils/string_test.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/tests/utils'); test('ember-htmlbars/tests/utils/string_test.js should pass jshint', function() { ok(true, 'ember-htmlbars/tests/utils/string_test.js should pass jshint.'); }); }); enifed('ember-htmlbars/utils/string.jscs-test', function () { 'use strict'; module('JSCS - ember-htmlbars/utils'); test('ember-htmlbars/utils/string.js should pass jscs', function() { ok(true, 'ember-htmlbars/utils/string.js should pass jscs.'); }); }); enifed('ember-htmlbars/utils/string.jshint', function () { 'use strict'; module('JSHint - ember-htmlbars/utils'); test('ember-htmlbars/utils/string.js should pass jshint', function() { ok(true, 'ember-htmlbars/utils/string.js should pass jshint.'); }); }); enifed('ember-metal-views.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-metal-views.js should pass jscs', function() { ok(true, 'ember-metal-views.js should pass jscs.'); }); }); enifed('ember-metal-views.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-metal-views.js should pass jshint', function() { ok(true, 'ember-metal-views.js should pass jshint.'); }); }); enifed('ember-metal-views/renderer.jscs-test', function () { 'use strict'; module('JSCS - ember-metal-views'); test('ember-metal-views/renderer.js should pass jscs', function() { ok(true, 'ember-metal-views/renderer.js should pass jscs.'); }); }); enifed('ember-metal-views/renderer.jshint', function () { 'use strict'; module('JSHint - ember-metal-views'); test('ember-metal-views/renderer.js should pass jshint', function() { ok(true, 'ember-metal-views/renderer.js should pass jshint.'); }); }); enifed('ember-metal-views/tests/attributes_test', ['ember-metal-views/tests/test_helpers'], function (test_helpers) { 'use strict'; test_helpers.testsFor("ember-metal-views - attributes"); QUnit.test('aliased attributeBindings', function() { var view = { isView: true, attributeBindings: ['isDisabled:disabled'], isDisabled: 'disabled' }; var el = test_helpers.appendTo(view); equal(el.getAttribute('disabled'), 'disabled', "The attribute alias was set"); test_helpers.subject().removeAndDestroy(view); }); }); enifed('ember-metal-views/tests/attributes_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal-views/tests'); test('ember-metal-views/tests/attributes_test.js should pass jscs', function() { ok(true, 'ember-metal-views/tests/attributes_test.js should pass jscs.'); }); }); enifed('ember-metal-views/tests/attributes_test.jshint', function () { 'use strict'; module('JSHint - ember-metal-views/tests'); test('ember-metal-views/tests/attributes_test.js should pass jshint', function() { ok(true, 'ember-metal-views/tests/attributes_test.js should pass jshint.'); }); }); enifed('ember-metal-views/tests/children_test', ['ember-metal-views/tests/test_helpers'], function (test_helpers) { 'use strict'; test_helpers.testsFor("ember-metal-views - children"); QUnit.test("a view can have child views", function() { var view = { isView: true, tagName: 'ul', childViews: [ { isView: true, tagName: 'li', textContent: 'ohai' } ] }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    • ohai
    "); }); QUnit.test("didInsertElement fires after children are rendered", function() { expect(2); var view = { isView: true, tagName: 'ul', childViews: [ { isView: true, tagName: 'li', textContent: 'ohai' } ], didInsertElement: function() { test_helpers.equalHTML(this.element, "
    • ohai
    ", "Children are rendered"); } }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    • ohai
    "); test_helpers.subject().removeAndDestroy(view); }); }); enifed('ember-metal-views/tests/children_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal-views/tests'); test('ember-metal-views/tests/children_test.js should pass jscs', function() { ok(true, 'ember-metal-views/tests/children_test.js should pass jscs.'); }); }); enifed('ember-metal-views/tests/children_test.jshint', function () { 'use strict'; module('JSHint - ember-metal-views/tests'); test('ember-metal-views/tests/children_test.js should pass jshint', function() { ok(true, 'ember-metal-views/tests/children_test.js should pass jshint.'); }); }); enifed('ember-metal-views/tests/main_test', ['ember-metal-views/tests/test_helpers'], function (test_helpers) { 'use strict'; var view; test_helpers.testsFor("ember-metal-views", { teardown: function(renderer) { if (view) { renderer.removeAndDestroy(view); } view = null; } }); // Test the behavior of the helper createElement stub QUnit.test("by default, view renders as a div", function() { view = { isView: true }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    "); }); // Test the behavior of the helper createElement stub QUnit.test("tagName can be specified", function() { view = { isView: true, tagName: 'span' }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', ""); }); // Test the behavior of the helper createElement stub QUnit.test("textContent can be specified", function() { view = { isView: true, textContent: 'ohai derp' }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    ohai <a>derp</a>
    "); }); // Test the behavior of the helper createElement stub QUnit.test("innerHTML can be specified", function() { view = { isView: true, innerHTML: 'ohai derp' }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    ohai derp
    "); }); // Test the behavior of the helper createElement stub QUnit.test("innerHTML tr can be specified", function() { view = { isView: true, tagName: 'table', innerHTML: 'ohai' }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    ohai
    "); }); // Test the behavior of the helper createElement stub QUnit.test("element can be specified", function() { view = { isView: true, element: document.createElement('i') }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', ""); }); QUnit.test("willInsertElement hook", function() { expect(3); view = { isView: true, willInsertElement: function(el) { ok(this.element && this.element.nodeType === 1, "We have an element"); equal(this.element.parentElement, null, "The element is parentless"); test_helpers.setElementText(this.element, 'you gone and done inserted that element'); } }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    you gone and done inserted that element
    "); }); QUnit.test("didInsertElement hook", function() { expect(3); view = { isView: true, didInsertElement: function() { ok(this.element && this.element.nodeType === 1, "We have an element"); equal(this.element.parentElement, document.getElementById('qunit-fixture'), "The element's parent is correct"); test_helpers.setElementText(this.element, 'you gone and done inserted that element'); } }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', "
    you gone and done inserted that element
    "); }); QUnit.test("classNames - array", function() { view = { isView: true, classNames: ['foo', 'bar'], textContent: 'ohai' }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', '
    ohai
    '); }); QUnit.test("classNames - string", function() { view = { isView: true, classNames: 'foo bar', textContent: 'ohai' }; test_helpers.appendTo(view); test_helpers.equalHTML('qunit-fixture', '
    ohai
    '); }); }); enifed('ember-metal-views/tests/main_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal-views/tests'); test('ember-metal-views/tests/main_test.js should pass jscs', function() { ok(true, 'ember-metal-views/tests/main_test.js should pass jscs.'); }); }); enifed('ember-metal-views/tests/main_test.jshint', function () { 'use strict'; module('JSHint - ember-metal-views/tests'); test('ember-metal-views/tests/main_test.js should pass jshint', function() { ok(true, 'ember-metal-views/tests/main_test.js should pass jshint.'); }); }); enifed('ember-metal-views/tests/test_helpers', ['exports', 'ember-metal/platform/create', 'ember-metal-views'], function (exports, create, ember_metal_views) { 'use strict'; exports.testsFor = testsFor; exports.subject = subject; exports.equalHTML = equalHTML; exports.appendTo = appendTo; var renderer; function MetalRenderer() { MetalRenderer._super.call(this); } MetalRenderer._super = ember_metal_views.Renderer; MetalRenderer.prototype = create['default'](ember_metal_views.Renderer.prototype, { constructor: { value: MetalRenderer, enumerable: false, writable: true, configurable: true } }); MetalRenderer.prototype.childViews = function (view) { return view.childViews; }; MetalRenderer.prototype.willCreateElement = function(view) { }; MetalRenderer.prototype.createElement = function (view, contextualElement) { var el; if (view.element) { el = view.element; } else { el = view.element = this._dom.createElement(view.tagName || 'div', contextualElement); } var classNames = view.classNames; if (typeof classNames === 'string') { el.setAttribute('class', classNames); } else if (classNames && classNames.length) { if (classNames.length === 1) { // PERF: avoid join'ing unnecessarily el.setAttribute('class', classNames[0]); } else { el.setAttribute('class', classNames.join(' ')); // TODO: faster way to do this? } } var attributeBindings = view.attributeBindings; if (attributeBindings && attributeBindings.length) { for (var i=0,l=attributeBindings.length; i obj.foo', function() { deepEqual(property_get.get(obj, 'foo'), obj.foo); }); QUnit.test('[obj, foo.bar] -> obj.foo.bar', function() { deepEqual(property_get.get(obj, 'foo.bar'), obj.foo.bar); }); QUnit.test('[obj, foothis.bar] -> obj.foothis.bar', function() { deepEqual(property_get.get(obj, 'foothis.bar'), obj.foothis.bar); }); QUnit.test('[obj, this.foo] -> obj.foo', function() { deepEqual(property_get.get(obj, 'this.foo'), obj.foo); }); QUnit.test('[obj, this.foo.bar] -> obj.foo.bar', function() { deepEqual(property_get.get(obj, 'this.foo.bar'), obj.foo.bar); }); QUnit.test('[obj, this.Foo.bar] -> (null)', function() { deepEqual(property_get.get(obj, 'this.Foo.bar'), undefined); }); QUnit.test('[obj, falseValue.notDefined] -> (null)', function() { deepEqual(property_get.get(obj, 'falseValue.notDefined'), undefined); }); // .......................................................... // LOCAL PATHS WITH NO TARGET DEPRECATION // QUnit.test('[null, length] returning data is deprecated', function() { expectGlobalContextDeprecation(function() { equal(5, property_get.get(null, 'localPathGlobal')); }); }); QUnit.test('[length] returning data is deprecated', function() { expectGlobalContextDeprecation(function() { equal(5, property_get.get('localPathGlobal')); }); }); // .......................................................... // NO TARGET // QUnit.test('[null, Foo] -> Foo', function() { deepEqual(property_get.get('Foo'), Foo); }); QUnit.test('[null, Foo.bar] -> Foo.bar', function() { deepEqual(property_get.get('Foo.bar'), Foo.bar); }); }); enifed('ember-metal/tests/accessors/get_path_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/get_path_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/get_path_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/get_path_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/get_path_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/get_path_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/get_properties_test', ['ember-metal/get_properties'], function (getProperties) { 'use strict'; QUnit.module('Ember.getProperties'); QUnit.test('can retrieve a hash of properties from an object via an argument list or array of property names', function() { var obj = { firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }; deepEqual(getProperties['default'](obj, "firstName", "lastName"), { firstName: 'Steve', lastName: 'Jobs' }); deepEqual(getProperties['default'](obj, "firstName", "lastName"), { firstName: 'Steve', lastName: 'Jobs' }); deepEqual(getProperties['default'](obj, "lastName"), { lastName: 'Jobs' }); deepEqual(getProperties['default'](obj), {}); deepEqual(getProperties['default'](obj, ["firstName", "lastName"]), { firstName: 'Steve', lastName: 'Jobs' }); deepEqual(getProperties['default'](obj, ["firstName"]), { firstName: 'Steve' }); deepEqual(getProperties['default'](obj, []), {}); }); }); enifed('ember-metal/tests/accessors/get_properties_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/get_properties_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/get_properties_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/get_properties_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/get_properties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/get_properties_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/get_test', ['ember-metal/tests/props_helper', 'ember-metal/property_get', 'ember-metal/mixin', 'ember-metal/observer', 'ember-metal/platform/create'], function (props_helper, property_get, mixin, observer, create) { 'use strict'; QUnit.module('Ember.get'); QUnit.test('should get arbitrary properties on an object', function() { var obj = { string: 'string', number: 23, boolTrue: true, boolFalse: false, nullValue: null }; for (var key in obj) { if (!obj.hasOwnProperty(key)) { continue; } equal(property_get.get(obj, key), obj[key], key); } }); props_helper.testBoth("should call unknownProperty on watched values if the value is undefined", function(get, set) { var obj = { count: 0, unknownProperty: function(key) { equal(key, 'foo', "should pass key"); this.count++; return "FOO"; } }; var count = 0; observer.addObserver(obj, 'foo', function() { count++; }); equal(get(obj, 'foo'), 'FOO', 'should return value from unknown'); }); QUnit.test('warn on attempts to get a property of undefined', function() { expectAssertion(function() { property_get.get(undefined, 'aProperty'); }, /Cannot call get with 'aProperty' on an undefined object/i); }); QUnit.test('warn on attempts to get a property path of undefined', function() { expectAssertion(function() { property_get.get(undefined, 'aProperty.on.aPath'); }, /Cannot call get with 'aProperty.on.aPath' on an undefined object/); }); QUnit.test('warn on attempts to get a falsy property', function() { var obj = {}; expectAssertion(function() { property_get.get(obj, null); }, /Cannot call get with null key/); expectAssertion(function() { property_get.get(obj, NaN); }, /Cannot call get with NaN key/); expectAssertion(function() { property_get.get(obj, undefined); }, /Cannot call get with undefined key/); expectAssertion(function() { property_get.get(obj, false); }, /Cannot call get with false key/); }); // .......................................................... // BUGS // QUnit.test('(regression) watched properties on unmodified inherited objects should still return their original value', function() { var MyMixin = mixin.Mixin.create({ someProperty: 'foo', propertyDidChange: mixin.observer('someProperty', function() { // NOTHING TO DO }) }); var baseObject = MyMixin.apply({}); var theRealObject = create['default'](baseObject); equal(property_get.get(theRealObject, 'someProperty'), 'foo', 'should return the set value, not false'); }); QUnit.module("Ember.getWithDefault"); QUnit.test('should get arbitrary properties on an object', function() { var obj = { string: 'string', number: 23, boolTrue: true, boolFalse: false, nullValue: null }; for (var key in obj) { if (!obj.hasOwnProperty(key)) { continue; } equal(property_get.getWithDefault(obj, key, "fail"), obj[key], key); } obj = { undef: undefined }; equal(property_get.getWithDefault(obj, "undef", "default"), "default", "explicit undefined retrieves the default"); equal(property_get.getWithDefault(obj, "not-present", "default"), "default", "non-present key retrieves the default"); }); QUnit.test('should call unknownProperty if defined and value is undefined', function() { var obj = { count: 0, unknownProperty: function(key) { equal(key, 'foo', 'should pass key'); this.count++; return 'FOO'; } }; equal(property_get.get(obj, 'foo'), 'FOO', 'should return value from unknown'); equal(obj.count, 1, 'should have invoked'); }); props_helper.testBoth("if unknownProperty is present, it is called", function(get, set) { var obj = { count: 0, unknownProperty: function(key) { if (key === "foo") { equal(key, 'foo', "should pass key"); this.count++; return "FOO"; } } }; var count = 0; observer.addObserver(obj, 'foo', function() { count++; }); equal(property_get.getWithDefault(obj, 'foo', "fail"), 'FOO', 'should return value from unknownProperty'); equal(property_get.getWithDefault(obj, 'bar', "default"), 'default', 'should convert undefined from unknownProperty into default'); }); // .......................................................... // BUGS // QUnit.test('(regression) watched properties on unmodified inherited objects should still return their original value', function() { var MyMixin = mixin.Mixin.create({ someProperty: 'foo', propertyDidChange: mixin.observer('someProperty', function() { // NOTHING TO DO }) }); var baseObject = MyMixin.apply({}); var theRealObject = create['default'](baseObject); equal(property_get.getWithDefault(theRealObject, 'someProperty', "fail"), 'foo', 'should return the set value, not false'); }); }); enifed('ember-metal/tests/accessors/get_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/get_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/get_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/get_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/get_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/get_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/is_global_path_test', ['ember-metal/binding'], function (binding) { 'use strict'; QUnit.module('Ember.isGlobalPath'); QUnit.test("global path's are recognized", function() { ok(binding.isGlobalPath('App.myProperty')); ok(binding.isGlobalPath('App.myProperty.subProperty')); }); QUnit.test("if there is a 'this' in the path, it's not a global path", function() { ok(!binding.isGlobalPath('this.myProperty')); ok(!binding.isGlobalPath('this')); }); QUnit.test("if the path starts with a lowercase character, it is not a global path", function() { ok(!binding.isGlobalPath('myObj')); ok(!binding.isGlobalPath('myObj.SecondProperty')); }); }); enifed('ember-metal/tests/accessors/is_global_path_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/is_global_path_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/is_global_path_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/is_global_path_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/is_global_path_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/is_global_path_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/mandatory_setters_test', ['ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/watching', 'ember-metal/platform/define_property', 'ember-metal/platform/create', 'ember-metal/utils'], function (property_get, property_set, watching, define_property, create, utils) { 'use strict'; QUnit.module('mandatory-setters'); function hasMandatorySetter(object, property) { var meta = utils.meta(object); return property in meta.values; } QUnit.test('does not assert', function() { var obj = { someProp: null, toString: function() { return 'custom-object'; } }; obj.someProp = 'blastix'; equal(property_get.get(obj, 'someProp'), 'blastix'); watching.watch(obj, 'someProp'); obj.someProp = 'foo-bar'; equal(property_get.get(obj, 'someProp'), 'foo-bar'); obj.someProp = 'bernie'; equal(property_get.get(obj, 'someProp'), 'bernie'); }); }); enifed('ember-metal/tests/accessors/mandatory_setters_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/mandatory_setters_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/mandatory_setters_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/mandatory_setters_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/mandatory_setters_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/mandatory_setters_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/normalize_tuple_test', ['ember-metal/property_get'], function (property_get) { 'use strict'; /*globals Foo:true, $foo:true */ var obj; var moduleOpts = { setup: function() { obj = { foo: { bar: { baz: {} } } }; window.Foo = { bar: { baz: {} } }; window.$foo = { bar: { baz: {} } }; }, teardown: function() { obj = undefined; window.Foo = undefined; window.$foo = undefined; } }; QUnit.module('normalizeTuple', moduleOpts); // .......................................................... // LOCAL PATHS // QUnit.test('[obj, foo] -> [obj, foo]', function() { deepEqual(property_get.normalizeTuple(obj, 'foo'), [obj, 'foo']); }); QUnit.test('[obj, *] -> [obj, *]', function() { deepEqual(property_get.normalizeTuple(obj, '*'), [obj, '*']); }); QUnit.test('[obj, foo.bar] -> [obj, foo.bar]', function() { deepEqual(property_get.normalizeTuple(obj, 'foo.bar'), [obj, 'foo.bar']); }); QUnit.test('[obj, foo.*] -> [obj, foo.*]', function() { deepEqual(property_get.normalizeTuple(obj, 'foo.*'), [obj, 'foo.*']); }); QUnit.test('[obj, foo.*.baz] -> [obj, foo.*.baz]', function() { deepEqual(property_get.normalizeTuple(obj, 'foo.*.baz'), [obj, 'foo.*.baz']); }); QUnit.test('[obj, this.foo] -> [obj, foo]', function() { deepEqual(property_get.normalizeTuple(obj, 'this.foo'), [obj, 'foo']); }); QUnit.test('[obj, this.foo.bar] -> [obj, foo.bar]', function() { deepEqual(property_get.normalizeTuple(obj, 'this.foo.bar'), [obj, 'foo.bar']); }); QUnit.test('[obj, this.Foo.bar] -> [obj, Foo.bar]', function() { deepEqual(property_get.normalizeTuple(obj, 'this.Foo.bar'), [obj, 'Foo.bar']); }); // .......................................................... // GLOBAL PATHS // QUnit.test('[obj, Foo] -> [obj, Foo]', function() { expectDeprecation(function() { deepEqual(property_get.normalizeTuple(obj, 'Foo'), [obj, 'Foo']); }, "normalizeTuple will return 'Foo' as a non-global. This behavior will change in the future (issue #3852)"); }); QUnit.test('[obj, Foo.bar] -> [Foo, bar]', function() { deepEqual(property_get.normalizeTuple(obj, 'Foo.bar'), [Foo, 'bar']); }); QUnit.test('[obj, $foo.bar.baz] -> [$foo, bar.baz]', function() { deepEqual(property_get.normalizeTuple(obj, '$foo.bar.baz'), [$foo, 'bar.baz']); }); // .......................................................... // NO TARGET // QUnit.test('[null, Foo] -> EXCEPTION', function() { throws(function() { property_get.normalizeTuple(null, 'Foo'); }, Error); }); QUnit.test('[null, Foo.bar] -> [Foo, bar]', function() { deepEqual(property_get.normalizeTuple(null, 'Foo.bar'), [Foo, 'bar']); }); }); enifed('ember-metal/tests/accessors/normalize_tuple_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/normalize_tuple_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/normalize_tuple_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/normalize_tuple_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/normalize_tuple_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/normalize_tuple_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/set_path_test', ['ember-metal/property_set', 'ember-metal/property_get'], function (property_set, property_get) { 'use strict'; var originalLookup = Ember.lookup; var obj; function commonSetup() { obj = { foo: { bar: { baz: { biff: 'BIFF' } } } }; Ember.lookup = { Foo: { bar: { baz: { biff: 'FooBiff' } } }, $foo: { bar: { baz: { biff: '$FOOBIFF' } } } }; } function commonTeardown() { obj = null; Ember.lookup = originalLookup; } QUnit.module('set with path', { setup: commonSetup, teardown: commonTeardown }); QUnit.test('[Foo, bar] -> Foo.bar', function() { Ember.lookup.Foo = { toString: function() { return 'Foo'; } }; // Behave like an Ember.Namespace property_set.set(Ember.lookup.Foo, 'bar', 'baz'); equal(property_get.get(Ember.lookup.Foo, 'bar'), 'baz'); }); // .......................................................... // // LOCAL PATHS QUnit.test('[obj, foo] -> obj.foo', function() { property_set.set(obj, 'foo', "BAM"); equal(property_get.get(obj, 'foo'), "BAM"); }); QUnit.test('[obj, foo.bar] -> obj.foo.bar', function() { property_set.set(obj, 'foo.bar', "BAM"); equal(property_get.get(obj, 'foo.bar'), "BAM"); }); QUnit.test('[obj, this.foo] -> obj.foo', function() { property_set.set(obj, 'this.foo', "BAM"); equal(property_get.get(obj, 'foo'), "BAM"); }); QUnit.test('[obj, this.foo.bar] -> obj.foo.bar', function() { property_set.set(obj, 'this.foo.bar', "BAM"); equal(property_get.get(obj, 'foo.bar'), "BAM"); }); // .......................................................... // NO TARGET // QUnit.test('[null, Foo.bar] -> Foo.bar', function() { property_set.set(null, 'Foo.bar', "BAM"); equal(property_get.get(Ember.lookup.Foo, 'bar'), "BAM"); }); // .......................................................... // DEPRECATED // QUnit.module("set with path - deprecated", { setup: commonSetup, teardown: commonTeardown }); QUnit.test('[null, bla] gives a proper exception message', function() { var exceptionMessage = 'Property set failed: object in path \"bla\" could not be found or was destroyed.'; try { property_set.set(null, 'bla', "BAM"); } catch(ex) { equal(ex.message, exceptionMessage); } }); QUnit.test('[obj, bla.bla] gives a proper exception message', function() { var exceptionMessage = 'Property set failed: object in path \"bla\" could not be found or was destroyed.'; try { property_set.set(obj, 'bla.bla', "BAM"); } catch(ex) { equal(ex.message, exceptionMessage); } }); QUnit.test('[obj, foo.baz.bat] -> EXCEPTION', function() { throws(function() { property_set.set(obj, 'foo.baz.bat', "BAM"); }, Error); }); QUnit.test('[obj, foo.baz.bat] -> EXCEPTION', function() { property_set.trySet(obj, 'foo.baz.bat', "BAM"); ok(true, "does not raise"); }); }); enifed('ember-metal/tests/accessors/set_path_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/set_path_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/set_path_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/set_path_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/set_path_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/set_path_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/accessors/set_test', ['ember-metal/property_get', 'ember-metal/property_set'], function (property_get, property_set) { 'use strict'; QUnit.module('set'); QUnit.test('should set arbitrary properties on an object', function() { var obj = { string: 'string', number: 23, boolTrue: true, boolFalse: false, nullValue: null, undefinedValue: undefined }; var newObj = { undefinedValue: 'emberjs' }; for (var key in obj) { if (!obj.hasOwnProperty(key)) { continue; } equal(property_set.set(newObj, key, obj[key]), obj[key], 'should return value'); equal(property_get.get(newObj, key), obj[key], 'should set value'); } }); QUnit.test('should call setUnknownProperty if defined and value is undefined', function() { var obj = { count: 0, unknownProperty: function(key, value) { ok(false, 'should not invoke unknownProperty if setUnknownProperty is defined'); }, setUnknownProperty: function(key, value) { equal(key, 'foo', 'should pass key'); equal(value, 'BAR', 'should pass key'); this.count++; return 'FOO'; } }; equal(property_set.set(obj, 'foo', "BAR"), 'BAR', 'should return set value'); equal(obj.count, 1, 'should have invoked'); }); }); enifed('ember-metal/tests/accessors/set_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/set_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/accessors/set_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/accessors/set_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/set_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/set_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/alias_test', ['ember-metal/alias', 'ember-metal/properties', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/watching', 'ember-metal/observer'], function (alias, properties, property_get, property_set, utils, watching, observer) { 'use strict'; var obj, count; QUnit.module('ember-metal/alias', { setup: function() { obj = { foo: { faz: 'FOO' } }; count = 0; }, teardown: function() { obj = null; } }); function incrementCount() { count++; } QUnit.test('should proxy get to alt key', function() { properties.defineProperty(obj, 'bar', alias['default']('foo.faz')); equal(property_get.get(obj, 'bar'), 'FOO'); }); QUnit.test('should proxy set to alt key', function() { properties.defineProperty(obj, 'bar', alias['default']('foo.faz')); property_set.set(obj, 'bar', 'BAR'); equal(property_get.get(obj, 'foo.faz'), 'BAR'); }); QUnit.test('basic lifecycle', function() { properties.defineProperty(obj, 'bar', alias['default']('foo.faz')); var m = utils.meta(obj); observer.addObserver(obj, 'bar', incrementCount); equal(m.deps['foo.faz'].bar, 1); observer.removeObserver(obj, 'bar', incrementCount); equal(m.deps['foo.faz'].bar, 0); }); QUnit.test('begins watching alt key as soon as alias is watched', function() { properties.defineProperty(obj, 'bar', alias['default']('foo.faz')); observer.addObserver(obj, 'bar', incrementCount); ok(watching.isWatching(obj, 'foo.faz')); property_set.set(obj, 'foo.faz', 'BAR'); equal(count, 1); }); QUnit.test('immediately sets up dependencies if already being watched', function() { observer.addObserver(obj, 'bar', incrementCount); properties.defineProperty(obj, 'bar', alias['default']('foo.faz')); ok(watching.isWatching(obj, 'foo.faz')); property_set.set(obj, 'foo.faz', 'BAR'); equal(count, 1); }); QUnit.test('setting alias on self should fail assertion', function() { expectAssertion(function() { properties.defineProperty(obj, 'bar', alias['default']('bar')); }, "Setting alias 'bar' on self"); }); }); enifed('ember-metal/tests/alias_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/alias_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/alias_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/alias_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/alias_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/alias_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/binding/connect_test', ['ember-metal/core', 'ember-metal/tests/props_helper', 'ember-metal/binding', 'ember-metal/run_loop', 'ember-metal/property_set', 'ember-metal/property_get'], function (Ember, props_helper, ember_metal__binding, run, property_set, property_get) { 'use strict'; function performTest(binding, a, b, get, set, connect) { if (connect === undefined) { connect = function() {binding.connect(a);}; } ok(!run['default'].currentRunLoop, 'performTest should not have a currentRunLoop'); equal(get(a, 'foo'), 'FOO', 'a should not have changed'); equal(get(b, 'bar'), 'BAR', 'b should not have changed'); connect(); equal(get(a, 'foo'), 'BAR', 'a should have changed'); equal(get(b, 'bar'), 'BAR', 'b should have changed'); // // make sure changes sync both ways run['default'](function () { set(b, 'bar', 'BAZZ'); }); equal(get(a, 'foo'), 'BAZZ', 'a should have changed'); run['default'](function () { set(a, 'foo', 'BARF'); }); equal(get(b, 'bar'), 'BARF', 'a should have changed'); } var originalLookup, lookup, GlobalB; QUnit.module("Ember.Binding", { setup: function() { originalLookup = Ember['default'].lookup; Ember['default'].lookup = lookup = {}; }, teardown: function() { lookup = null; Ember['default'].lookup = originalLookup; } }); props_helper.testBoth('Connecting a binding between two properties', function(get, set) { var a = { foo: 'FOO', bar: 'BAR' }; // a.bar -> a.foo var binding = new ember_metal__binding.Binding('foo', 'bar'); performTest(binding, a, a, get, set); }); props_helper.testBoth('Connecting a binding between two objects', function(get, set) { var b = { bar: 'BAR' }; var a = { foo: 'FOO', b: b }; // b.bar -> a.foo var binding = new ember_metal__binding.Binding('foo', 'b.bar'); performTest(binding, a, b, get, set); }); props_helper.testBoth('Connecting a binding to path', function(get, set) { var a = { foo: 'FOO' }; lookup['GlobalB'] = GlobalB = { b: { bar: 'BAR' } }; var b = get(GlobalB, 'b'); // globalB.b.bar -> a.foo var binding = new ember_metal__binding.Binding('foo', 'GlobalB.b.bar'); performTest(binding, a, b, get, set); // make sure modifications update b = { bar: 'BIFF' }; run['default'](function() { set(GlobalB, 'b', b); }); equal(get(a, 'foo'), 'BIFF', 'a should have changed'); }); props_helper.testBoth('Calling connect more than once', function(get, set) { var b = { bar: 'BAR' }; var a = { foo: 'FOO', b: b }; // b.bar -> a.foo var binding = new ember_metal__binding.Binding('foo', 'b.bar'); performTest(binding, a, b, get, set, function () { binding.connect(a); binding.connect(a); }); }); QUnit.test('inherited bindings should sync on create', function() { var a; run['default'](function () { var A = function() { ember_metal__binding.bind(this, 'foo', 'bar.baz'); }; a = new A(); property_set.set(a, 'bar', { baz: 'BAZ' }); }); equal(property_get.get(a, 'foo'), 'BAZ', 'should have synced binding on new obj'); }); }); enifed('ember-metal/tests/binding/connect_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/binding'); test('ember-metal/tests/binding/connect_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/binding/connect_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/binding/connect_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/binding'); test('ember-metal/tests/binding/connect_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/binding/connect_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/binding/one_way_test', ['ember-metal/property_set', 'ember-metal/property_get', 'ember-metal/run_loop', 'ember-metal/binding'], function (property_set, property_get, run, ember_metal__binding) { 'use strict'; var MyApp; QUnit.module('system/mixin/binding/oneWay_test', { setup: function() { MyApp = { foo: { value: 'FOO' }, bar: { value: 'BAR' } }; }, teardown: function() { MyApp = null; } }); QUnit.test('oneWay(true) should only sync one way', function() { var binding; run['default'](function() { binding = ember_metal__binding.oneWay(MyApp, 'bar.value', 'foo.value'); }); equal(property_get.get(MyApp, 'foo.value'), 'FOO', 'foo synced'); equal(property_get.get(MyApp, 'bar.value'), 'FOO', 'bar synced'); run['default'](function() { property_set.set(MyApp, 'bar.value', 'BAZ'); }); equal(property_get.get(MyApp, 'foo.value'), 'FOO', 'foo synced'); equal(property_get.get(MyApp, 'bar.value'), 'BAZ', 'bar not synced'); run['default'](function() { property_set.set(MyApp, 'foo.value', 'BIFF'); }); equal(property_get.get(MyApp, 'foo.value'), 'BIFF', 'foo synced'); equal(property_get.get(MyApp, 'bar.value'), 'BIFF', 'foo synced'); }); }); enifed('ember-metal/tests/binding/one_way_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/binding'); test('ember-metal/tests/binding/one_way_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/binding/one_way_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/binding/one_way_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/binding'); test('ember-metal/tests/binding/one_way_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/binding/one_way_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/binding/sync_test', ['ember-metal/tests/props_helper', 'ember-metal/run_loop', 'ember-metal/observer', 'ember-metal/binding', 'ember-metal/computed', 'ember-metal/properties'], function (props_helper, run, observer, binding, computed, properties) { 'use strict'; QUnit.module("system/binding/sync_test.js"); props_helper.testBoth("bindings should not sync twice in a single run loop", function(get, set) { var a, b, setValue; var setCalled=0; var getCalled=0; run['default'](function() { a = {}; properties.defineProperty(a, 'foo', computed.computed(function(key, value) { if (arguments.length === 2) { setCalled++; setValue = value; return value; } else { getCalled++; return setValue; } })["volatile"]()); b = { a: a }; binding.bind(b, 'foo', 'a.foo'); }); // reset after initial binding synchronization getCalled = 0; run['default'](function() { set(a, 'foo', 'trollface'); }); equal(get(b, 'foo'), "trollface", "the binding should sync"); equal(setCalled, 1, "Set should only be called once"); equal(getCalled, 1, "Get should only be called once"); }); props_helper.testBoth("bindings should not infinite loop if computed properties return objects", function(get, set) { var a, b; var getCalled=0; run['default'](function() { a = {}; properties.defineProperty(a, 'foo', computed.computed(function() { getCalled++; if (getCalled > 1000) { throw 'infinite loop detected'; } return ['foo', 'bar']; })); b = { a: a }; binding.bind(b, 'foo', 'a.foo'); }); deepEqual(get(b, 'foo'), ['foo', 'bar'], "the binding should sync"); equal(getCalled, 1, "Get should only be called once"); }); props_helper.testBoth("bindings should do the right thing when observers trigger bindings in the opposite direction", function(get, set) { var a, b, c; run['default'](function() { a = { foo: 'trololol' }; b = { a: a }; binding.bind(b, 'foo', 'a.foo'); c = { a: a }; binding.bind(c, 'foo', 'a.foo'); }); observer.addObserver(b, 'foo', function() { set(c, 'foo', "what is going on"); }); run['default'](function() { set(a, 'foo', 'trollface'); }); equal(get(a, 'foo'), "what is going on"); }); props_helper.testBoth("bindings should not try to sync destroyed objects", function(get, set) { var a, b; run['default'](function() { a = { foo: 'trololol' }; b = { a: a }; binding.bind(b, 'foo', 'a.foo'); }); run['default'](function() { set(a, 'foo', 'trollface'); set(b, 'isDestroyed', true); ok(true, "should not raise"); }); run['default'](function() { a = { foo: 'trololol' }; b = { a: a }; binding.bind(b, 'foo', 'a.foo'); }); run['default'](function() { set(b, 'foo', 'trollface'); set(a, 'isDestroyed', true); ok(true, "should not raise"); }); }); }); enifed('ember-metal/tests/binding/sync_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/binding'); test('ember-metal/tests/binding/sync_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/binding/sync_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/binding/sync_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/binding'); test('ember-metal/tests/binding/sync_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/binding/sync_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/cache_test', ['ember-metal/cache'], function (Cache) { 'use strict'; QUnit.module("Cache"); QUnit.test("basic", function() { var cache = new Cache['default'](100, function(key) { return key.toUpperCase(); }); equal(cache.get("foo"), "FOO"); equal(cache.get("bar"), "BAR"); equal(cache.get("foo"), "FOO"); }); QUnit.test("caches computation correctly", function() { var count = 0; var cache = new Cache['default'](100, function(key) { count++; return key.toUpperCase(); }); equal(count, 0); cache.get("foo"); equal(count, 1); cache.get("bar"); equal(count, 2); cache.get("bar"); equal(count, 2); cache.get("foo"); equal(count, 2); }); QUnit.test("handles undefined value correctly", function() { var cache = new Cache['default'](100, function(key) {}); equal(cache.get("foo"), undefined); }); QUnit.test("continues working after reaching cache limit", function() { var cache = new Cache['default'](3, function(key) { return key.toUpperCase(); }); cache.get("a"); cache.get("b"); cache.get("c"); equal(cache.get("d"), "D"); equal(cache.get("a"), "A"); equal(cache.get("b"), "B"); equal(cache.get("c"), "C"); }); }); enifed('ember-metal/tests/cache_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/cache_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/cache_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/cache_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/cache_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/cache_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/chains_test', ['ember-metal/observer', 'ember-metal/chains', 'ember-metal/platform/create'], function (observer, chains, create) { 'use strict'; QUnit.module("Chains"); QUnit.test("finishChains should properly copy chains from prototypes to instances", function() { function didChange() {} var obj = {}; observer.addObserver(obj, 'foo.bar', null, didChange); var childObj = create['default'](obj); chains.finishChains(childObj); ok(obj['__ember_meta__'].chains !== childObj['__ember_meta__'].chains, "The chains object is copied"); }); }); enifed('ember-metal/tests/chains_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/chains_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/chains_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/chains_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/chains_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/chains_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/computed_test', ['ember-metal/core', 'ember-metal/tests/props_helper', 'ember-metal/platform/create', 'ember-metal/computed', 'ember-metal/computed_macros', 'ember-metal/alias', 'ember-metal/properties', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/watching', 'ember-metal/observer', 'ember-metal/enumerable_utils'], function (Ember, props_helper, create, computed, computed_macros, alias, properties, property_get, property_set, watching, observer, enumerable_utils) { 'use strict'; var originalLookup = Ember['default'].lookup; var obj, count, Global, lookup; QUnit.module('computed'); QUnit.test('computed property should be an instance of descriptor', function() { ok(computed.computed(function() {}) instanceof properties.Descriptor); }); QUnit.test('defining computed property should invoke property on get', function() { var obj = {}; var count = 0; properties.defineProperty(obj, 'foo', computed.computed(function(key) { count++; return 'computed '+key; })); equal(property_get.get(obj, 'foo'), 'computed foo', 'should return value'); equal(count, 1, 'should have invoked computed property'); }); QUnit.test('defining computed property should invoke property on set', function() { var obj = {}; var count = 0; properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { if (value !== undefined) { count++; this['__'+key] = 'computed '+value; } return this['__'+key]; })); equal(property_set.set(obj, 'foo', 'bar'), 'bar', 'should return set value'); equal(count, 1, 'should have invoked computed property'); equal(property_get.get(obj, 'foo'), 'computed bar', 'should return new value'); }); var objA, objB; QUnit.module('computed should inherit through prototype', { setup: function() { objA = { __foo: 'FOO' }; properties.defineProperty(objA, 'foo', computed.computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'computed '+value; } return this['__'+key]; })); objB = create['default'](objA); objB.__foo = 'FOO'; // make a copy; }, teardown: function() { objA = objB = null; } }); props_helper.testBoth('using get() and set()', function(get, set) { equal(get(objA, 'foo'), 'FOO', 'should get FOO from A'); equal(get(objB, 'foo'), 'FOO', 'should get FOO from B'); set(objA, 'foo', 'BIFF'); equal(get(objA, 'foo'), 'computed BIFF', 'should change A'); equal(get(objB, 'foo'), 'FOO', 'should NOT change B'); set(objB, 'foo', 'bar'); equal(get(objB, 'foo'), 'computed bar', 'should change B'); equal(get(objA, 'foo'), 'computed BIFF', 'should NOT change A'); set(objA, 'foo', 'BAZ'); equal(get(objA, 'foo'), 'computed BAZ', 'should change A'); equal(get(objB, 'foo'), 'computed bar', 'should NOT change B'); }); QUnit.module('redefining computed property to normal', { setup: function() { objA = { __foo: 'FOO' }; properties.defineProperty(objA, 'foo', computed.computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'computed '+value; } return this['__'+key]; })); objB = create['default'](objA); properties.defineProperty(objB, 'foo'); // make this just a normal property. }, teardown: function() { objA = objB = null; } }); props_helper.testBoth('using get() and set()', function(get, set) { equal(get(objA, 'foo'), 'FOO', 'should get FOO from A'); equal(get(objB, 'foo'), undefined, 'should get undefined from B'); set(objA, 'foo', 'BIFF'); equal(get(objA, 'foo'), 'computed BIFF', 'should change A'); equal(get(objB, 'foo'), undefined, 'should NOT change B'); set(objB, 'foo', 'bar'); equal(get(objB, 'foo'), 'bar', 'should change B'); equal(get(objA, 'foo'), 'computed BIFF', 'should NOT change A'); set(objA, 'foo', 'BAZ'); equal(get(objA, 'foo'), 'computed BAZ', 'should change A'); equal(get(objB, 'foo'), 'bar', 'should NOT change B'); }); QUnit.module('redefining computed property to another property', { setup: function() { objA = { __foo: 'FOO' }; properties.defineProperty(objA, 'foo', computed.computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'A '+value; } return this['__'+key]; })); objB = create['default'](objA); objB.__foo = 'FOO'; properties.defineProperty(objB, 'foo', computed.computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'B '+value; } return this['__'+key]; })); }, teardown: function() { objA = objB = null; } }); props_helper.testBoth('using get() and set()', function(get, set) { equal(get(objA, 'foo'), 'FOO', 'should get FOO from A'); equal(get(objB, 'foo'), 'FOO', 'should get FOO from B'); set(objA, 'foo', 'BIFF'); equal(get(objA, 'foo'), 'A BIFF', 'should change A'); equal(get(objB, 'foo'), 'FOO', 'should NOT change B'); set(objB, 'foo', 'bar'); equal(get(objB, 'foo'), 'B bar', 'should change B'); equal(get(objA, 'foo'), 'A BIFF', 'should NOT change A'); set(objA, 'foo', 'BAZ'); equal(get(objA, 'foo'), 'A BAZ', 'should change A'); equal(get(objB, 'foo'), 'B bar', 'should NOT change B'); }); QUnit.module('computed - metadata'); QUnit.test("can set metadata on a computed property", function() { var computedProperty = computed.computed(function() { }); computedProperty.meta({ key: 'keyValue' }); equal(computedProperty.meta().key, 'keyValue', "saves passed meta hash to the _meta property"); }); QUnit.test("meta should return an empty hash if no meta is set", function() { var computedProperty = computed.computed(function() { }); deepEqual(computedProperty.meta(), {}, "returned value is an empty hash"); }); // .......................................................... // CACHEABLE // QUnit.module('computed - cacheable', { setup: function() { obj = {}; count = 0; properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { count++; return 'bar '+count; })); }, teardown: function() { obj = count = null; } }); props_helper.testBoth('cacheable should cache', function(get, set) { equal(get(obj, 'foo'), 'bar 1', 'first get'); equal(get(obj, 'foo'), 'bar 1', 'second get'); equal(count, 1, 'should only invoke once'); }); props_helper.testBoth('modifying a cacheable property should update cache', function(get, set) { equal(get(obj, 'foo'), 'bar 1', 'first get'); equal(get(obj, 'foo'), 'bar 1', 'second get'); equal(set(obj, 'foo', 'baz'), 'baz', 'setting'); equal(get(obj, 'foo'), 'bar 2', 'third get'); equal(count, 2, 'should not invoke again'); }); QUnit.test('calling cacheable() on a computed property raises a deprecation', function() { var cp = new computed.ComputedProperty(function() {}); expectDeprecation(function() { cp.cacheable(); }, 'ComputedProperty.cacheable() is deprecated. All computed properties are cacheable by default.'); }); QUnit.test('passing cacheable in a the options to the CP constructor raises a deprecation', function() { expectDeprecation(function() { new computed.ComputedProperty(function() {}, { cacheable: true }); }, "Passing opts.cacheable to the CP constructor is deprecated. Invoke `volatile()` on the CP instead."); }); QUnit.test('calling readOnly() on a computed property with arguments raises a deprecation', function() { var cp = new computed.ComputedProperty(function() {}); expectDeprecation(function() { cp.readOnly(true); }, 'Passing arguments to ComputedProperty.readOnly() is deprecated.'); }); QUnit.test('passing readOnly in a the options to the CP constructor raises a deprecation', function() { expectDeprecation(function() { new computed.ComputedProperty(function() {}, { readOnly: false }); }, "Passing opts.readOnly to the CP constructor is deprecated. All CPs are writable by default. Yo can invoke `readOnly()` on the CP to change this."); }); props_helper.testBoth('inherited property should not pick up cache', function(get, set) { var objB = create['default'](obj); equal(get(obj, 'foo'), 'bar 1', 'obj first get'); equal(get(objB, 'foo'), 'bar 2', 'objB first get'); equal(get(obj, 'foo'), 'bar 1', 'obj second get'); equal(get(objB, 'foo'), 'bar 2', 'objB second get'); set(obj, 'foo', 'baz'); // modify A equal(get(obj, 'foo'), 'bar 3', 'obj third get'); equal(get(objB, 'foo'), 'bar 2', 'objB third get'); }); props_helper.testBoth('cacheFor should return the cached value', function(get, set) { equal(computed.cacheFor(obj, 'foo'), undefined, "should not yet be a cached value"); get(obj, 'foo'); equal(computed.cacheFor(obj, 'foo'), "bar 1", "should retrieve cached value"); }); props_helper.testBoth('cacheFor should return falsy cached values', function(get, set) { properties.defineProperty(obj, 'falsy', computed.computed(function() { return false; })); equal(computed.cacheFor(obj, 'falsy'), undefined, "should not yet be a cached value"); get(obj, 'falsy'); equal(computed.cacheFor(obj, 'falsy'), false, "should retrieve cached value"); }); props_helper.testBoth("setting a cached computed property passes the old value as the third argument", function(get, set) { var obj = { foo: 0 }; var receivedOldValue; properties.defineProperty(obj, 'plusOne', computed.computed( function(key, value, oldValue) { receivedOldValue = oldValue; return value; }).property('foo') ); set(obj, 'plusOne', 1); strictEqual(receivedOldValue, undefined, "oldValue should be undefined"); set(obj, 'plusOne', 2); strictEqual(receivedOldValue, 1, "oldValue should be 1"); set(obj, 'plusOne', 3); strictEqual(receivedOldValue, 2, "oldValue should be 2"); }); props_helper.testBoth("the old value is only passed in if the computed property specifies three arguments", function(get, set) { var obj = { foo: 0 }; properties.defineProperty(obj, 'plusOne', computed.computed( function(key, value) { equal(arguments.length, 2, "computed property is only invoked with two arguments"); return value; }).property('foo') ); set(obj, 'plusOne', 1); set(obj, 'plusOne', 2); set(obj, 'plusOne', 3); }); // .......................................................... // DEPENDENT KEYS // QUnit.module('computed - dependentkey', { setup: function() { obj = { bar: 'baz' }; count = 0; properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { count++; property_get.get(this, 'bar'); return 'bar '+count; }).property('bar')); }, teardown: function() { obj = count = null; } }); props_helper.testBoth('should lazily watch dependent keys on set', function (get, set) { equal(watching.isWatching(obj, 'bar'), false, 'precond not watching dependent key'); set(obj, 'foo', 'bar'); equal(watching.isWatching(obj, 'bar'), true, 'lazily watching dependent key'); }); props_helper.testBoth('should lazily watch dependent keys on get', function (get, set) { equal(watching.isWatching(obj, 'bar'), false, 'precond not watching dependent key'); get(obj, 'foo'); equal(watching.isWatching(obj, 'bar'), true, 'lazily watching dependent key'); }); props_helper.testBoth('local dependent key should invalidate cache', function(get, set) { equal(watching.isWatching(obj, 'bar'), false, 'precond not watching dependent key'); equal(get(obj, 'foo'), 'bar 1', 'get once'); equal(watching.isWatching(obj, 'bar'), true, 'lazily setup watching dependent key'); equal(get(obj, 'foo'), 'bar 1', 'cached retrieve'); set(obj, 'bar', 'BIFF'); // should invalidate foo equal(get(obj, 'foo'), 'bar 2', 'should recache'); equal(get(obj, 'foo'), 'bar 2', 'cached retrieve'); }); props_helper.testBoth('should invalidate multiple nested dependent keys', function(get, set) { var count = 0; properties.defineProperty(obj, 'bar', computed.computed(function() { count++; get(this, 'baz'); return 'baz '+count; }).property('baz')); equal(watching.isWatching(obj, 'bar'), false, 'precond not watching dependent key'); equal(watching.isWatching(obj, 'baz'), false, 'precond not watching dependent key'); equal(get(obj, 'foo'), 'bar 1', 'get once'); equal(watching.isWatching(obj, 'bar'), true, 'lazily setup watching dependent key'); equal(watching.isWatching(obj, 'baz'), true, 'lazily setup watching dependent key'); equal(get(obj, 'foo'), 'bar 1', 'cached retrieve'); set(obj, 'baz', 'BIFF'); // should invalidate bar -> foo equal(watching.isWatching(obj, 'bar'), false, 'should not be watching dependent key after cache cleared'); equal(watching.isWatching(obj, 'baz'), false, 'should not be watching dependent key after cache cleared'); equal(get(obj, 'foo'), 'bar 2', 'should recache'); equal(get(obj, 'foo'), 'bar 2', 'cached retrieve'); equal(watching.isWatching(obj, 'bar'), true, 'lazily setup watching dependent key'); equal(watching.isWatching(obj, 'baz'), true, 'lazily setup watching dependent key'); }); props_helper.testBoth('circular keys should not blow up', function(get, set) { properties.defineProperty(obj, 'bar', computed.computed(function(key, value) { count++; return 'bar '+count; }).property('foo')); properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { count++; return 'foo '+count; }).property('bar')); equal(get(obj, 'foo'), 'foo 1', 'get once'); equal(get(obj, 'foo'), 'foo 1', 'cached retrieve'); set(obj, 'bar', 'BIFF'); // should invalidate bar -> foo -> bar equal(get(obj, 'foo'), 'foo 3', 'should recache'); equal(get(obj, 'foo'), 'foo 3', 'cached retrieve'); }); props_helper.testBoth('redefining a property should undo old dependent keys', function(get, set) { equal(watching.isWatching(obj, 'bar'), false, 'precond not watching dependent key'); equal(get(obj, 'foo'), 'bar 1'); equal(watching.isWatching(obj, 'bar'), true, 'lazily watching dependent key'); properties.defineProperty(obj, 'foo', computed.computed(function() { count++; return 'baz '+count; }).property('baz')); equal(watching.isWatching(obj, 'bar'), false, 'after redefining should not be watching dependent key'); equal(get(obj, 'foo'), 'baz 2'); set(obj, 'bar', 'BIFF'); // should not kill cache equal(get(obj, 'foo'), 'baz 2'); set(obj, 'baz', 'BOP'); equal(get(obj, 'foo'), 'baz 3'); }); props_helper.testBoth('can watch multiple dependent keys specified declaratively via brace expansion', function (get, set) { properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { count++; return 'foo '+count; }).property('qux.{bar,baz}')); equal(get(obj, 'foo'), 'foo 1', "get once"); equal(get(obj, 'foo'), 'foo 1', "cached retrieve"); set(obj, 'qux', {}); set(obj, 'qux.bar', 'bar'); // invalidate foo equal(get(obj, 'foo'), 'foo 2', "foo invalidated from bar"); set(obj, 'qux.baz', 'baz'); // invalidate foo equal(get(obj, 'foo'), 'foo 3', "foo invalidated from baz"); set(obj, 'qux.quux', 'quux'); // do not invalidate foo equal(get(obj, 'foo'), 'foo 3', "foo not invalidated by quux"); }); props_helper.testBoth('throws assertion if brace expansion notation has spaces', function (get, set) { throws(function () { properties.defineProperty(obj, 'roo', computed.computed(function (key, value) { count++; return 'roo ' + count; }).property('fee.{bar, baz,bop , }')); }, /cannot contain spaces/); }); // .......................................................... // CHAINED DEPENDENT KEYS // var func; var moduleOpts = { setup: function() { originalLookup = Ember['default'].lookup; lookup = Ember['default'].lookup = {}; obj = { foo: { bar: { baz: { biff: "BIFF" } } } }; Global = { foo: { bar: { baz: { biff: "BIFF" } } } }; lookup['Global'] = Global; count = 0; func = function() { count++; return property_get.get(obj, 'foo.bar.baz.biff')+' '+count; }; }, teardown: function() { obj = count = func = Global = null; Ember['default'].lookup = originalLookup; } }; QUnit.module('computed - dependentkey with chained properties', moduleOpts); props_helper.testBoth('depending on simple chain', function(get, set) { // assign computed property properties.defineProperty(obj, 'prop', computed.computed(func).property('foo.bar.baz.biff')); equal(get(obj, 'prop'), 'BIFF 1'); set(get(obj, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 2'); equal(get(obj, 'prop'), 'BUZZ 2'); set(get(obj, 'foo.bar'), 'baz', { biff: 'BLOB' }); equal(get(obj, 'prop'), 'BLOB 3'); equal(get(obj, 'prop'), 'BLOB 3'); set(get(obj, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 4'); equal(get(obj, 'prop'), 'BUZZ 4'); set(get(obj, 'foo'), 'bar', { baz: { biff: 'BOOM' } }); equal(get(obj, 'prop'), 'BOOM 5'); equal(get(obj, 'prop'), 'BOOM 5'); set(get(obj, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 6'); equal(get(obj, 'prop'), 'BUZZ 6'); set(obj, 'foo', { bar: { baz: { biff: 'BLARG' } } }); equal(get(obj, 'prop'), 'BLARG 7'); equal(get(obj, 'prop'), 'BLARG 7'); set(get(obj, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 8'); equal(get(obj, 'prop'), 'BUZZ 8'); properties.defineProperty(obj, 'prop'); set(obj, 'prop', 'NONE'); equal(get(obj, 'prop'), 'NONE'); set(obj, 'foo', { bar: { baz: { biff: 'BLARG' } } }); equal(get(obj, 'prop'), 'NONE'); // should do nothing equal(count, 8, 'should be not have invoked computed again'); }); props_helper.testBoth('depending on Global chain', function(get, set) { // assign computed property properties.defineProperty(obj, 'prop', computed.computed(function() { count++; return get('Global.foo.bar.baz.biff')+' '+count; }).property('Global.foo.bar.baz.biff')); equal(get(obj, 'prop'), 'BIFF 1'); set(get(Global, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 2'); equal(get(obj, 'prop'), 'BUZZ 2'); set(get(Global, 'foo.bar'), 'baz', { biff: 'BLOB' }); equal(get(obj, 'prop'), 'BLOB 3'); equal(get(obj, 'prop'), 'BLOB 3'); set(get(Global, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 4'); equal(get(obj, 'prop'), 'BUZZ 4'); set(get(Global, 'foo'), 'bar', { baz: { biff: 'BOOM' } }); equal(get(obj, 'prop'), 'BOOM 5'); equal(get(obj, 'prop'), 'BOOM 5'); set(get(Global, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 6'); equal(get(obj, 'prop'), 'BUZZ 6'); set(Global, 'foo', { bar: { baz: { biff: 'BLARG' } } }); equal(get(obj, 'prop'), 'BLARG 7'); equal(get(obj, 'prop'), 'BLARG 7'); set(get(Global, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(get(obj, 'prop'), 'BUZZ 8'); equal(get(obj, 'prop'), 'BUZZ 8'); properties.defineProperty(obj, 'prop'); set(obj, 'prop', 'NONE'); equal(get(obj, 'prop'), 'NONE'); set(Global, 'foo', { bar: { baz: { biff: 'BLARG' } } }); equal(get(obj, 'prop'), 'NONE'); // should do nothing equal(count, 8, 'should be not have invoked computed again'); }); props_helper.testBoth('chained dependent keys should evaluate computed properties lazily', function(get, set) { properties.defineProperty(obj.foo.bar, 'b', computed.computed(func)); properties.defineProperty(obj.foo, 'c', computed.computed(function() {}).property('bar.b')); equal(count, 0, 'b should not run'); }); // .......................................................... // improved-cp-syntax // if (Ember['default'].FEATURES.isEnabled("new-computed-syntax")) { QUnit.module('computed - improved cp syntax'); QUnit.test('setter and getters are passed using an object', function() { var testObj = Ember['default'].Object.extend({ a: '1', b: '2', aInt: computed.computed('a', { get: function(keyName) { equal(keyName, 'aInt', 'getter receives the keyName'); return parseInt(this.get('a')); }, set: function(keyName, value, oldValue) { equal(keyName, 'aInt', 'setter receives the keyName'); equal(value, 123, 'setter receives the new value'); equal(oldValue, 1, 'setter receives the old value'); this.set('a', ""+value); // side effect return parseInt(this.get('a')); } }) }).create(); ok(testObj.get('aInt') === 1, 'getter works'); testObj.set('aInt', 123); ok(testObj.get('a') === '123', 'setter works'); ok(testObj.get('aInt') === 123, 'cp has been updated too'); }); QUnit.test('setter can be omited', function() { var testObj = Ember['default'].Object.extend({ a: '1', b: '2', aInt: computed.computed('a', { get: function(keyName) { equal(keyName, 'aInt', 'getter receives the keyName'); return parseInt(this.get('a')); } }) }).create(); ok(testObj.get('aInt') === 1, 'getter works'); ok(testObj.get('a') === '1'); testObj.set('aInt', '123'); ok(testObj.get('aInt') === '123', 'cp has been updated too'); }); QUnit.test('the return value of the setter gets cached', function() { var testObj = Ember['default'].Object.extend({ a: '1', sampleCP: computed.computed('a', { get: function(keyName) { ok(false, "The getter should not be invoked"); return 'get-value'; }, set: function(keyName, value, oldValue) { return 'set-value'; } }) }).create(); testObj.set('sampleCP', 'abcd'); ok(testObj.get('sampleCP') === 'set-value', 'The return value of the CP was cached'); }); } // .......................................................... // BUGS // QUnit.module('computed edge cases'); QUnit.test('adding a computed property should show up in key iteration', function() { var obj = {}; properties.defineProperty(obj, 'foo', computed.computed(function() {})); var found = []; for (var key in obj) { found.push(key); } ok(enumerable_utils.indexOf(found, 'foo')>=0, 'should find computed property in iteration found=' + found); ok('foo' in obj, 'foo in obj should pass'); }); props_helper.testBoth("when setting a value after it had been retrieved empty don't pass function UNDEFINED as oldValue", function(get, set) { var obj = {}; var oldValueIsNoFunction = true; properties.defineProperty(obj, 'foo', computed.computed(function(key, value, oldValue) { if (typeof oldValue === 'function') { oldValueIsNoFunction = false; } return undefined; })); get(obj, 'foo'); set(obj, 'foo', undefined); ok(oldValueIsNoFunction); }); QUnit.module('computed - setter'); props_helper.testBoth('setting a watched computed property', function(get, set) { var obj = { firstName: 'Yehuda', lastName: 'Katz' }; properties.defineProperty(obj, 'fullName', computed.computed( function(key, value) { if (arguments.length > 1) { var values = value.split(' '); set(this, 'firstName', values[0]); set(this, 'lastName', values[1]); return value; } return get(this, 'firstName') + ' ' + get(this, 'lastName'); }).property('firstName', 'lastName') ); var fullNameWillChange = 0; var fullNameDidChange = 0; var firstNameWillChange = 0; var firstNameDidChange = 0; var lastNameWillChange = 0; var lastNameDidChange = 0; observer.addBeforeObserver(obj, 'fullName', function () { fullNameWillChange++; }); observer.addObserver(obj, 'fullName', function () { fullNameDidChange++; }); observer.addBeforeObserver(obj, 'firstName', function () { firstNameWillChange++; }); observer.addObserver(obj, 'firstName', function () { firstNameDidChange++; }); observer.addBeforeObserver(obj, 'lastName', function () { lastNameWillChange++; }); observer.addObserver(obj, 'lastName', function () { lastNameDidChange++; }); equal(get(obj, 'fullName'), 'Yehuda Katz'); set(obj, 'fullName', 'Yehuda Katz'); set(obj, 'fullName', 'Kris Selden'); equal(get(obj, 'fullName'), 'Kris Selden'); equal(get(obj, 'firstName'), 'Kris'); equal(get(obj, 'lastName'), 'Selden'); equal(fullNameWillChange, 1); equal(fullNameDidChange, 1); equal(firstNameWillChange, 1); equal(firstNameDidChange, 1); equal(lastNameWillChange, 1); equal(lastNameDidChange, 1); }); props_helper.testBoth('setting a cached computed property that modifies the value you give it', function(get, set) { var obj = { foo: 0 }; properties.defineProperty(obj, 'plusOne', computed.computed( function(key, value) { if (arguments.length > 1) { set(this, 'foo', value); return value + 1; } return get(this, 'foo') + 1; }).property('foo') ); var plusOneWillChange = 0; var plusOneDidChange = 0; observer.addBeforeObserver(obj, 'plusOne', function () { plusOneWillChange++; }); observer.addObserver(obj, 'plusOne', function () { plusOneDidChange++; }); equal(get(obj, 'plusOne'), 1); set(obj, 'plusOne', 1); equal(get(obj, 'plusOne'), 2); set(obj, 'plusOne', 1); equal(get(obj, 'plusOne'), 2); equal(plusOneWillChange, 1); equal(plusOneDidChange, 1); set(obj, 'foo', 5); equal(get(obj, 'plusOne'), 6); equal(plusOneWillChange, 2); equal(plusOneDidChange, 2); }); QUnit.module('computed - default setter'); props_helper.testBoth("when setting a value on a computed property that doesn't handle sets", function(get, set) { var obj = {}; var observerFired = false; properties.defineProperty(obj, 'foo', computed.computed(function() { return 'foo'; })); observer.addObserver(obj, 'foo', null, function() { observerFired = true; }); set(obj, 'foo', 'bar'); equal(get(obj, 'foo'), 'bar', 'The set value is properly returned'); ok(typeof obj.foo === 'string', 'The computed property was removed'); ok(observerFired, 'The observer was still notified'); }); QUnit.module('computed - readOnly'); QUnit.test('is chainable', function() { var cp = computed.computed(function() {}).readOnly(); ok(cp instanceof properties.Descriptor); ok(cp instanceof computed.ComputedProperty); }); props_helper.testBoth('protects against setting', function(get, set) { var obj = { }; properties.defineProperty(obj, 'bar', computed.computed(function(key) { return 'barValue'; }).readOnly()); equal(get(obj, 'bar'), 'barValue'); throws(function() { set(obj, 'bar', 'newBar'); }, /Cannot set read\-only property "bar" on object:/ ); equal(get(obj, 'bar'), 'barValue'); }); QUnit.module('CP macros'); props_helper.testBoth('computed.not', function(get, set) { var obj = { foo: true }; properties.defineProperty(obj, 'notFoo', computed_macros.not('foo')); equal(get(obj, 'notFoo'), false); obj = { foo: { bar: true } }; properties.defineProperty(obj, 'notFoo', computed_macros.not('foo.bar')); equal(get(obj, 'notFoo'), false); }); props_helper.testBoth('computed.empty', function(get, set) { var obj = { foo: [], bar: undefined, baz: null, quz: '' }; properties.defineProperty(obj, 'fooEmpty', computed_macros.empty('foo')); properties.defineProperty(obj, 'barEmpty', computed_macros.empty('bar')); properties.defineProperty(obj, 'bazEmpty', computed_macros.empty('baz')); properties.defineProperty(obj, 'quzEmpty', computed_macros.empty('quz')); equal(get(obj, 'fooEmpty'), true); set(obj, 'foo', [1]); equal(get(obj, 'fooEmpty'), false); equal(get(obj, 'barEmpty'), true); equal(get(obj, 'bazEmpty'), true); equal(get(obj, 'quzEmpty'), true); set(obj, 'quz', 'asdf'); equal(get(obj, 'quzEmpty'), false); }); props_helper.testBoth('computed.bool', function(get, set) { var obj = { foo: function() {}, bar: 'asdf', baz: null, quz: false }; properties.defineProperty(obj, 'fooBool', computed_macros.bool('foo')); properties.defineProperty(obj, 'barBool', computed_macros.bool('bar')); properties.defineProperty(obj, 'bazBool', computed_macros.bool('baz')); properties.defineProperty(obj, 'quzBool', computed_macros.bool('quz')); equal(get(obj, 'fooBool'), true); equal(get(obj, 'barBool'), true); equal(get(obj, 'bazBool'), false); equal(get(obj, 'quzBool'), false); }); props_helper.testBoth('computed.alias', function(get, set) { var obj = { bar: 'asdf', baz: null, quz: false }; properties.defineProperty(obj, 'bay', computed.computed(function(key) { return 'apple'; })); properties.defineProperty(obj, 'barAlias', alias['default']('bar')); properties.defineProperty(obj, 'bazAlias', alias['default']('baz')); properties.defineProperty(obj, 'quzAlias', alias['default']('quz')); properties.defineProperty(obj, 'bayAlias', alias['default']('bay')); equal(get(obj, 'barAlias'), 'asdf'); equal(get(obj, 'bazAlias'), null); equal(get(obj, 'quzAlias'), false); equal(get(obj, 'bayAlias'), 'apple'); set(obj, 'barAlias', 'newBar'); set(obj, 'bazAlias', 'newBaz'); set(obj, 'quzAlias', null); equal(get(obj, 'barAlias'), 'newBar'); equal(get(obj, 'bazAlias'), 'newBaz'); equal(get(obj, 'quzAlias'), null); equal(get(obj, 'bar'), 'newBar'); equal(get(obj, 'baz'), 'newBaz'); equal(get(obj, 'quz'), null); }); props_helper.testBoth('computed.alias set', function(get, set) { var obj = {}; var constantValue = 'always `a`'; properties.defineProperty(obj, 'original', computed.computed(function(key, value) { return constantValue; })); properties.defineProperty(obj, 'aliased', alias['default']('original')); equal(get(obj, 'original'), constantValue); equal(get(obj, 'aliased'), constantValue); set(obj, 'aliased', 'should not set to this value'); equal(get(obj, 'original'), constantValue); equal(get(obj, 'aliased'), constantValue); }); props_helper.testBoth('computed.defaultTo', function(get, set) { expect(6); var obj = { source: 'original source value' }; properties.defineProperty(obj, 'copy', computed_macros.defaultTo('source')); ignoreDeprecation(function() { equal(get(obj, 'copy'), 'original source value'); set(obj, 'copy', 'new copy value'); equal(get(obj, 'source'), 'original source value'); equal(get(obj, 'copy'), 'new copy value'); set(obj, 'source', 'new source value'); equal(get(obj, 'copy'), 'new copy value'); set(obj, 'copy', null); equal(get(obj, 'copy'), 'new source value'); }); expectDeprecation(function() { var obj = { source: 'original source value' }; properties.defineProperty(obj, 'copy', computed_macros.defaultTo('source')); get(obj, 'copy'); }, 'Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead.'); }); props_helper.testBoth('computed.match', function(get, set) { var obj = { name: 'Paul' }; properties.defineProperty(obj, 'isPaul', computed_macros.match('name', /Paul/)); equal(get(obj, 'isPaul'), true, 'is Paul'); set(obj, 'name', 'Pierre'); equal(get(obj, 'isPaul'), false, 'is not Paul anymore'); }); props_helper.testBoth('computed.notEmpty', function(get, set) { var obj = { items: [1] }; properties.defineProperty(obj, 'hasItems', computed_macros.notEmpty('items')); equal(get(obj, 'hasItems'), true, 'is not empty'); set(obj, 'items', []); equal(get(obj, 'hasItems'), false, 'is empty'); }); props_helper.testBoth('computed.equal', function(get, set) { var obj = { name: 'Paul' }; properties.defineProperty(obj, 'isPaul', computed_macros.equal('name', 'Paul')); equal(get(obj, 'isPaul'), true, 'is Paul'); set(obj, 'name', 'Pierre'); equal(get(obj, 'isPaul'), false, 'is not Paul anymore'); }); props_helper.testBoth('computed.gt', function(get, set) { var obj = { number: 2 }; properties.defineProperty(obj, 'isGreaterThenOne', computed_macros.gt('number', 1)); equal(get(obj, 'isGreaterThenOne'), true, 'is gt'); set(obj, 'number', 1); equal(get(obj, 'isGreaterThenOne'), false, 'is not gt'); set(obj, 'number', 0); equal(get(obj, 'isGreaterThenOne'), false, 'is not gt'); }); props_helper.testBoth('computed.gte', function(get, set) { var obj = { number: 2 }; properties.defineProperty(obj, 'isGreaterOrEqualThenOne', computed_macros.gte('number', 1)); equal(get(obj, 'isGreaterOrEqualThenOne'), true, 'is gte'); set(obj, 'number', 1); equal(get(obj, 'isGreaterOrEqualThenOne'), true, 'is gte'); set(obj, 'number', 0); equal(get(obj, 'isGreaterOrEqualThenOne'), false, 'is not gte'); }); props_helper.testBoth('computed.lt', function(get, set) { var obj = { number: 0 }; properties.defineProperty(obj, 'isLesserThenOne', computed_macros.lt('number', 1)); equal(get(obj, 'isLesserThenOne'), true, 'is lt'); set(obj, 'number', 1); equal(get(obj, 'isLesserThenOne'), false, 'is not lt'); set(obj, 'number', 2); equal(get(obj, 'isLesserThenOne'), false, 'is not lt'); }); props_helper.testBoth('computed.lte', function(get, set) { var obj = { number: 0 }; properties.defineProperty(obj, 'isLesserOrEqualThenOne', computed_macros.lte('number', 1)); equal(get(obj, 'isLesserOrEqualThenOne'), true, 'is lte'); set(obj, 'number', 1); equal(get(obj, 'isLesserOrEqualThenOne'), true, 'is lte'); set(obj, 'number', 2); equal(get(obj, 'isLesserOrEqualThenOne'), false, 'is not lte'); }); props_helper.testBoth('computed.and', function(get, set) { var obj = { one: true, two: true }; properties.defineProperty(obj, 'oneAndTwo', computed_macros.and('one', 'two')); equal(get(obj, 'oneAndTwo'), true, 'one and two'); set(obj, 'one', false); equal(get(obj, 'oneAndTwo'), false, 'one and not two'); set(obj, 'one', true); set(obj, 'two', 2); equal(get(obj, 'oneAndTwo'), 2, 'returns truthy value as in &&'); }); props_helper.testBoth('computed.or', function(get, set) { var obj = { one: true, two: true }; properties.defineProperty(obj, 'oneOrTwo', computed_macros.or('one', 'two')); equal(get(obj, 'oneOrTwo'), true, 'one or two'); set(obj, 'one', false); equal(get(obj, 'oneOrTwo'), true, 'one or two'); set(obj, 'two', false); equal(get(obj, 'oneOrTwo'), false, 'nore one nore two'); set(obj, 'two', true); equal(get(obj, 'oneOrTwo'), true, 'one or two'); set(obj, 'one', 1); equal(get(obj, 'oneOrTwo'), 1, 'returns truthy value as in ||'); }); props_helper.testBoth('computed.any', function(get, set) { var obj = { one: 'foo', two: 'bar' }; properties.defineProperty(obj, 'anyOf', computed_macros.any('one', 'two')); equal(get(obj, 'anyOf'), 'foo', 'is foo'); set(obj, 'one', false); equal(get(obj, 'anyOf'), 'bar', 'is bar'); }); props_helper.testBoth('computed.collect', function(get, set) { var obj = { one: 'foo', two: 'bar', three: null }; properties.defineProperty(obj, 'all', computed_macros.collect('one', 'two', 'three', 'four')); deepEqual(get(obj, 'all'), ['foo', 'bar', null, null], 'have all of them'); set(obj, 'four', true); deepEqual(get(obj, 'all'), ['foo', 'bar', null, true], 'have all of them'); var a = []; set(obj, 'one', 0); set(obj, 'three', a); deepEqual(get(obj, 'all'), [0, 'bar', a, true], 'have all of them'); }); props_helper.testBoth('computed.oneWay', function(get, set) { var obj = { firstName: 'Teddy', lastName: 'Zeenny' }; properties.defineProperty(obj, 'nickName', computed_macros.oneWay('firstName')); equal(get(obj, 'firstName'), 'Teddy'); equal(get(obj, 'lastName'), 'Zeenny'); equal(get(obj, 'nickName'), 'Teddy'); set(obj, 'nickName', 'TeddyBear'); equal(get(obj, 'firstName'), 'Teddy'); equal(get(obj, 'lastName'), 'Zeenny'); equal(get(obj, 'nickName'), 'TeddyBear'); set(obj, 'firstName', 'TEDDDDDDDDYYY'); equal(get(obj, 'nickName'), 'TeddyBear'); }); props_helper.testBoth('computed.readOnly', function(get, set) { var obj = { firstName: 'Teddy', lastName: 'Zeenny' }; properties.defineProperty(obj, 'nickName', computed_macros.readOnly('firstName')); equal(get(obj, 'firstName'), 'Teddy'); equal(get(obj, 'lastName'), 'Zeenny'); equal(get(obj, 'nickName'), 'Teddy'); throws(function() { set(obj, 'nickName', 'TeddyBear'); }, / /); equal(get(obj, 'firstName'), 'Teddy'); equal(get(obj, 'lastName'), 'Zeenny'); equal(get(obj, 'nickName'), 'Teddy'); set(obj, 'firstName', 'TEDDDDDDDDYYY'); equal(get(obj, 'nickName'), 'TEDDDDDDDDYYY'); }); props_helper.testBoth('computed.deprecatingAlias', function(get, set) { var obj = { bar: 'asdf', baz: null, quz: false }; properties.defineProperty(obj, 'bay', computed.computed(function(key) { return 'apple'; })); properties.defineProperty(obj, 'barAlias', computed_macros.deprecatingAlias('bar')); properties.defineProperty(obj, 'bazAlias', computed_macros.deprecatingAlias('baz')); properties.defineProperty(obj, 'quzAlias', computed_macros.deprecatingAlias('quz')); properties.defineProperty(obj, 'bayAlias', computed_macros.deprecatingAlias('bay')); expectDeprecation(function() { equal(get(obj, 'barAlias'), 'asdf'); }, 'Usage of `barAlias` is deprecated, use `bar` instead.'); expectDeprecation(function() { equal(get(obj, 'bazAlias'), null); }, 'Usage of `bazAlias` is deprecated, use `baz` instead.'); expectDeprecation(function() { equal(get(obj, 'quzAlias'), false); }, 'Usage of `quzAlias` is deprecated, use `quz` instead.'); expectDeprecation(function() { equal(get(obj, 'bayAlias'), 'apple'); }, 'Usage of `bayAlias` is deprecated, use `bay` instead.'); expectDeprecation(function() { set(obj, 'barAlias', 'newBar'); }, 'Usage of `barAlias` is deprecated, use `bar` instead.'); expectDeprecation(function() { set(obj, 'bazAlias', 'newBaz'); }, 'Usage of `bazAlias` is deprecated, use `baz` instead.'); expectDeprecation(function() { set(obj, 'quzAlias', null); }, 'Usage of `quzAlias` is deprecated, use `quz` instead.'); equal(get(obj, 'barAlias'), 'newBar'); equal(get(obj, 'bazAlias'), 'newBaz'); equal(get(obj, 'quzAlias'), null); equal(get(obj, 'bar'), 'newBar'); equal(get(obj, 'baz'), 'newBaz'); equal(get(obj, 'quz'), null); }); }); enifed('ember-metal/tests/computed_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/computed_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/computed_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/computed_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/computed_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/computed_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/core/inspect_test', ['ember-metal/utils', 'ember-metal/platform/create'], function (utils, create) { 'use strict'; QUnit.module("Ember.inspect"); QUnit.test("strings", function() { equal(utils.inspect("foo"), "foo"); }); QUnit.test("numbers", function() { equal(utils.inspect(2.6), "2.6"); }); QUnit.test("null", function() { equal(utils.inspect(null), "null"); }); QUnit.test("undefined", function() { equal(utils.inspect(undefined), "undefined"); }); QUnit.test("true", function() { equal(utils.inspect(true), "true"); }); QUnit.test("false", function() { equal(utils.inspect(false), "false"); }); QUnit.test("object", function() { equal(utils.inspect({}), "{}"); equal(utils.inspect({ foo: 'bar' }), "{foo: bar}"); equal(utils.inspect({ foo: function() { return this; } }), "{foo: function() { ... }}"); }); QUnit.test("objects without a prototype", function() { var prototypelessObj = create['default'](null); equal(utils.inspect({ foo: prototypelessObj }), "{foo: [object Object]}"); }); QUnit.test("array", function() { equal(utils.inspect([1,2,3]), "[1,2,3]"); }); QUnit.test("regexp", function() { equal(utils.inspect(/regexp/), "/regexp/"); }); QUnit.test("date", function() { var inspected = utils.inspect(new Date("Sat Apr 30 2011 13:24:11")); ok(inspected.match(/Sat Apr 30/), "The inspected date has its date"); ok(inspected.match(/2011/), "The inspected date has its year"); ok(inspected.match(/13:24:11/), "The inspected date has its time"); }); }); enifed('ember-metal/tests/core/inspect_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/core'); test('ember-metal/tests/core/inspect_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/core/inspect_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/core/inspect_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/core'); test('ember-metal/tests/core/inspect_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/core/inspect_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/enumerable_utils_test', ['ember-metal/enumerable_utils'], function (EnumerableUtils) { 'use strict'; QUnit.module('Ember.EnumerableUtils.intersection'); QUnit.test('returns an array of objects that appear in both enumerables', function() { var a = [1,2,3]; var b = [2,3,4]; var result; result = EnumerableUtils['default'].intersection(a, b); deepEqual(result, [2,3]); }); QUnit.test("large replace", function() { expect(0); // https://code.google.com/p/chromium/issues/detail?id=56588 EnumerableUtils['default'].replace([], 0, undefined, new Array(62401)); // max + 1 in Chrome 28.0.1500.71 EnumerableUtils['default'].replace([], 0, undefined, new Array(65535)); // max + 1 in Safari 6.0.5 (8536.30.1) EnumerableUtils['default'].replace([], 0, undefined, new Array(491519)); // max + 1 in FireFox 22.0 }); }); enifed('ember-metal/tests/enumerable_utils_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/enumerable_utils_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/enumerable_utils_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/enumerable_utils_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/enumerable_utils_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/enumerable_utils_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/error_test', function () { 'use strict'; QUnit.module("Ember Error Throwing"); QUnit.test("new Ember.Error displays provided message", function() { throws(function() { throw new Ember.Error('A Message'); }, function(e) { return e.message === 'A Message'; }, 'the assigned message was displayed'); }); }); enifed('ember-metal/tests/error_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/error_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/error_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/error_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/error_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/error_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/events_test', ['ember-metal/mixin', 'ember-metal/platform/create', 'ember-metal/utils', 'ember-metal/events'], function (mixin, create, utils, events) { 'use strict'; QUnit.module('system/props/events_test'); QUnit.test('listener should receive event - removing should remove', function() { var obj = {}; var count = 0; var F = function() { count++; }; events.addListener(obj, 'event!', F); equal(count, 0, 'nothing yet'); events.sendEvent(obj, 'event!'); equal(count, 1, 'received event'); events.removeListener(obj, 'event!', F); count = 0; events.sendEvent(obj, 'event!'); equal(count, 0, 'received event'); }); QUnit.test('listeners should be inherited', function() { var obj = {}; var count = 0; var F = function() { count++; }; events.addListener(obj, 'event!', F); var obj2 = create['default'](obj); equal(count, 0, 'nothing yet'); events.sendEvent(obj2, 'event!'); equal(count, 1, 'received event'); events.removeListener(obj2, 'event!', F); count = 0; events.sendEvent(obj2, 'event!'); equal(count, 0, 'did not receive event'); events.sendEvent(obj, 'event!'); equal(count, 1, 'should still invoke on parent'); }); QUnit.test('adding a listener more than once should only invoke once', function() { var obj = {}; var count = 0; var F = function() { count++; }; events.addListener(obj, 'event!', F); events.addListener(obj, 'event!', F); events.sendEvent(obj, 'event!'); equal(count, 1, 'should only invoke once'); }); QUnit.test('adding a listener with a target should invoke with target', function() { var obj = {}; var target; target = { count: 0, method: function() { this.count++; } }; events.addListener(obj, 'event!', target, target.method); events.sendEvent(obj, 'event!'); equal(target.count, 1, 'should invoke'); }); QUnit.test('suspending a listener should not invoke during callback', function() { var obj = {}; var target, otherTarget; target = { count: 0, method: function() { this.count++; } }; otherTarget = { count: 0, method: function() { this.count++; } }; events.addListener(obj, 'event!', target, target.method); events.addListener(obj, 'event!', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); events.sendEvent(obj, 'event!'); return 'result'; } events.sendEvent(obj, 'event!'); equal(events.suspendListener(obj, 'event!', target, target.method, callback), 'result'); events.sendEvent(obj, 'event!'); equal(target.count, 2, 'should invoke'); equal(otherTarget.count, 3, 'should invoke'); }); QUnit.test('adding a listener with string method should lookup method on event delivery', function() { var obj = {}; var target; target = { count: 0, method: function() {} }; events.addListener(obj, 'event!', target, 'method'); events.sendEvent(obj, 'event!'); equal(target.count, 0, 'should invoke but do nothing'); target.method = function() { this.count++; }; events.sendEvent(obj, 'event!'); equal(target.count, 1, 'should invoke now'); }); QUnit.test('calling sendEvent with extra params should be passed to listeners', function() { var obj = {}; var params = null; events.addListener(obj, 'event!', function() { params = Array.prototype.slice.call(arguments); }); events.sendEvent(obj, 'event!', ['foo', 'bar']); deepEqual(params, ['foo', 'bar'], 'params should be saved'); }); QUnit.test('implementing sendEvent on object should invoke', function() { var obj = { sendEvent: function(eventName, params) { equal(eventName, 'event!', 'eventName'); deepEqual(params, ['foo', 'bar']); this.count++; }, count: 0 }; events.addListener(obj, 'event!', obj, function() { this.count++; }); events.sendEvent(obj, 'event!', ['foo', 'bar']); equal(obj.count, 2, 'should have invoked method & listener'); }); QUnit.test('hasListeners tells you if there are listeners for a given event', function() { var obj = {}; var F = function() {}; var F2 = function() {}; equal(events.hasListeners(obj, 'event!'), false, 'no listeners at first'); events.addListener(obj, 'event!', F); events.addListener(obj, 'event!', F2); equal(events.hasListeners(obj, 'event!'), true, 'has listeners'); events.removeListener(obj, 'event!', F); equal(events.hasListeners(obj, 'event!'), true, 'has listeners'); events.removeListener(obj, 'event!', F2); equal(events.hasListeners(obj, 'event!'), false, 'has no more listeners'); events.addListener(obj, 'event!', F); equal(events.hasListeners(obj, 'event!'), true, 'has listeners'); }); QUnit.test('calling removeListener without method should remove all listeners', function() { var obj = {}; var F = function() {}; var F2 = function() {}; equal(events.hasListeners(obj, 'event!'), false, 'no listeners at first'); events.addListener(obj, 'event!', F); events.addListener(obj, 'event!', F2); equal(events.hasListeners(obj, 'event!'), true, 'has listeners'); events.removeListener(obj, 'event!'); equal(events.hasListeners(obj, 'event!'), false, 'has no more listeners'); }); QUnit.test('while suspended, it should not be possible to add a duplicate listener', function() { var obj = {}; var target; target = { count: 0, method: function() { this.count++; } }; events.addListener(obj, 'event!', target, target.method); function callback() { events.addListener(obj, 'event!', target, target.method); } events.sendEvent(obj, 'event!'); events.suspendListener(obj, 'event!', target, target.method, callback); equal(target.count, 1, 'should invoke'); equal(utils.meta(obj).listeners['event!'].length, 3, "a duplicate listener wasn't added"); // now test suspendListeners... events.sendEvent(obj, 'event!'); events.suspendListeners(obj, ['event!'], target, target.method, callback); equal(target.count, 2, 'should have invoked again'); equal(utils.meta(obj).listeners['event!'].length, 3, "a duplicate listener wasn't added"); }); QUnit.test('a listener can be added as part of a mixin', function() { var triggered = 0; var MyMixin = mixin.Mixin.create({ foo1: events.on('bar', function() { triggered++; }), foo2: events.on('bar', function() { triggered++; }) }); var obj = {}; MyMixin.apply(obj); events.sendEvent(obj, 'bar'); equal(triggered, 2, 'should invoke listeners'); }); QUnit.test('a listener added as part of a mixin may be overridden', function() { var triggered = 0; var FirstMixin = mixin.Mixin.create({ foo: events.on('bar', function() { triggered++; }) }); var SecondMixin = mixin.Mixin.create({ foo: events.on('baz', function() { triggered++; }) }); var obj = {}; FirstMixin.apply(obj); SecondMixin.apply(obj); events.sendEvent(obj, 'bar'); equal(triggered, 0, 'should not invoke from overriden property'); events.sendEvent(obj, 'baz'); equal(triggered, 1, 'should invoke from subclass property'); }); }); enifed('ember-metal/tests/events_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/events_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/events_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/events_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/events_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/events_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/expand_properties_test', ['ember-metal/expand_properties'], function (expandProperties) { 'use strict'; var foundProperties = []; function addProperty(property) { foundProperties.push(property); } QUnit.module('Property Brace Expansion Test', { setup: function() { foundProperties = []; } }); QUnit.test('Properties without expansions are unaffected', function() { expect(1); expandProperties['default']('a', addProperty); expandProperties['default']('a.b', addProperty); expandProperties['default']('a.b.@each', addProperty); deepEqual(['a', 'a.b', 'a.b.@each'].sort(), foundProperties.sort()); }); QUnit.test('A single expansion at the end expands properly', function() { expect(1); expandProperties['default']('a.b.{c,d}', addProperty); deepEqual(['a.b.c', 'a.b.d'].sort(), foundProperties.sort()); }); QUnit.test('A property with only a brace expansion expands correctly', function() { expect(1); expandProperties['default']('{a,b,c}', addProperty); var expected = ['a', 'b', 'c']; deepEqual(expected.sort(), foundProperties.sort()); }); QUnit.test('Expansions with single properties only expand once', function() { expect(1); expandProperties['default']('a.b.{c}.d.{e}', addProperty); deepEqual(['a.b.c.d.e'], foundProperties); }); QUnit.test('A single brace expansion expands correctly', function() { expect(1); expandProperties['default']('a.{b,c,d}.e', addProperty); var expected = ['a.b.e', 'a.c.e', 'a.d.e']; deepEqual(expected.sort(), foundProperties.sort()); }); QUnit.test('Multiple brace expansions work correctly', function() { expect(1); expandProperties['default']('{a,b,c}.d.{e,f}.g', addProperty); var expected = ['a.d.e.g', 'a.d.f.g', 'b.d.e.g', 'b.d.f.g', 'c.d.e.g', 'c.d.f.g']; deepEqual(expected.sort(), foundProperties.sort()); }); QUnit.test('A property with only brace expansions expands correctly', function() { expect(1); expandProperties['default']('{a,b,c}.{d}.{e,f}', addProperty); var expected = ['a.d.e', 'a.d.f', 'b.d.e', 'b.d.f', 'c.d.e', 'c.d.f']; deepEqual(expected.sort(), foundProperties.sort()); }); }); enifed('ember-metal/tests/expand_properties_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/expand_properties_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/expand_properties_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/expand_properties_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/expand_properties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/expand_properties_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/features_test', ['ember-metal/core'], function (Ember) { 'use strict'; var isEnabled = Ember['default'].FEATURES.isEnabled; var origFeatures, origEnableAll, origEnableOptional; QUnit.module("Ember.FEATURES.isEnabled", { setup: function() { origFeatures = Ember['default'].FEATURES; origEnableAll = Ember['default'].ENV.ENABLE_ALL_FEATURES; origEnableOptional = Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES; }, teardown: function() { Ember['default'].FEATURES = origFeatures; Ember['default'].ENV.ENABLE_ALL_FEATURES = origEnableAll; Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = origEnableOptional; } }); QUnit.test("ENV.ENABLE_ALL_FEATURES", function() { Ember['default'].ENV.ENABLE_ALL_FEATURES = true; Ember['default'].FEATURES['fred'] = false; Ember['default'].FEATURES['wilma'] = null; equal(isEnabled('fred'), true, "overrides features set to false"); equal(isEnabled('wilma'), true, "enables optional features"); equal(isEnabled('betty'), true, "enables non-specified features"); }); QUnit.test("ENV.ENABLE_OPTIONAL_FEATURES", function() { Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = true; Ember['default'].FEATURES['fred'] = false; Ember['default'].FEATURES['barney'] = true; Ember['default'].FEATURES['wilma'] = null; equal(isEnabled('fred'), false, "returns flag value if false"); equal(isEnabled('barney'), true, "returns flag value if true"); equal(isEnabled('wilma'), true, "returns true if flag is not true|false|undefined"); equal(isEnabled('betty'), undefined, "returns flag value if undefined"); }); QUnit.test("isEnabled without ENV options", function() { Ember['default'].ENV.ENABLE_ALL_FEATURES = false; Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES = false; Ember['default'].FEATURES['fred'] = false; Ember['default'].FEATURES['barney'] = true; Ember['default'].FEATURES['wilma'] = null; equal(isEnabled('fred'), false, "returns flag value if false"); equal(isEnabled('barney'), true, "returns flag value if true"); equal(isEnabled('wilma'), false, "returns false if flag is not set"); equal(isEnabled('betty'), undefined, "returns flag value if undefined"); }); }); enifed('ember-metal/tests/features_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/features_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/features_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/features_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/features_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/features_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/injected_property_test', ['ember-metal/properties', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/injected_property'], function (properties, property_get, property_set, InjectedProperty) { 'use strict'; QUnit.module('InjectedProperty'); QUnit.test('injected properties should be descriptors', function() { ok(new InjectedProperty['default']() instanceof properties.Descriptor); }); QUnit.test('injected properties should be overridable', function() { var obj = {}; properties.defineProperty(obj, 'foo', new InjectedProperty['default']()); property_set.set(obj, 'foo', 'bar'); equal(property_get.get(obj, 'foo'), 'bar', 'should return the overriden value'); }); QUnit.test("getting on an object without a container should fail assertion", function() { var obj = {}; properties.defineProperty(obj, 'foo', new InjectedProperty['default']('type', 'name')); expectAssertion(function() { property_get.get(obj, 'foo'); }, /Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container./); }); QUnit.test("getting should return a lookup on the container", function() { expect(2); var obj = { container: { lookup: function(key) { ok(true, 'should call container.lookup'); return key; } } }; properties.defineProperty(obj, 'foo', new InjectedProperty['default']('type', 'name')); equal(property_get.get(obj, 'foo'), 'type:name', 'should return the value of container.lookup'); }); QUnit.test("omitting the lookup name should default to the property name", function() { var obj = { container: { lookup: function(key) { return key; } } }; properties.defineProperty(obj, 'foo', new InjectedProperty['default']('type')); equal(property_get.get(obj, 'foo'), 'type:foo', 'should lookup the type using the property name'); }); }); enifed('ember-metal/tests/injected_property_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/injected_property_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/injected_property_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/injected_property_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/injected_property_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/injected_property_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/instrumentation_test', ['ember-metal/instrumentation'], function (instrumentation) { 'use strict'; QUnit.module("Ember Instrumentation", { setup: function() { }, teardown: function() { instrumentation.reset(); } }); QUnit.test("execute block even if no listeners", function() { var result = instrumentation.instrument("render", {}, function() { return "hello"; }); equal(result, "hello", 'called block'); }); QUnit.test("subscribing to a simple path receives the listener", function() { expect(12); var sentPayload = {}; var count = 0; instrumentation.subscribe("render", { before: function(name, timestamp, payload) { if (count === 0) { strictEqual(name, "render"); } else { strictEqual(name, "render.handlebars"); } ok(typeof timestamp === 'number'); strictEqual(payload, sentPayload); }, after: function(name, timestamp, payload) { if (count === 0) { strictEqual(name, "render"); } else { strictEqual(name, "render.handlebars"); } ok(typeof timestamp === 'number'); strictEqual(payload, sentPayload); count++; } }); instrumentation.instrument("render", sentPayload, function() { }); instrumentation.instrument("render.handlebars", sentPayload, function() { }); }); QUnit.test("returning a value from the before callback passes it to the after callback", function() { expect(2); var passthru1 = {}; var passthru2 = {}; instrumentation.subscribe("render", { before: function(name, timestamp, payload) { return passthru1; }, after: function(name, timestamp, payload, beforeValue) { strictEqual(beforeValue, passthru1); } }); instrumentation.subscribe("render", { before: function(name, timestamp, payload) { return passthru2; }, after: function(name, timestamp, payload, beforeValue) { strictEqual(beforeValue, passthru2); } }); instrumentation.instrument("render", null, function() {}); }); QUnit.test("instrument with 2 args (name, callback) no payload", function() { expect(1); instrumentation.subscribe("render", { before: function(name, timestamp, payload) { deepEqual(payload, {}); }, after: function() {} }); instrumentation.instrument("render", function() {}); }); QUnit.test("instrument with 3 args (name, callback, binding) no payload", function() { expect(2); var binding = {}; instrumentation.subscribe("render", { before: function(name, timestamp, payload) { deepEqual(payload, {}); }, after: function() {} }); instrumentation.instrument("render", function() { deepEqual(this, binding); }, binding); }); QUnit.test("instrument with 3 args (name, payload, callback) with payload", function() { expect(1); var expectedPayload = { hi: 1 }; instrumentation.subscribe("render", { before: function(name, timestamp, payload) { deepEqual(payload, expectedPayload); }, after: function() {} }); instrumentation.instrument("render", expectedPayload, function() {}); }); QUnit.test("instrument with 4 args (name, payload, callback, binding) with payload", function() { expect(2); var expectedPayload = { hi: 1 }; var binding = {}; instrumentation.subscribe("render", { before: function(name, timestamp, payload) { deepEqual(payload, expectedPayload); }, after: function() {} }); instrumentation.instrument("render", expectedPayload, function() { deepEqual(this, binding); }, binding); }); QUnit.test("raising an exception in the instrumentation attaches it to the payload", function() { expect(2); var error = new Error("Instrumentation"); instrumentation.subscribe("render", { before: function() {}, after: function(name, timestamp, payload) { strictEqual(payload.exception, error); } }); instrumentation.subscribe("render", { before: function() {}, after: function(name, timestamp, payload) { strictEqual(payload.exception, error); } }); instrumentation.instrument("render.handlebars", null, function() { throw error; }); }); QUnit.test("it is possible to add a new subscriber after the first instrument", function() { instrumentation.instrument("render.handlebars", null, function() {}); instrumentation.subscribe("render", { before: function() { ok(true, "Before callback was called"); }, after: function() { ok(true, "After callback was called"); } }); instrumentation.instrument("render.handlebars", null, function() {}); }); QUnit.test("it is possible to remove a subscriber", function() { expect(4); var count = 0; var subscriber = instrumentation.subscribe("render", { before: function() { equal(count, 0); ok(true, "Before callback was called"); }, after: function() { equal(count, 0); ok(true, "After callback was called"); count++; } }); instrumentation.instrument("render.handlebars", null, function() {}); instrumentation.unsubscribe(subscriber); instrumentation.instrument("render.handlebars", null, function() {}); }); }); enifed('ember-metal/tests/instrumentation_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/instrumentation_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/instrumentation_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/instrumentation_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/instrumentation_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/instrumentation_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/is_blank_test', ['ember-metal/is_blank'], function (isBlank) { 'use strict'; QUnit.module("Ember.isBlank"); QUnit.test("Ember.isBlank", function() { var string = "string"; var fn = function() {}; var object = { length: 0 }; equal(true, isBlank['default'](null), "for null"); equal(true, isBlank['default'](undefined), "for undefined"); equal(true, isBlank['default'](""), "for an empty String"); equal(true, isBlank['default'](" "), "for a whitespace String"); equal(true, isBlank['default']("\n\t"), "for another whitespace String"); equal(false, isBlank['default']("\n\t Hi"), "for a String with whitespaces"); equal(false, isBlank['default'](true), "for true"); equal(false, isBlank['default'](false), "for false"); equal(false, isBlank['default'](string), "for a String"); equal(false, isBlank['default'](fn), "for a Function"); equal(false, isBlank['default'](0), "for 0"); equal(true, isBlank['default']([]), "for an empty Array"); equal(false, isBlank['default']({}), "for an empty Object"); equal(true, isBlank['default'](object), "for an Object that has zero 'length'"); equal(false, isBlank['default']([1, 2, 3]), "for a non-empty array"); }); }); enifed('ember-metal/tests/is_blank_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/is_blank_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/is_blank_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/is_blank_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/is_blank_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/is_blank_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/is_empty_test', ['ember-metal/is_empty', 'ember-metal/map'], function (isEmpty, ember_metal__map) { 'use strict'; QUnit.module("Ember.isEmpty"); QUnit.test("Ember.isEmpty", function() { var string = "string"; var fn = function() {}; var object = { length: 0 }; equal(true, isEmpty['default'](null), "for null"); equal(true, isEmpty['default'](undefined), "for undefined"); equal(true, isEmpty['default'](""), "for an empty String"); equal(false, isEmpty['default'](true), "for true"); equal(false, isEmpty['default'](false), "for false"); equal(false, isEmpty['default'](string), "for a String"); equal(false, isEmpty['default'](fn), "for a Function"); equal(false, isEmpty['default'](0), "for 0"); equal(true, isEmpty['default']([]), "for an empty Array"); equal(false, isEmpty['default']({}), "for an empty Object"); equal(true, isEmpty['default'](object), "for an Object that has zero 'length'"); }); QUnit.test("Ember.isEmpty Ember.Map", function() { var map = new ember_metal__map.Map(); equal(true, isEmpty['default'](map), "Empty map is empty"); map.set('foo', 'bar'); equal(false, isEmpty['default'](map), "Map is not empty"); }); QUnit.test("Ember.isEmpty Ember.OrderedSet", function() { var orderedSet = new ember_metal__map.OrderedSet(); equal(true, isEmpty['default'](orderedSet), "Empty ordered set is empty"); orderedSet.add('foo'); equal(false, isEmpty['default'](orderedSet), "Ordered set is not empty"); }); }); enifed('ember-metal/tests/is_empty_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/is_empty_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/is_empty_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/is_empty_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/is_empty_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/is_empty_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/is_none_test', ['ember-metal/is_none'], function (isNone) { 'use strict'; QUnit.module("Ember.isNone"); QUnit.test("Ember.isNone", function() { var string = "string"; var fn = function() {}; equal(true, isNone['default'](null), "for null"); equal(true, isNone['default'](undefined), "for undefined"); equal(false, isNone['default'](""), "for an empty String"); equal(false, isNone['default'](true), "for true"); equal(false, isNone['default'](false), "for false"); equal(false, isNone['default'](string), "for a String"); equal(false, isNone['default'](fn), "for a Function"); equal(false, isNone['default'](0), "for 0"); equal(false, isNone['default']([]), "for an empty Array"); equal(false, isNone['default']({}), "for an empty Object"); }); }); enifed('ember-metal/tests/is_none_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/is_none_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/is_none_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/is_none_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/is_none_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/is_none_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/is_present_test', ['ember-metal/is_present'], function (isPresent) { 'use strict'; QUnit.module("Ember.isPresent"); QUnit.test("Ember.isPresent", function() { var string = "string"; var fn = function() {}; var object = { length: 0 }; equal(false, isPresent['default'](), "for no params"); equal(false, isPresent['default'](null), "for null"); equal(false, isPresent['default'](undefined), "for undefined"); equal(false, isPresent['default'](""), "for an empty String"); equal(false, isPresent['default'](" "), "for a whitespace String"); equal(false, isPresent['default']("\n\t"), "for another whitespace String"); equal(true, isPresent['default']("\n\t Hi"), "for a String with whitespaces"); equal(true, isPresent['default'](true), "for true"); equal(true, isPresent['default'](false), "for false"); equal(true, isPresent['default'](string), "for a String"); equal(true, isPresent['default'](fn), "for a Function"); equal(true, isPresent['default'](0), "for 0"); equal(false, isPresent['default']([]), "for an empty Array"); equal(true, isPresent['default']({}), "for an empty Object"); equal(false, isPresent['default'](object), "for an Object that has zero 'length'"); equal(true, isPresent['default']([1,2,3]), "for a non-empty array"); }); }); enifed('ember-metal/tests/is_present_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/is_present_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/is_present_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/is_present_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/is_present_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/is_present_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/keys_test', ['ember-metal/property_set', 'ember-metal/keys', 'ember-metal/observer'], function (property_set, keys, observer) { 'use strict'; function K() { return this; } QUnit.module("Fetch Keys "); QUnit.test("should get a key array for a specified object", function() { var object1 = {}; object1.names = "Rahul"; object1.age = "23"; object1.place = "Mangalore"; var object2 = keys['default'](object1); deepEqual(object2, ['names','age','place']); }); // This test is for IE8. QUnit.test("should get a key array for property that is named the same as prototype property", function() { var object1 = { toString: function() {} }; var object2 = keys['default'](object1); deepEqual(object2, ['toString']); }); QUnit.test('should not contain properties declared in the prototype', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); deepEqual(keys['default'](beer), []); }); QUnit.test('should return properties that were set after object creation', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); property_set.set(beer, 'brand', 'big daddy'); deepEqual(keys['default'](beer), ['brand']); }); QUnit.module('Keys behavior with observers'); QUnit.test('should not leak properties on the prototype', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); observer.addObserver(beer, 'type', K); deepEqual(keys['default'](beer), []); observer.removeObserver(beer, 'type', K); }); QUnit.test('observing a non existent property', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); observer.addObserver(beer, 'brand', K); deepEqual(keys['default'](beer), []); property_set.set(beer, 'brand', 'Corona'); deepEqual(keys['default'](beer), ['brand']); observer.removeObserver(beer, 'brand', K); }); QUnit.test('with observers switched on and off', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); observer.addObserver(beer, 'type', K); observer.removeObserver(beer, 'type', K); deepEqual(keys['default'](beer), []); }); QUnit.test('observers switched on and off with setter in between', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); observer.addObserver(beer, 'type', K); property_set.set(beer, 'type', 'ale'); observer.removeObserver(beer, 'type', K); deepEqual(keys['default'](beer), ['type']); }); QUnit.test('observer switched on and off and then setter', function () { function Beer() { } Beer.prototype.type = 'ipa'; var beer = new Beer(); observer.addObserver(beer, 'type', K); observer.removeObserver(beer, 'type', K); property_set.set(beer, 'type', 'ale'); deepEqual(keys['default'](beer), ['type']); }); }); enifed('ember-metal/tests/keys_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/keys_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/keys_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/keys_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/keys_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/keys_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/libraries_test', ['ember-metal/libraries'], function (Libraries) { 'use strict'; /* globals EmberDev */ var libs, registry; QUnit.module('Libraries registry', { setup: function() { libs = new Libraries['default'](); registry = libs._registry; }, teardown: function() { libs = null; registry = null; } }); QUnit.test('core libraries come before other libraries', function() { expect(2); libs.register('my-lib', '2.0.0a'); libs.registerCoreLibrary('DS', '1.0.0-beta.2'); equal(registry[0].name, 'DS'); equal(registry[1].name, 'my-lib'); }); QUnit.test('only the first registration of a library is stored', function() { expect(3); libs.register('magic', 1.23); libs.register('magic', 2.23); equal(registry[0].name, 'magic'); equal(registry[0].version, 1.23); equal(registry.length, 1); }); QUnit.test('attempting to register a library that is already registered warns you', function() { if (EmberDev && EmberDev.runningProdBuild) { ok(true, 'Logging does not occur in production builds'); return; } expect(1); var oldWarn = Ember.warn; libs.register('magic', 1.23); Ember.warn = function(msg, test) { if (!test) { equal(msg, 'Library "magic" is already registered with Ember.'); } }; // Should warn us libs.register('magic', 2.23); Ember.warn = oldWarn; }); QUnit.test('libraries can be de-registered', function() { expect(2); libs.register('lib1', '1.0.0b'); libs.register('lib2', '1.0.0b'); libs.register('lib3', '1.0.0b'); libs.deRegister('lib1'); libs.deRegister('lib3'); equal(registry[0].name, 'lib2'); equal(registry.length, 1); }); QUnit.test('Libraries#each allows us to loop through each registered library (but is deprecated)', function() { expect(5); var items = [{ name: 'lib1', version: '1.0.0' }, { name: 'lib2', version: '2.0.0' }]; for (var i = 0, l = items.length; i < l; i++) { libs.register(items[i].name, items[i].version); } expectDeprecation(function() { libs.each(function (name, version) { var expectedLib = items.shift(); equal(expectedLib.name, name); equal(expectedLib.version, version); }); }, 'Using Ember.libraries.each() is deprecated. Access to a list of registered libraries is currently a private API. If you are not knowingly accessing this method, your out-of-date Ember Inspector may be doing so.'); }); }); enifed('ember-metal/tests/libraries_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/libraries_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/libraries_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/libraries_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/libraries_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/libraries_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/main_test', ['ember-metal/core'], function (Ember) { 'use strict'; QUnit.module('ember-metal/core/main'); QUnit.test('Ember registers itself', function() { var lib = Ember['default'].libraries._registry[0]; equal(lib.name, 'Ember'); equal(lib.version, Ember['default'].VERSION); }); }); enifed('ember-metal/tests/main_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/main_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/main_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/main_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/main_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/main_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/map_test', ['ember-metal/map', 'ember-metal/platform/define_property'], function (ember_metal__map, define_property) { 'use strict'; var object, number, string, map, variety; var varieties = [['Map', ember_metal__map.Map], ['MapWithDefault', ember_metal__map.MapWithDefault]]; function testMap(nameAndFunc) { variety = nameAndFunc[0]; QUnit.module("Ember." + variety + " (forEach and get are implicitly tested)", { setup: function() { object = {}; number = 42; string = "foo"; map = nameAndFunc[1].create(); } }); var mapHasLength = function(expected, theMap) { theMap = theMap || map; var length = 0; theMap.forEach(function() { length++; }); equal(length, expected, "map should contain " + expected + " items"); }; var mapHasEntries = function(entries, theMap) { theMap = theMap || map; for (var i = 0, l = entries.length; i < l; i++) { equal(theMap.get(entries[i][0]), entries[i][1]); equal(theMap.has(entries[i][0]), true); } mapHasLength(entries.length, theMap); }; var unboundThis; (function() { unboundThis = this; }()); QUnit.test("set", function() { map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); mapHasEntries([ [object, "winning"], [number, "winning"], [string, "winning"] ]); map.set(object, "losing"); map.set(number, "losing"); map.set(string, "losing"); mapHasEntries([ [object, "losing"], [number, "losing"], [string, "losing"] ]); equal(map.has("nope"), false, "expected the key `nope` to not be present"); equal(map.has({}), false, "expected they key `{}` to not be present"); }); QUnit.test("set chaining", function() { map.set(object, "winning"). set(number, "winning"). set(string, "winning"); mapHasEntries([ [object, "winning"], [number, "winning"], [string, "winning"] ]); map.set(object, "losing"). set(number, "losing"). set(string, "losing"); mapHasEntries([ [object, "losing"], [number, "losing"], [string, "losing"] ]); equal(map.has("nope"), false, "expected the key `nope` to not be present"); equal(map.has({}), false, "expected they key `{}` to not be present"); }); QUnit.test("with key with undefined value", function() { map.set("foo", undefined); map.forEach(function(value, key) { equal(value, undefined); equal(key, 'foo'); }); ok(map.has("foo"), "has key foo, even with undefined value"); equal(map.size, 1); }); QUnit.test("arity of forEach is 1 – es6 23.1.3.5", function() { equal(map.forEach.length, 1, 'expected arity for map.forEach is 1'); }); QUnit.test("forEach throws without a callback as the first argument", function() { equal(map.forEach.length, 1, 'expected arity for map.forEach is 1'); }); QUnit.test("remove", function() { map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); expectDeprecation(function() { map.remove(object); map.remove(number); map.remove(string); // doesn't explode map.remove({}); }, 'Calling `Map.prototype.remove` has been deprecated, please use `Map.prototype.delete` instead.'); mapHasEntries([]); }); QUnit.test("has empty collection", function() { equal(map.has('foo'), false); equal(map.has(), false); }); QUnit.test("delete", function() { expectNoDeprecation(); map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); map["delete"](object); map["delete"](number); map["delete"](string); // doesn't explode map["delete"]({}); mapHasEntries([]); }); QUnit.test("copy and then update", function() { map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); var map2 = map.copy(); map2.set(object, "losing"); map2.set(number, "losing"); map2.set(string, "losing"); mapHasEntries([ [object, "winning"], [number, "winning"], [string, "winning"] ]); mapHasEntries([ [object, "losing"], [number, "losing"], [string, "losing"] ], map2); }); QUnit.test("copy and then delete", function() { map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); var map2 = map.copy(); map2["delete"](object); map2["delete"](number); map2["delete"](string); mapHasEntries([ [object, "winning"], [number, "winning"], [string, "winning"] ]); mapHasEntries([], map2); }); if (define_property.hasPropertyAccessors) { QUnit.test("length", function() { expectDeprecation('Usage of `length` is deprecated, use `size` instead.'); //Add a key twice equal(map.length, 0); map.set(string, "a string"); equal(map.length, 1); map.set(string, "the same string"); equal(map.length, 1); //Add another map.set(number, "a number"); equal(map.length, 2); //Remove one that doesn't exist map["delete"]('does not exist'); equal(map.length, 2); //Check copy var copy = map.copy(); equal(copy.length, 2); //Remove a key twice map["delete"](number); equal(map.length, 1); map["delete"](number); equal(map.length, 1); //Remove the last key map["delete"](string); equal(map.length, 0); map["delete"](string); equal(map.length, 0); }); } QUnit.test("size", function() { //Add a key twice equal(map.size, 0); map.set(string, "a string"); equal(map.size, 1); map.set(string, "the same string"); equal(map.size, 1); //Add another map.set(number, "a number"); equal(map.size, 2); //Remove one that doesn't exist map["delete"]('does not exist'); equal(map.size, 2); //Check copy var copy = map.copy(); equal(copy.size, 2); //Remove a key twice map["delete"](number); equal(map.size, 1); map["delete"](number); equal(map.size, 1); //Remove the last key map["delete"](string); equal(map.size, 0); map["delete"](string); equal(map.size, 0); }); QUnit.test("forEach without proper callback", function() { QUnit["throws"](function() { map.forEach(); }, '[object Undefined] is not a function'); QUnit["throws"](function() { map.forEach(undefined); }, '[object Undefined] is not a function'); QUnit["throws"](function() { map.forEach(1); }, '[object Number] is not a function'); QUnit["throws"](function() { map.forEach({}); }, '[object Object] is not a function'); map.forEach(function(value, key) { map["delete"](key); }); // ensure the error happens even if no data is present equal(map.size, 0); QUnit["throws"](function() { map.forEach({}); }, '[object Object] is not a function'); }); QUnit.test("forEach basic", function() { map.set("a", 1); map.set("b", 2); map.set("c", 3); var iteration = 0; var expectations = [ { value: 1, key: "a", context: unboundThis }, { value: 2, key: "b", context: unboundThis }, { value: 3, key: "c", context: unboundThis } ]; map.forEach(function(value, key, theMap) { var expectation = expectations[iteration]; equal(value, expectation.value, 'value should be correct'); equal(key, expectation.key, 'key should be correct'); equal(this, expectation.context, 'context should be as if it was unbound'); equal(map, theMap, 'map being iterated over should be passed in'); iteration++; }); equal(iteration, 3, 'expected 3 iterations'); }); QUnit.test("forEach basic /w context", function() { map.set("a", 1); map.set("b", 2); map.set("c", 3); var iteration = 0; var context = {}; var expectations = [ { value: 1, key: "a", context: context }, { value: 2, key: "b", context: context }, { value: 3, key: "c", context: context } ]; map.forEach(function(value, key, theMap) { var expectation = expectations[iteration]; equal(value, expectation.value, 'value should be correct'); equal(key, expectation.key, 'key should be correct'); equal(this, expectation.context, 'context should be as if it was unbound'); equal(map, theMap, 'map being iterated over should be passed in'); iteration++; }, context); equal(iteration, 3, 'expected 3 iterations'); }); QUnit.test("forEach basic /w deletion while enumerating", function() { map.set("a", 1); map.set("b", 2); map.set("c", 3); var iteration = 0; var expectations = [ { value: 1, key: "a", context: unboundThis }, { value: 2, key: "b", context: unboundThis } ]; map.forEach(function(value, key, theMap) { if (iteration === 0) { map["delete"]("c"); } var expectation = expectations[iteration]; equal(value, expectation.value, 'value should be correct'); equal(key, expectation.key, 'key should be correct'); equal(this, expectation.context, 'context should be as if it was unbound'); equal(map, theMap, 'map being iterated over should be passed in'); iteration++; }); equal(iteration, 2, 'expected 3 iterations'); }); QUnit.test("forEach basic /w addition while enumerating", function() { map.set("a", 1); map.set("b", 2); map.set("c", 3); var iteration = 0; var expectations = [ { value: 1, key: "a", context: unboundThis }, { value: 2, key: "b", context: unboundThis }, { value: 3, key: "c", context: unboundThis }, { value: 4, key: "d", context: unboundThis } ]; map.forEach(function(value, key, theMap) { if (iteration === 0) { map.set('d', 4); } var expectation = expectations[iteration]; equal(value, expectation.value, 'value should be correct'); equal(key, expectation.key, 'key should be correct'); equal(this, expectation.context, 'context should be as if it was unbound'); equal(map, theMap, 'map being iterated over should be passed in'); iteration++; }); equal(iteration, 4, 'expected 3 iterations'); }); QUnit.test("clear", function() { var iterations = 0; map.set("a", 1); map.set("b", 2); map.set("c", 3); map.set("d", 4); equal(map.size, 4); map.forEach(function() { iterations++; }); equal(iterations, 4); map.clear(); equal(map.size, 0); iterations = 0; map.forEach(function() { iterations++; }); equal(iterations, 0); }); QUnit.test("-0", function() { equal(map.has(-0), false); equal(map.has(0), false); map.set(-0, 'zero'); equal(map.has(-0), true); equal(map.has(0), true); equal(map.get(0), 'zero'); equal(map.get(-0), 'zero'); map.forEach(function(value, key) { equal(1/key, Infinity, 'spec says key should be positive zero'); }); }); QUnit.test("NaN", function() { equal(map.has(NaN), false); map.set(NaN, 'not-a-number'); equal(map.has(NaN), true); equal(map.get(NaN), 'not-a-number'); }); QUnit.test("NaN Boxed", function() { //jshint -W053 var boxed = new Number(NaN); equal(map.has(boxed), false); map.set(boxed, 'not-a-number'); equal(map.has(boxed), true); equal(map.has(NaN), false); equal(map.get(NaN), undefined); equal(map.get(boxed), 'not-a-number'); }); QUnit.test("0 value", function() { var obj = {}; equal(map.has(obj), false); equal(map.size, 0); map.set(obj, 0); equal(map.size, 1); equal(map.has(obj), true); equal(map.get(obj), 0); map["delete"](obj); equal(map.has(obj), false); equal(map.get(obj), undefined); equal(map.size, 0); }); } for (var i = 0; i < varieties.length; i++) { testMap(varieties[i]); } QUnit.module("MapWithDefault - default values"); QUnit.test("Retrieving a value that has not been set returns and sets a default value", function() { var map = ember_metal__map.MapWithDefault.create({ defaultValue: function(key) { return [key]; } }); var value = map.get('ohai'); deepEqual(value, ['ohai']); strictEqual(value, map.get('ohai')); }); QUnit.test("Map.prototype.constructor", function() { var map = new ember_metal__map.Map(); equal(map.constructor, ember_metal__map.Map); }); QUnit.test("MapWithDefault.prototype.constructor", function() { var map = new ember_metal__map.MapWithDefault({ defaultValue: function(key) { return key; } }); equal(map.constructor, ember_metal__map.MapWithDefault); }); QUnit.test("Copying a MapWithDefault copies the default value", function() { var map = ember_metal__map.MapWithDefault.create({ defaultValue: function(key) { return [key]; } }); map.set('ohai', 1); map.get('bai'); var map2 = map.copy(); equal(map2.get('ohai'), 1); deepEqual(map2.get('bai'), ['bai']); map2.set('kthx', 3); deepEqual(map.get('kthx'), ['kthx']); equal(map2.get('kthx'), 3); deepEqual(map2.get('default'), ['default']); map2.defaultValue = function(key) { return ['tom is on', key]; }; deepEqual(map2.get('drugs'), ['tom is on', 'drugs']); }); QUnit.module("OrderedSet", { setup: function() { object = {}; number = 42; string = "foo"; map = ember_metal__map.OrderedSet.create(); } }); QUnit.test("add returns the set", function() { var obj = {}; equal(map.add(obj), map); equal(map.add(obj), map, 'when it is already in the set'); }); }); enifed('ember-metal/tests/map_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/map_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/map_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/map_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/map_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/map_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/alias_method_test', ['ember-metal/property_get', 'ember-metal/mixin'], function (property_get, mixin) { 'use strict'; QUnit.module('aliasMethod'); function validateAliasMethod(obj) { equal(obj.fooMethod(), 'FOO', 'obj.fooMethod()'); equal(obj.barMethod(), 'FOO', 'obj.barMethod should be a copy of foo'); } QUnit.test('methods of another name are aliased when the mixin is applied', function() { var MyMixin = mixin.Mixin.create({ fooMethod: function() { return 'FOO'; }, barMethod: mixin.aliasMethod('fooMethod') }); var obj = MyMixin.apply({}); validateAliasMethod(obj); }); QUnit.test('should follow aliasMethods all the way down', function() { var MyMixin = mixin.Mixin.create({ bar: mixin.aliasMethod('foo'), // put first to break ordered iteration baz: function() { return 'baz'; }, foo: mixin.aliasMethod('baz') }); var obj = MyMixin.apply({}); equal(property_get.get(obj, 'bar')(), 'baz', 'should have followed aliasMethods'); }); QUnit.test('should alias methods from other dependent mixins', function() { var BaseMixin = mixin.Mixin.create({ fooMethod: function() { return 'FOO'; } }); var MyMixin = mixin.Mixin.create(BaseMixin, { barMethod: mixin.aliasMethod('fooMethod') }); var obj = MyMixin.apply({}); validateAliasMethod(obj); }); QUnit.test('should alias methods from other mixins applied at same time', function() { var BaseMixin = mixin.Mixin.create({ fooMethod: function() { return 'FOO'; } }); var MyMixin = mixin.Mixin.create({ barMethod: mixin.aliasMethod('fooMethod') }); var obj = mixin.mixin({}, BaseMixin, MyMixin); validateAliasMethod(obj); }); QUnit.test('should alias methods from mixins already applied on object', function() { var BaseMixin = mixin.Mixin.create({ quxMethod: function() { return 'qux'; } }); var MyMixin = mixin.Mixin.create({ bar: mixin.aliasMethod('foo'), barMethod: mixin.aliasMethod('fooMethod') }); var obj = { fooMethod: function() { return 'FOO'; } }; BaseMixin.apply(obj); MyMixin.apply(obj); validateAliasMethod(obj); }); }); enifed('ember-metal/tests/mixin/alias_method_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/alias_method_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/alias_method_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/alias_method_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/alias_method_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/alias_method_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/apply_test', ['ember-metal/property_get', 'ember-metal/mixin'], function (get, mixin) { 'use strict'; QUnit.module('Ember.Mixin.apply'); function K() {} QUnit.test('using apply() should apply properties', function() { var MixinA = mixin.Mixin.create({ foo: 'FOO', baz: K }); var obj = {}; mixin.mixin(obj, MixinA); equal(get['default'](obj, 'foo'), "FOO", 'should apply foo'); equal(get['default'](obj, 'baz'), K, 'should apply foo'); }); QUnit.test('applying anonymous properties', function() { var obj = {}; mixin.mixin(obj, { foo: 'FOO', baz: K }); equal(get['default'](obj, 'foo'), "FOO", 'should apply foo'); equal(get['default'](obj, 'baz'), K, 'should apply foo'); }); QUnit.test('applying null values', function() { expectAssertion(function() { mixin.mixin({}, null); }); }); QUnit.test('applying a property with an undefined value', function() { var obj = { tagName: '' }; mixin.mixin(obj, { tagName: undefined }); strictEqual(get['default'](obj, 'tagName'), ''); }); }); enifed('ember-metal/tests/mixin/apply_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/apply_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/apply_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/apply_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/apply_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/apply_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/computed_test', ['ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/mixin', 'ember-metal/computed', 'ember-metal/properties'], function (property_get, property_set, mixin, computed, properties) { 'use strict'; function K() { return this; } QUnit.module('Mixin Computed Properties'); QUnit.test('overriding computed properties', function() { var MixinA, MixinB, MixinC, MixinD; var obj; MixinA = mixin.Mixin.create({ aProp: computed.computed(function() { return 'A'; }) }); MixinB = mixin.Mixin.create(MixinA, { aProp: computed.computed(function() { return this._super.apply(this, arguments)+'B'; }) }); MixinC = mixin.Mixin.create(MixinA, { aProp: computed.computed(function() { return this._super.apply(this, arguments)+'C'; }) }); MixinD = mixin.Mixin.create({ aProp: computed.computed(function() { return this._super.apply(this, arguments)+'D'; }) }); obj = {}; MixinB.apply(obj); equal(property_get.get(obj, 'aProp'), 'AB', "should expose super for B"); obj = {}; MixinC.apply(obj); equal(property_get.get(obj, 'aProp'), 'AC', "should expose super for C"); obj = {}; MixinA.apply(obj); MixinD.apply(obj); equal(property_get.get(obj, 'aProp'), 'AD', "should define super for D"); obj = { }; properties.defineProperty(obj, 'aProp', computed.computed(function(key, value) { return 'obj'; })); MixinD.apply(obj); equal(property_get.get(obj, 'aProp'), "objD", "should preserve original computed property"); }); QUnit.test('calling set on overridden computed properties', function() { var SuperMixin, SubMixin; var obj; var superGetOccurred = false; var superSetOccurred = false; SuperMixin = mixin.Mixin.create({ aProp: computed.computed(function(key, val) { if (arguments.length === 1) { superGetOccurred = true; } else { superSetOccurred = true; } return true; }) }); SubMixin = mixin.Mixin.create(SuperMixin, { aProp: computed.computed(function(key, val) { return this._super.apply(this, arguments); }) }); obj = {}; SubMixin.apply(obj); property_set.set(obj, 'aProp', 'set thyself'); ok(superSetOccurred, 'should pass set to _super'); superSetOccurred = false; // reset the set assertion obj = {}; SubMixin.apply(obj); property_get.get(obj, 'aProp'); ok(superGetOccurred, 'should pass get to _super'); property_set.set(obj, 'aProp', 'set thyself'); ok(superSetOccurred, 'should pass set to _super after getting'); }); QUnit.test('setter behavior works properly when overriding computed properties', function() { var obj = {}; var MixinA = mixin.Mixin.create({ cpWithSetter2: computed.computed(K), cpWithSetter3: computed.computed(K), cpWithoutSetter: computed.computed(K) }); var cpWasCalled = false; var MixinB = mixin.Mixin.create({ cpWithSetter2: computed.computed(function(k, v) { cpWasCalled = true; }), cpWithSetter3: computed.computed(function(k, v) { cpWasCalled = true; }), cpWithoutSetter: computed.computed(function(k) { cpWasCalled = true; }) }); MixinA.apply(obj); MixinB.apply(obj); property_set.set(obj, 'cpWithSetter2', 'test'); ok(cpWasCalled, "The computed property setter was called when defined with two args"); cpWasCalled = false; property_set.set(obj, 'cpWithSetter3', 'test'); ok(cpWasCalled, "The computed property setter was called when defined with three args"); cpWasCalled = false; property_set.set(obj, 'cpWithoutSetter', 'test'); equal(property_get.get(obj, 'cpWithoutSetter'), 'test', "The default setter was called, the value is correct"); ok(!cpWasCalled, "The default setter was called, not the CP itself"); }); }); enifed('ember-metal/tests/mixin/computed_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/computed_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/computed_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/computed_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/computed_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/computed_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/concatenated_properties_test', ['ember-metal/property_get', 'ember-metal/mixin'], function (get, mixin) { 'use strict'; QUnit.module('Mixin concatenatedProperties'); QUnit.test('defining concatenated properties should concat future version', function() { var MixinA = mixin.Mixin.create({ concatenatedProperties: ['foo'], foo: ['a', 'b', 'c'] }); var MixinB = mixin.Mixin.create({ foo: ['d', 'e', 'f'] }); var obj = mixin.mixin({}, MixinA, MixinB); deepEqual(get['default'](obj, 'foo'), ['a', 'b', 'c', 'd', 'e', 'f']); }); QUnit.test('defining concatenated properties should concat future version', function() { var MixinA = mixin.Mixin.create({ concatenatedProperties: null }); var MixinB = mixin.Mixin.create({ concatenatedProperties: null }); var obj = mixin.mixin({}, MixinA, MixinB); deepEqual(obj.concatenatedProperties, []); }); QUnit.test('concatenatedProperties should be concatenated', function() { var MixinA = mixin.Mixin.create({ concatenatedProperties: ['foo'], foo: ['a', 'b', 'c'] }); var MixinB = mixin.Mixin.create({ concatenatedProperties: 'bar', foo: ['d', 'e', 'f'], bar: [1,2,3] }); var MixinC = mixin.Mixin.create({ bar: [4,5,6] }); var obj = mixin.mixin({}, MixinA, MixinB, MixinC); deepEqual(get['default'](obj, 'concatenatedProperties'), ['foo', 'bar'], 'get concatenatedProperties'); deepEqual(get['default'](obj, 'foo'), ['a', 'b', 'c', 'd', 'e', 'f'], 'get foo'); deepEqual(get['default'](obj, 'bar'), [1,2,3,4,5,6], 'get bar'); }); QUnit.test('adding a prop that is not an array should make array', function() { var MixinA = mixin.Mixin.create({ concatenatedProperties: ['foo'], foo: [1,2,3] }); var MixinB = mixin.Mixin.create({ foo: 4 }); var obj = mixin.mixin({}, MixinA, MixinB); deepEqual(get['default'](obj, 'foo'), [1,2,3,4]); }); QUnit.test('adding a prop that is not an array should make array', function() { var MixinA = mixin.Mixin.create({ concatenatedProperties: ['foo'], foo: 'bar' }); var obj = mixin.mixin({}, MixinA); deepEqual(get['default'](obj, 'foo'), ['bar']); }); QUnit.test('adding a non-concatenable property that already has a defined value should result in an array with both values', function() { var mixinA = mixin.Mixin.create({ foo: 1 }); var mixinB = mixin.Mixin.create({ concatenatedProperties: ['foo'], foo: 2 }); var obj = mixin.mixin({}, mixinA, mixinB); deepEqual(get['default'](obj, 'foo'), [1, 2]); }); QUnit.test('adding a concatenable property that already has a defined value should result in a concatenated value', function() { var mixinA = mixin.Mixin.create({ foobar: 'foo' }); var mixinB = mixin.Mixin.create({ concatenatedProperties: ['foobar'], foobar: 'bar' }); var obj = mixin.mixin({}, mixinA, mixinB); equal(get['default'](obj, 'foobar'), 'foobar'); }); }); enifed('ember-metal/tests/mixin/concatenated_properties_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/concatenated_properties_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/concatenated_properties_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/concatenated_properties_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/concatenated_properties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/concatenated_properties_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/detect_test', ['ember-metal/mixin'], function (mixin) { 'use strict'; QUnit.module('Mixin.detect'); QUnit.test('detect() finds a directly applied mixin', function() { var MixinA = mixin.Mixin.create(); var obj = {}; equal(MixinA.detect(obj), false, 'MixinA.detect(obj) before apply()'); MixinA.apply(obj); equal(MixinA.detect(obj), true, 'MixinA.detect(obj) after apply()'); }); QUnit.test('detect() finds nested mixins', function() { var MixinA = mixin.Mixin.create({}); var MixinB = mixin.Mixin.create(MixinA); var obj = {}; equal(MixinA.detect(obj), false, 'MixinA.detect(obj) before apply()'); MixinB.apply(obj); equal(MixinA.detect(obj), true, 'MixinA.detect(obj) after apply()'); }); QUnit.test('detect() finds mixins on other mixins', function() { var MixinA = mixin.Mixin.create({}); var MixinB = mixin.Mixin.create(MixinA); equal(MixinA.detect(MixinB), true, 'MixinA is part of MixinB'); equal(MixinB.detect(MixinA), false, 'MixinB is not part of MixinA'); }); QUnit.test('detect handles null values', function() { var MixinA = mixin.Mixin.create(); equal(MixinA.detect(null), false); }); }); enifed('ember-metal/tests/mixin/detect_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/detect_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/detect_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/detect_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/detect_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/detect_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/introspection_test', ['ember-metal/utils', 'ember-metal/mixin', 'ember-metal/enumerable_utils'], function (utils, mixin, EnumerableUtils) { 'use strict'; // NOTE: A previous iteration differentiated between public and private props // as well as methods vs props. We are just keeping these for testing; the // current impl doesn't care about the differences as much... var PrivateProperty = mixin.Mixin.create({ _foo: '_FOO' }); var PublicProperty = mixin.Mixin.create({ foo: 'FOO' }); var PrivateMethod = mixin.Mixin.create({ _fooMethod: function() {} }); var PublicMethod = mixin.Mixin.create({ fooMethod: function() {} }); var BarProperties = mixin.Mixin.create({ _bar: '_BAR', bar: 'bar' }); var BarMethods = mixin.Mixin.create({ _barMethod: function() {}, barMethod: function() {} }); var Combined = mixin.Mixin.create(BarProperties, BarMethods); var obj; QUnit.module('Basic introspection', { setup: function() { obj = {}; mixin.mixin(obj, PrivateProperty, PublicProperty, PrivateMethod, PublicMethod, Combined); } }); QUnit.test('Ember.mixins()', function() { function mapGuids(ary) { return EnumerableUtils['default'].map(ary, function(x) { return utils.guidFor(x); }); } deepEqual(mapGuids(mixin.Mixin.mixins(obj)), mapGuids([PrivateProperty, PublicProperty, PrivateMethod, PublicMethod, Combined, BarProperties, BarMethods]), 'should return included mixins'); }); }); enifed('ember-metal/tests/mixin/introspection_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/introspection_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/introspection_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/introspection_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/introspection_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/introspection_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/merged_properties_test', ['ember-metal/property_get', 'ember-metal/mixin'], function (property_get, mixin) { 'use strict'; QUnit.module('Mixin mergedProperties'); QUnit.test('defining mergedProperties should merge future version', function() { var MixinA = mixin.Mixin.create({ mergedProperties: ['foo'], foo: { a: true, b: true, c: true } }); var MixinB = mixin.Mixin.create({ foo: { d: true, e: true, f: true } }); var obj = mixin.mixin({}, MixinA, MixinB); deepEqual(property_get.get(obj, 'foo'), { a: true, b: true, c: true, d: true, e: true, f: true }); }); QUnit.test('defining mergedProperties on future mixin should merged into past', function() { var MixinA = mixin.Mixin.create({ foo: { a: true, b: true, c: true } }); var MixinB = mixin.Mixin.create({ mergedProperties: ['foo'], foo: { d: true, e: true, f: true } }); var obj = mixin.mixin({}, MixinA, MixinB); deepEqual(property_get.get(obj, 'foo'), { a: true, b: true, c: true, d: true, e: true, f: true }); }); QUnit.test('defining mergedProperties with null properties should keep properties null', function() { var MixinA = mixin.Mixin.create({ mergedProperties: ['foo'], foo: null }); var MixinB = mixin.Mixin.create({ foo: null }); var obj = mixin.mixin({}, MixinA, MixinB); equal(property_get.get(obj, 'foo'), null); }); QUnit.test("mergedProperties' properties can get overwritten", function() { var MixinA = mixin.Mixin.create({ mergedProperties: ['foo'], foo: { a: 1 } }); var MixinB = mixin.Mixin.create({ foo: { a: 2 } }); var obj = mixin.mixin({}, MixinA, MixinB); deepEqual(property_get.get(obj, 'foo'), { a: 2 }); }); QUnit.test('mergedProperties should be concatenated', function() { var MixinA = mixin.Mixin.create({ mergedProperties: ['foo'], foo: { a: true, b: true, c: true } }); var MixinB = mixin.Mixin.create({ mergedProperties: 'bar', foo: { d: true, e: true, f: true }, bar: { a: true, l: true } }); var MixinC = mixin.Mixin.create({ bar: { e: true, x: true } }); var obj = mixin.mixin({}, MixinA, MixinB, MixinC); deepEqual(property_get.get(obj, 'mergedProperties'), ['foo', 'bar'], 'get mergedProperties'); deepEqual(property_get.get(obj, 'foo'), { a: true, b: true, c: true, d: true, e: true, f: true }, "get foo"); deepEqual(property_get.get(obj, 'bar'), { a: true, l: true, e: true, x: true }, "get bar"); }); QUnit.test("mergedProperties should exist even if not explicitly set on create", function() { var AnObj = Ember.Object.extend({ mergedProperties: ['options'], options: { a: 'a', b: { c: 'ccc' } } }); var obj = AnObj.create({ options: { a: 'A' } }); equal(property_get.get(obj, "options").a, 'A'); equal(property_get.get(obj, "options").b.c, 'ccc'); }); QUnit.test("mergedProperties' overwriting methods can call _super", function() { expect(4); var MixinA = mixin.Mixin.create({ mergedProperties: ['foo'], foo: { meth: function(a) { equal(a, "WOOT", "_super successfully called MixinA's `foo.meth` method"); return "WAT"; } } }); var MixinB = mixin.Mixin.create({ foo: { meth: function(a) { ok(true, "MixinB's `foo.meth` method called"); return this._super.apply(this, arguments); } } }); var MixinC = mixin.Mixin.create({ foo: { meth: function(a) { ok(true, "MixinC's `foo.meth` method called"); return this._super(a); } } }); var obj = mixin.mixin({}, MixinA, MixinB, MixinC); equal(obj.foo.meth("WOOT"), "WAT"); }); QUnit.test('Merging an Array should raise an error', function() { expect(1); var MixinA = mixin.Mixin.create({ mergedProperties: ['foo'], foo: { a: true, b: true, c: true } }); var MixinB = mixin.Mixin.create({ foo: ["a"] }); expectAssertion(function() { mixin.mixin({}, MixinA, MixinB); }, 'You passed in `["a"]` as the value for `foo` but `foo` cannot be an Array'); }); }); enifed('ember-metal/tests/mixin/merged_properties_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/merged_properties_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/merged_properties_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/merged_properties_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/merged_properties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/merged_properties_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/method_test', ['ember-metal/platform/create', 'ember-metal/mixin'], function (create, mixin) { 'use strict'; QUnit.module('Mixin Methods'); QUnit.test('defining simple methods', function() { var MixinA, obj, props; props = { publicMethod: function() { return 'publicMethod'; }, _privateMethod: function() { return 'privateMethod'; } }; MixinA = mixin.Mixin.create(props); obj = {}; MixinA.apply(obj); // but should be defined equal(props.publicMethod(), 'publicMethod', 'publicMethod is func'); equal(props._privateMethod(), 'privateMethod', 'privateMethod is func'); }); QUnit.test('overriding public methods', function() { var MixinA, MixinB, MixinD, MixinF, obj; MixinA = mixin.Mixin.create({ publicMethod: function() { return 'A'; } }); MixinB = mixin.Mixin.create(MixinA, { publicMethod: function() { return this._super.apply(this, arguments)+'B'; } }); MixinD = mixin.Mixin.create(MixinA, { publicMethod: function() { return this._super.apply(this, arguments)+'D'; } }); MixinF = mixin.Mixin.create({ publicMethod: function() { return this._super.apply(this, arguments)+'F'; } }); obj = {}; MixinB.apply(obj); equal(obj.publicMethod(), 'AB', 'should define super for A and B'); obj = {}; MixinD.apply(obj); equal(obj.publicMethod(), 'AD', 'should define super for A and B'); obj = {}; MixinA.apply(obj); MixinF.apply(obj); equal(obj.publicMethod(), 'AF', 'should define super for A and F'); obj = { publicMethod: function() { return 'obj'; } }; MixinF.apply(obj); equal(obj.publicMethod(), 'objF', 'should define super for F'); }); QUnit.test('overriding inherited objects', function() { var cnt = 0; var MixinA = mixin.Mixin.create({ foo: function() { cnt++; } }); var MixinB = mixin.Mixin.create({ foo: function() { this._super.apply(this, arguments); cnt++; } }); var objA = {}; MixinA.apply(objA); var objB = create['default'](objA); MixinB.apply(objB); cnt = 0; objB.foo(); equal(cnt, 2, 'should invoke both methods'); cnt = 0; objA.foo(); equal(cnt, 1, 'should not screw w/ parent obj'); }); QUnit.test('Including the same mixin more than once will only run once', function() { var cnt = 0; var MixinA = mixin.Mixin.create({ foo: function() { cnt++; } }); var MixinB = mixin.Mixin.create(MixinA, { foo: function() { this._super.apply(this, arguments); } }); var MixinC = mixin.Mixin.create(MixinA, { foo: function() { this._super.apply(this, arguments); } }); var MixinD = mixin.Mixin.create(MixinB, MixinC, MixinA, { foo: function() { this._super.apply(this, arguments); } }); var obj = {}; MixinD.apply(obj); MixinA.apply(obj); // try to apply again.. cnt = 0; obj.foo(); equal(cnt, 1, 'should invoke MixinA.foo one time'); }); QUnit.test('_super from a single mixin with no superclass does not error', function() { var MixinA = mixin.Mixin.create({ foo: function() { this._super.apply(this, arguments); } }); var obj = {}; MixinA.apply(obj); obj.foo(); ok(true); }); QUnit.test('_super from a first-of-two mixins with no superclass function does not error', function() { // _super was previously calling itself in the second assertion. // Use remaining count of calls to ensure it doesn't loop indefinitely. var remaining = 3; var MixinA = mixin.Mixin.create({ foo: function() { if (remaining-- > 0) { this._super.apply(this, arguments); } } }); var MixinB = mixin.Mixin.create({ foo: function() { this._super.apply(this, arguments); } }); var obj = {}; MixinA.apply(obj); MixinB.apply(obj); obj.foo(); ok(true); }); // .......................................................... // CONFLICTS // QUnit.module('Method Conflicts'); QUnit.test('overriding toString', function() { var MixinA = mixin.Mixin.create({ toString: function() { return 'FOO'; } }); var obj = {}; MixinA.apply(obj); equal(obj.toString(), 'FOO', 'should override toString w/o error'); obj = {}; mixin.mixin(obj, { toString: function() { return 'FOO'; } }); equal(obj.toString(), 'FOO', 'should override toString w/o error'); }); // .......................................................... // BUGS // QUnit.module('system/mixin/method_test BUGS'); QUnit.test('applying several mixins at once with sup already defined causes infinite loop', function() { var cnt = 0; var MixinA = mixin.Mixin.create({ foo: function() { cnt++; } }); var MixinB = mixin.Mixin.create({ foo: function() { this._super.apply(this, arguments); cnt++; } }); var MixinC = mixin.Mixin.create({ foo: function() { this._super.apply(this, arguments); cnt++; } }); var obj = {}; mixin.mixin(obj, MixinA); // sup already exists mixin.mixin(obj, MixinB, MixinC); // must be more than one mixin cnt = 0; obj.foo(); equal(cnt, 3, 'should invoke all 3 methods'); }); }); enifed('ember-metal/tests/mixin/method_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/method_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/method_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/method_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/method_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/method_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/observer_test', ['ember-metal/tests/props_helper', 'ember-metal/mixin', 'ember-metal/watching'], function (props_helper, mixin, watching) { 'use strict'; QUnit.module('Mixin observer'); props_helper.testBoth('global observer helper', function(get, set) { var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({}, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('global observer helper takes multiple params', function(get, set) { var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar', 'baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({}, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); set(obj, 'baz', "BAZ"); equal(get(obj, 'count'), 2, 'should invoke observer after change'); }); props_helper.testBoth('replacing observer should remove old observer', function(get, set) { var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var Mixin2 = mixin.Mixin.create({ foo: mixin.observer('baz', function() { set(this, 'count', get(this, 'count')+10); }) }); var obj = mixin.mixin({}, MyMixin, Mixin2); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); equal(get(obj, 'count'), 0, 'should not invoke observer after change'); set(obj, 'baz', "BAZ"); equal(get(obj, 'count'), 10, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with property before', function(get, set) { var obj2 = { baz: 'baz' }; var MyMixin = mixin.Mixin.create({ count: 0, bar: obj2, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({}, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with property after', function(get, set) { var obj2 = { baz: 'baz' }; var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }), bar: obj2 }); var obj = mixin.mixin({}, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with property in mixin applied later', function(get, set) { var obj2 = { baz: 'baz' }; var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var MyMixin2 = mixin.Mixin.create({ bar: obj2 }); var obj = mixin.mixin({}, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); MyMixin2.apply(obj); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with existing property', function(get, set) { var obj2 = { baz: 'baz' }; var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({ bar: obj2 }, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with property in mixin before', function(get, set) { var obj2 = { baz: 'baz' }; var MyMixin2 = mixin.Mixin.create({ bar: obj2 }); var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({}, MyMixin2, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with property in mixin after', function(get, set) { var obj2 = { baz: 'baz' }; var MyMixin2 = mixin.Mixin.create({ bar: obj2 }); var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({}, MyMixin, MyMixin2); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observing chain with overriden property', function(get, set) { var obj2 = { baz: 'baz' }; var obj3 = { baz: 'foo' }; var MyMixin2 = mixin.Mixin.create({ bar: obj3 }); var MyMixin = mixin.Mixin.create({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin.mixin({ bar: obj2 }, MyMixin, MyMixin2); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); equal(watching.isWatching(obj2, 'baz'), false, 'should not be watching baz'); equal(watching.isWatching(obj3, 'baz'), true, 'should be watching baz'); set(obj2, 'baz', "BAZ"); equal(get(obj, 'count'), 0, 'should not invoke observer after change'); set(obj3, 'baz', "BEAR"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); }); enifed('ember-metal/tests/mixin/observer_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/observer_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/observer_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/observer_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/observer_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/observer_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/reopen_test', ['ember-metal/run_loop', 'ember-metal/property_get', 'ember-runtime/system/object', 'ember-metal/mixin'], function (run, get, EmberObject, Mixin) { 'use strict'; QUnit.module('Ember.Mixin#reopen'); QUnit.test('using reopen() to add more properties to a simple', function() { var MixinA = Mixin['default'].create({ foo: 'FOO', baz: 'BAZ' }); MixinA.reopen({ bar: 'BAR', foo: 'FOO2' }); var obj = {}; MixinA.apply(obj); equal(get['default'](obj, 'foo'), 'FOO2', 'mixin() should override'); equal(get['default'](obj, 'baz'), 'BAZ', 'preserve MixinA props'); equal(get['default'](obj, 'bar'), 'BAR', 'include MixinB props'); }); QUnit.test('using reopen() and calling _super where there is not a super function does not cause infinite recursion', function() { var Taco = EmberObject['default'].extend({ createBreakfast: function() { // There is no original createBreakfast function. // Calling the wrapped _super function here // used to end in an infinite call loop this._super.apply(this, arguments); return "Breakfast!"; } }); Taco.reopen({ createBreakfast: function() { return this._super.apply(this, arguments); } }); var taco = Taco.create(); var result; run['default'](function() { try { result = taco.createBreakfast(); } catch(e) { result = "Your breakfast was interrupted by an infinite stack error."; } }); equal(result, "Breakfast!"); }); }); enifed('ember-metal/tests/mixin/reopen_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/reopen_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/reopen_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/reopen_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/reopen_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/reopen_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/required_test', ['ember-metal/mixin', 'ember-metal/property_get'], function (mixin, property_get) { 'use strict'; var PartialMixin, FinalMixin, obj; QUnit.module('Module.required', { setup: function() { PartialMixin = mixin.Mixin.create({ foo: mixin.required(), bar: 'BAR' }); FinalMixin = mixin.Mixin.create({ foo: 'FOO' }); obj = {}; }, teardown: function() { PartialMixin = FinalMixin = obj = null; } }); QUnit.test('applying a mixin to meet requirement', function() { FinalMixin.apply(obj); PartialMixin.apply(obj); equal(property_get.get(obj, 'foo'), 'FOO', 'should now be defined'); }); QUnit.test('combined mixins to meet requirement', function() { mixin.Mixin.create(PartialMixin, FinalMixin).apply(obj); equal(property_get.get(obj, 'foo'), 'FOO', 'should now be defined'); }); QUnit.test('merged mixin', function() { mixin.Mixin.create(PartialMixin, { foo: 'FOO' }).apply(obj); equal(property_get.get(obj, 'foo'), 'FOO', 'should now be defined'); }); QUnit.test('define property on source object', function() { obj.foo = 'FOO'; PartialMixin.apply(obj); equal(property_get.get(obj, 'foo'), 'FOO', 'should now be defined'); }); QUnit.test('using apply', function() { mixin.mixin(obj, PartialMixin, { foo: 'FOO' }); equal(property_get.get(obj, 'foo'), 'FOO', 'should now be defined'); }); }); enifed('ember-metal/tests/mixin/required_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/required_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/required_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/required_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/required_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/required_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/mixin/without_test', ['ember-metal/mixin'], function (mixin) { 'use strict'; QUnit.test('without should create a new mixin excluding named properties', function() { var MixinA = mixin.Mixin.create({ foo: 'FOO', bar: 'BAR' }); var MixinB = MixinA.without('bar'); var obj = {}; MixinB.apply(obj); equal(obj.foo, 'FOO', 'should defined foo'); equal(obj.bar, undefined, 'should not define bar'); }); }); enifed('ember-metal/tests/mixin/without_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/without_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/mixin/without_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/mixin/without_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/without_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/without_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/observer_test', ['ember-metal/core', 'ember-metal/tests/props_helper', 'ember-metal/observer', 'ember-metal/property_events', 'ember-metal/platform/create', 'ember-metal/properties', 'ember-metal/computed', 'ember-metal/mixin', 'ember-metal/run_loop'], function (Ember, props_helper, observer, property_events, create, properties, computed, ember_metal__mixin, run) { 'use strict'; QUnit.module('addObserver'); props_helper.testBoth('observer should fire when property is modified', function(get, set) { var obj = {}; var count = 0; observer.addObserver(obj, 'foo', function() { equal(get(obj, 'foo'), 'bar', 'should invoke AFTER value changed'); count++; }); set(obj, 'foo', 'bar'); equal(count, 1, 'should have invoked observer'); }); props_helper.testBoth('observer should fire when dependent property is modified', function(get, set) { var obj = { bar: 'bar' }; properties.defineProperty(obj, 'foo', computed.computed(function() { return get(this, 'bar').toUpperCase(); }).property('bar')); get(obj, 'foo'); var count = 0; observer.addObserver(obj, 'foo', function() { equal(get(obj, 'foo'), 'BAZ', 'should have invoked after prop change'); count++; }); set(obj, 'bar', 'baz'); equal(count, 1, 'should have invoked observer'); }); props_helper.testBoth('observer should continue to fire after dependent properties are accessed', function(get, set) { var observerCount = 0; var obj = {}; properties.defineProperty(obj, 'prop', computed.computed(function () { return Math.random(); })); properties.defineProperty(obj, 'anotherProp', computed.computed('prop', function () { return get(this, 'prop') + Math.random(); })); observer.addObserver(obj, 'prop', function () { observerCount++; }); get(obj, 'anotherProp'); for (var i = 0; i < 10; i++) { property_events.propertyWillChange(obj, 'prop'); property_events.propertyDidChange(obj, 'prop'); } equal(observerCount, 10, 'should continue to fire indefinitely'); }); if (Ember['default'].EXTEND_PROTOTYPES) { props_helper.testBoth('observer added declaratively via brace expansion should fire when property changes', function (get, set) { var obj = { }; var count = 0; ember_metal__mixin.mixin(obj, { observeFooAndBar: function () { count++; }.observes('{foo,bar}') }); set(obj, 'foo', 'foo'); equal(count, 1, 'observer specified via brace expansion invoked on property change'); set(obj, 'bar', 'bar'); equal(count, 2, 'observer specified via brace expansion invoked on property change'); set(obj, 'baz', 'baz'); equal(count, 2, 'observer not invoked on unspecified property'); }); props_helper.testBoth('observer specified declaratively via brace expansion should fire when dependent property changes', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; properties.defineProperty(obj, 'foo', computed.computed(function() { return get(this, 'bar').toLowerCase(); }).property('bar')); properties.defineProperty(obj, 'bar', computed.computed(function() { return get(this, 'baz').toUpperCase(); }).property('baz')); ember_metal__mixin.mixin(obj, { fooAndBarWatcher: function () { count++; }.observes('{foo,bar}') }); get(obj, 'foo'); set(obj, 'baz', 'Baz'); // fire once for foo, once for bar equal(count, 2, 'observer specified via brace expansion invoked on dependent property change'); set(obj, 'quux', 'Quux'); equal(count, 2, 'observer not fired on unspecified property'); }); } props_helper.testBoth('observers watching multiple properties via brace expansion should fire when the properties change', function (get, set) { var obj = { }; var count = 0; ember_metal__mixin.mixin(obj, { observeFooAndBar: ember_metal__mixin.observer('{foo,bar}', function () { count++; }) }); set(obj, 'foo', 'foo'); equal(count, 1, 'observer specified via brace expansion invoked on property change'); set(obj, 'bar', 'bar'); equal(count, 2, 'observer specified via brace expansion invoked on property change'); set(obj, 'baz', 'baz'); equal(count, 2, 'observer not invoked on unspecified property'); }); props_helper.testBoth('observers watching multiple properties via brace expansion should fire when dependent properties change', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; properties.defineProperty(obj, 'foo', computed.computed(function() { return get(this, 'bar').toLowerCase(); }).property('bar')); properties.defineProperty(obj, 'bar', computed.computed(function() { return get(this, 'baz').toUpperCase(); }).property('baz')); ember_metal__mixin.mixin(obj, { fooAndBarWatcher: ember_metal__mixin.observer('{foo,bar}', function () { count++; }) }); get(obj, 'foo'); set(obj, 'baz', 'Baz'); // fire once for foo, once for bar equal(count, 2, 'observer specified via brace expansion invoked on dependent property change'); set(obj, 'quux', 'Quux'); equal(count, 2, 'observer not fired on unspecified property'); }); props_helper.testBoth('nested observers should fire in order', function(get, set) { var obj = { foo: 'foo', bar: 'bar' }; var fooCount = 0; var barCount = 0; observer.addObserver(obj, 'foo', function() { fooCount++; }); observer.addObserver(obj, 'bar', function() { set(obj, 'foo', 'BAZ'); equal(fooCount, 1, 'fooCount should have fired already'); barCount++; }); set(obj, 'bar', 'BIFF'); equal(barCount, 1, 'barCount should have fired'); equal(fooCount, 1, 'foo should have fired'); }); props_helper.testBoth('removing an chain observer on change should not fail', function(get, set) { var foo = { bar: 'bar' }; var obj1 = { foo: foo }; var obj2 = { foo: foo }; var obj3 = { foo: foo }; var obj4 = { foo: foo }; var count1=0; var count2=0; var count3=0; var count4=0; function observer1() { count1++; } function observer2() { count2++; } function observer3() { count3++; observer.removeObserver(obj1, 'foo.bar', observer1); observer.removeObserver(obj2, 'foo.bar', observer2); observer.removeObserver(obj4, 'foo.bar', observer4); } function observer4() { count4++; } observer.addObserver(obj1, 'foo.bar' , observer1); observer.addObserver(obj2, 'foo.bar' , observer2); observer.addObserver(obj3, 'foo.bar' , observer3); observer.addObserver(obj4, 'foo.bar' , observer4); set(foo, 'bar', 'baz'); equal(count1, 1, 'observer1 fired'); equal(count2, 1, 'observer2 fired'); equal(count3, 1, 'observer3 fired'); equal(count4, 0, 'observer4 did not fire'); }); props_helper.testBoth('removing an chain before observer on change should not fail', function(get, set) { var foo = { bar: 'bar' }; var obj1 = { foo: foo }; var obj2 = { foo: foo }; var obj3 = { foo: foo }; var obj4 = { foo: foo }; var count1=0; var count2=0; var count3=0; var count4=0; function observer1() { count1++; } function observer2() { count2++; } function observer3() { count3++; observer.removeBeforeObserver(obj1, 'foo.bar', observer1); observer.removeBeforeObserver(obj2, 'foo.bar', observer2); observer.removeBeforeObserver(obj4, 'foo.bar', observer4); } function observer4() { count4++; } observer.addBeforeObserver(obj1, 'foo.bar' , observer1); observer.addBeforeObserver(obj2, 'foo.bar' , observer2); observer.addBeforeObserver(obj3, 'foo.bar' , observer3); observer.addBeforeObserver(obj4, 'foo.bar' , observer4); set(foo, 'bar', 'baz'); equal(count1, 1, 'observer1 fired'); equal(count2, 1, 'observer2 fired'); equal(count3, 1, 'observer3 fired'); equal(count4, 0, 'observer4 did not fire'); }); props_helper.testBoth('suspending an observer should not fire during callback', function(get, set) { var obj = {}; var target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; observer.addObserver(obj, 'foo', target, target.method); observer.addObserver(obj, 'foo', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); set(obj, 'foo', '2'); return 'result'; } set(obj, 'foo', '1'); equal(observer._suspendObserver(obj, 'foo', target, target.method, callback), 'result'); set(obj, 'foo', '3'); deepEqual(target.values, ['1', '3'], 'should invoke'); deepEqual(otherTarget.values, ['1', '2', '3'], 'should invoke'); }); props_helper.testBoth('suspending an observer should not defer change notifications during callback', function(get, set) { var obj = {}; var target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; observer.addObserver(obj, 'foo', target, target.method); observer.addObserver(obj, 'foo', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); set(obj, 'foo', '2'); return 'result'; } set(obj, 'foo', '1'); property_events.beginPropertyChanges(); equal(observer._suspendObserver(obj, 'foo', target, target.method, callback), 'result'); property_events.endPropertyChanges(); set(obj, 'foo', '3'); deepEqual(target.values, ['1', '3'], 'should invoke'); deepEqual(otherTarget.values, ['1', '2', '3'], 'should invoke'); }); props_helper.testBoth('suspending observers should not fire during callback', function(get, set) { var obj = {}; var target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; observer.addObserver(obj, 'foo', target, target.method); observer.addObserver(obj, 'foo', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); set(obj, 'foo', '2'); return 'result'; } set(obj, 'foo', '1'); equal(observer._suspendObservers(obj, ['foo'], target, target.method, callback), 'result'); set(obj, 'foo', '3'); deepEqual(target.values, ['1', '3'], 'should invoke'); deepEqual(otherTarget.values, ['1', '2', '3'], 'should invoke'); }); props_helper.testBoth('suspending observers should not defer change notifications during callback', function(get, set) { var obj = {}; var target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; observer.addObserver(obj, 'foo', target, target.method); observer.addObserver(obj, 'foo', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); set(obj, 'foo', '2'); return 'result'; } set(obj, 'foo', '1'); property_events.beginPropertyChanges(); equal(observer._suspendObservers(obj, ['foo'], target, target.method, callback), 'result'); property_events.endPropertyChanges(); set(obj, 'foo', '3'); deepEqual(target.values, ['1', '3'], 'should invoke'); deepEqual(otherTarget.values, ['1', '2', '3'], 'should invoke'); }); props_helper.testBoth('deferring property change notifications', function(get, set) { var obj = { foo: 'foo' }; var fooCount = 0; observer.addObserver(obj, 'foo', function() { fooCount++; }); property_events.beginPropertyChanges(obj); set(obj, 'foo', 'BIFF'); set(obj, 'foo', 'BAZ'); property_events.endPropertyChanges(obj); equal(fooCount, 1, 'foo should have fired once'); }); props_helper.testBoth('deferring property change notifications safely despite exceptions', function(get, set) { var obj = { foo: 'foo' }; var fooCount = 0; var exc = new Error("Something unexpected happened!"); expect(2); observer.addObserver(obj, 'foo', function() { fooCount++; }); try { property_events.changeProperties(function() { set(obj, 'foo', 'BIFF'); set(obj, 'foo', 'BAZ'); throw exc; }); } catch(err) { if (err !== exc) { throw err; } } equal(fooCount, 1, 'foo should have fired once'); property_events.changeProperties(function() { set(obj, 'foo', 'BIFF2'); set(obj, 'foo', 'BAZ2'); }); equal(fooCount, 2, 'foo should have fired again once'); }); props_helper.testBoth('deferring property change notifications will not defer before observers', function(get, set) { var obj = { foo: 'foo' }; var fooCount = 0; observer.addBeforeObserver(obj, 'foo', function() { fooCount++; }); property_events.beginPropertyChanges(obj); set(obj, 'foo', 'BIFF'); equal(fooCount, 1, 'should fire before observer immediately'); set(obj, 'foo', 'BAZ'); property_events.endPropertyChanges(obj); equal(fooCount, 1, 'should not fire before observer twice'); }); props_helper.testBoth('implementing sendEvent on object should invoke when deferring property change notifications ends', function(get, set) { var count = 0; var events = []; var obj = { sendEvent: function(eventName) { events.push(eventName); }, foo: 'baz' }; observer.addObserver(obj, 'foo', function() { count++; }); property_events.beginPropertyChanges(obj); set(obj, 'foo', 'BAZ'); equal(count, 0, 'should have not invoked observer'); equal(events.length, 1, 'should have invoked sendEvent for before'); property_events.endPropertyChanges(obj); equal(count, 1, 'should have invoked observer'); equal(events.length, 2, 'should have invoked sendEvent'); equal(events[0], 'foo:before'); equal(events[1], 'foo:change'); }); props_helper.testBoth('addObserver should propagate through prototype', function(get, set) { var obj = { foo: 'foo', count: 0 }; var obj2; observer.addObserver(obj, 'foo', function() { this.count++; }); obj2 = create['default'](obj); set(obj2, 'foo', 'bar'); equal(obj2.count, 1, 'should have invoked observer on inherited'); equal(obj.count, 0, 'should not have invoked observer on parent'); obj2.count = 0; set(obj, 'foo', 'baz'); equal(obj.count, 1, 'should have invoked observer on parent'); equal(obj2.count, 0, 'should not have invoked observer on inherited'); }); props_helper.testBoth('addObserver should respect targets with methods', function(get, set) { var observed = { foo: 'foo' }; var target1 = { count: 0, didChange: function(obj, keyName) { var value = get(obj, keyName); equal(this, target1, 'should invoke with this'); equal(obj, observed, 'param1 should be observed object'); equal(keyName, 'foo', 'param2 should be keyName'); equal(value, 'BAZ', 'param3 should new value'); this.count++; } }; var target2 = { count: 0, didChange: function(obj, keyName) { var value = get(obj, keyName); equal(this, target2, 'should invoke with this'); equal(obj, observed, 'param1 should be observed object'); equal(keyName, 'foo', 'param2 should be keyName'); equal(value, 'BAZ', 'param3 should new value'); this.count++; } }; observer.addObserver(observed, 'foo', target1, 'didChange'); observer.addObserver(observed, 'foo', target2, target2.didChange); set(observed, 'foo', 'BAZ'); equal(target1.count, 1, 'target1 observer should have fired'); equal(target2.count, 1, 'target2 observer should have fired'); }); props_helper.testBoth('addObserver should allow multiple objects to observe a property', function(get, set) { var observed = { foo: 'foo' }; var target1 = { count: 0, didChange: function(obj, keyName, value) { this.count++; } }; var target2 = { count: 0, didChange: function(obj, keyName, value) { this.count++; } }; observer.addObserver(observed, 'foo', target1, 'didChange'); observer.addObserver(observed, 'foo', target2, 'didChange'); set(observed, 'foo', 'BAZ'); equal(target1.count, 1, 'target1 observer should have fired'); equal(target2.count, 1, 'target2 observer should have fired'); }); // .......................................................... // REMOVE OBSERVER // QUnit.module('removeObserver'); props_helper.testBoth('removing observer should stop firing', function(get, set) { var obj = {}; var count = 0; function F() { count++; } observer.addObserver(obj, 'foo', F); set(obj, 'foo', 'bar'); equal(count, 1, 'should have invoked observer'); observer.removeObserver(obj, 'foo', F); set(obj, 'foo', 'baz'); equal(count, 1, "removed observer shouldn't fire"); }); props_helper.testBoth('local observers can be removed', function(get, set) { var barObserved = 0; var MyMixin = ember_metal__mixin.Mixin.create({ foo1: ember_metal__mixin.observer('bar', function() { barObserved++; }), foo2: ember_metal__mixin.observer('bar', function() { barObserved++; }) }); var obj = {}; MyMixin.apply(obj); set(obj, 'bar', 'HI!'); equal(barObserved, 2, 'precond - observers should be fired'); observer.removeObserver(obj, 'bar', null, 'foo1'); barObserved = 0; set(obj, 'bar', 'HI AGAIN!'); equal(barObserved, 1, 'removed observers should not be called'); }); props_helper.testBoth('removeObserver should respect targets with methods', function(get, set) { var observed = { foo: 'foo' }; var target1 = { count: 0, didChange: function() { this.count++; } }; var target2 = { count: 0, didChange: function() { this.count++; } }; observer.addObserver(observed, 'foo', target1, 'didChange'); observer.addObserver(observed, 'foo', target2, target2.didChange); set(observed, 'foo', 'BAZ'); equal(target1.count, 1, 'target1 observer should have fired'); equal(target2.count, 1, 'target2 observer should have fired'); observer.removeObserver(observed, 'foo', target1, 'didChange'); observer.removeObserver(observed, 'foo', target2, target2.didChange); target1.count = target2.count = 0; set(observed, 'foo', 'BAZ'); equal(target1.count, 0, 'target1 observer should not fire again'); equal(target2.count, 0, 'target2 observer should not fire again'); }); // .......................................................... // BEFORE OBSERVER // QUnit.module('addBeforeObserver'); props_helper.testBoth('observer should fire before a property is modified', function(get, set) { var obj = { foo: 'foo' }; var count = 0; observer.addBeforeObserver(obj, 'foo', function() { equal(get(obj, 'foo'), 'foo', 'should invoke before value changed'); count++; }); set(obj, 'foo', 'bar'); equal(count, 1, 'should have invoked observer'); }); props_helper.testBoth('observer should fire before dependent property is modified', function(get, set) { var obj = { bar: 'bar' }; properties.defineProperty(obj, 'foo', computed.computed(function() { return get(this, 'bar').toUpperCase(); }).property('bar')); get(obj, 'foo'); var count = 0; observer.addBeforeObserver(obj, 'foo', function() { equal(get(obj, 'foo'), 'BAR', 'should have invoked after prop change'); count++; }); set(obj, 'bar', 'baz'); equal(count, 1, 'should have invoked observer'); }); if (Ember['default'].EXTEND_PROTOTYPES) { props_helper.testBoth('before observer added declaratively via brace expansion should fire when property changes', function (get, set) { var obj = {}; var count = 0; ember_metal__mixin.mixin(obj, { fooAndBarWatcher: function () { count++; }.observesBefore('{foo,bar}') }); set(obj, 'foo', 'foo'); equal(count, 1, 'observer specified via brace expansion invoked on property change'); set(obj, 'bar', 'bar'); equal(count, 2, 'observer specified via brace expansion invoked on property change'); set(obj, 'baz', 'baz'); equal(count, 2, 'observer not invoked on unspecified property'); }); props_helper.testBoth('before observer specified declaratively via brace expansion should fire when dependent property changes', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; properties.defineProperty(obj, 'foo', computed.computed(function() { return get(this, 'bar').toLowerCase(); }).property('bar')); properties.defineProperty(obj, 'bar', computed.computed(function() { return get(this, 'baz').toUpperCase(); }).property('baz')); ember_metal__mixin.mixin(obj, { fooAndBarWatcher: function () { count++; }.observesBefore('{foo,bar}') }); get(obj, 'foo'); set(obj, 'baz', 'Baz'); // fire once for foo, once for bar equal(count, 2, 'observer specified via brace expansion invoked on dependent property change'); set(obj, 'quux', 'Quux'); equal(count, 2, 'observer not fired on unspecified property'); }); } props_helper.testBoth('before observer watching multiple properties via brace expansion should fire when properties change', function (get, set) { var obj = {}; var count = 0; ember_metal__mixin.mixin(obj, { fooAndBarWatcher: ember_metal__mixin.beforeObserver('{foo,bar}', function () { count++; }) }); set(obj, 'foo', 'foo'); equal(count, 1, 'observer specified via brace expansion invoked on property change'); set(obj, 'bar', 'bar'); equal(count, 2, 'observer specified via brace expansion invoked on property change'); set(obj, 'baz', 'baz'); equal(count, 2, 'observer not invoked on unspecified property'); }); props_helper.testBoth('before observer watching multiple properties via brace expansion should fire when dependent property changes', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; properties.defineProperty(obj, 'foo', computed.computed(function() { return get(this, 'bar').toLowerCase(); }).property('bar')); properties.defineProperty(obj, 'bar', computed.computed(function() { return get(this, 'baz').toUpperCase(); }).property('baz')); ember_metal__mixin.mixin(obj, { fooAndBarWatcher: ember_metal__mixin.beforeObserver('{foo,bar}', function () { count++; }) }); get(obj, 'foo'); set(obj, 'baz', 'Baz'); // fire once for foo, once for bar equal(count, 2, 'observer specified via brace expansion invoked on dependent property change'); set(obj, 'quux', 'Quux'); equal(count, 2, 'observer not fired on unspecified property'); }); props_helper.testBoth('addBeforeObserver should propagate through prototype', function(get, set) { var obj = { foo: 'foo', count: 0 }; var obj2; observer.addBeforeObserver(obj, 'foo', function() { this.count++; }); obj2 = create['default'](obj); set(obj2, 'foo', 'bar'); equal(obj2.count, 1, 'should have invoked observer on inherited'); equal(obj.count, 0, 'should not have invoked observer on parent'); obj2.count = 0; set(obj, 'foo', 'baz'); equal(obj.count, 1, 'should have invoked observer on parent'); equal(obj2.count, 0, 'should not have invoked observer on inherited'); }); props_helper.testBoth('addBeforeObserver should respect targets with methods', function(get, set) { var observed = { foo: 'foo' }; var target1 = { count: 0, willChange: function(obj, keyName) { var value = get(obj, keyName); equal(this, target1, 'should invoke with this'); equal(obj, observed, 'param1 should be observed object'); equal(keyName, 'foo', 'param2 should be keyName'); equal(value, 'foo', 'param3 should old value'); this.count++; } }; var target2 = { count: 0, willChange: function(obj, keyName) { var value = get(obj, keyName); equal(this, target2, 'should invoke with this'); equal(obj, observed, 'param1 should be observed object'); equal(keyName, 'foo', 'param2 should be keyName'); equal(value, 'foo', 'param3 should old value'); this.count++; } }; observer.addBeforeObserver(observed, 'foo', target1, 'willChange'); observer.addBeforeObserver(observed, 'foo', target2, target2.willChange); set(observed, 'foo', 'BAZ'); equal(target1.count, 1, 'target1 observer should have fired'); equal(target2.count, 1, 'target2 observer should have fired'); }); // .......................................................... // CHAINED OBSERVERS // var obj, count, lookup; var originalLookup = Ember['default'].lookup; QUnit.module('addObserver - dependentkey with chained properties', { setup: function() { obj = { foo: { bar: { baz: { biff: "BIFF" } } } }; Ember['default'].lookup = lookup = { Global: { foo: { bar: { baz: { biff: "BIFF" } } } } }; count = 0; }, teardown: function() { obj = count = null; Ember['default'].lookup = originalLookup; } }); props_helper.testBoth('depending on a chain with a computed property', function (get, set) { properties.defineProperty(obj, 'computed', computed.computed(function () { return { foo: 'bar' }; })); var changed = 0; observer.addObserver(obj, 'computed.foo', function () { changed++; }); equal(undefined, computed.cacheFor(obj, 'computed'), 'addObserver should not compute CP'); set(obj, 'computed.foo', 'baz'); equal(changed, 1, 'should fire observer'); }); props_helper.testBoth('depending on a simple chain', function(get, set) { var val; observer.addObserver(obj, 'foo.bar.baz.biff', function(target, key) { val = get(target, key); count++; }); set(get(obj, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(val, 'BUZZ'); equal(count, 1); set(get(obj, 'foo.bar'), 'baz', { biff: 'BLARG' }); equal(val, 'BLARG'); equal(count, 2); set(get(obj, 'foo'), 'bar', { baz: { biff: 'BOOM' } }); equal(val, 'BOOM'); equal(count, 3); set(obj, 'foo', { bar: { baz: { biff: 'BLARG' } } }); equal(val, 'BLARG'); equal(count, 4); set(get(obj, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(val, 'BUZZ'); equal(count, 5); var foo = get(obj, 'foo'); set(obj, 'foo', 'BOO'); equal(val, undefined); equal(count, 6); set(foo.bar.baz, 'biff', "BOOM"); equal(count, 6, 'should be not have invoked observer'); }); props_helper.testBoth('depending on a Global chain', function(get, set) { var Global = lookup.Global; var val; observer.addObserver(obj, 'Global.foo.bar.baz.biff', function(target, key) { val = get(lookup, key); count++; }); set(get(Global, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(val, 'BUZZ'); equal(count, 1); set(get(Global, 'foo.bar'), 'baz', { biff: 'BLARG' }); equal(val, 'BLARG'); equal(count, 2); set(get(Global, 'foo'), 'bar', { baz: { biff: 'BOOM' } }); equal(val, 'BOOM'); equal(count, 3); set(Global, 'foo', { bar: { baz: { biff: 'BLARG' } } }); equal(val, 'BLARG'); equal(count, 4); set(get(Global, 'foo.bar.baz'), 'biff', 'BUZZ'); equal(val, 'BUZZ'); equal(count, 5); var foo = get(obj, 'foo'); set(Global, 'foo', 'BOO'); equal(val, undefined); equal(count, 6); set(foo.bar.baz, 'biff', "BOOM"); equal(count, 6, 'should be not have invoked observer'); }); QUnit.module('removeBeforeObserver'); // .......................................................... // SETTING IDENTICAL VALUES // QUnit.module('props/observer_test - setting identical values'); props_helper.testBoth('setting simple prop should not trigger', function(get, set) { var obj = { foo: 'bar' }; var count = 0; observer.addObserver(obj, 'foo', function() { count++; }); set(obj, 'foo', 'bar'); equal(count, 0, 'should not trigger observer'); set(obj, 'foo', 'baz'); equal(count, 1, 'should trigger observer'); set(obj, 'foo', 'baz'); equal(count, 1, 'should not trigger observer again'); }); // The issue here is when a computed property is directly set with a value, then has a // dependent key change (which triggers a cache expiration and recomputation), observers will // not be fired if the CP setter is called with the last set value. props_helper.testBoth('setting a cached computed property whose value has changed should trigger', function(get, set) { var obj = {}; properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { if (arguments.length === 2) { return value; } return get(this, 'baz'); }).property('baz')); var count = 0; observer.addObserver(obj, 'foo', function() { count++; }); set(obj, 'foo', 'bar'); equal(count, 1); equal(get(obj, 'foo'), 'bar'); set(obj, 'baz', 'qux'); equal(count, 2); equal(get(obj, 'foo'), 'qux'); get(obj, 'foo'); set(obj, 'foo', 'bar'); equal(count, 3); equal(get(obj, 'foo'), 'bar'); }); QUnit.module("Ember.immediateObserver"); props_helper.testBoth("immediate observers should fire synchronously", function(get, set) { var obj = {}; var observerCalled = 0; var mixin; // explicitly create a run loop so we do not inadvertently // trigger deferred behavior run['default'](function() { mixin = ember_metal__mixin.Mixin.create({ fooDidChange: ember_metal__mixin.immediateObserver('foo', function() { observerCalled++; equal(get(this, 'foo'), "barbaz", "newly set value is immediately available"); }) }); mixin.apply(obj); properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { if (arguments.length > 1) { return value; } return "yes hello this is foo"; })); equal(get(obj, 'foo'), "yes hello this is foo", "precond - computed property returns a value"); equal(observerCalled, 0, "observer has not yet been called"); set(obj, 'foo', 'barbaz'); equal(observerCalled, 1, "observer was called once"); }); }); if (Ember['default'].EXTEND_PROTOTYPES) { props_helper.testBoth('immediate observers added declaratively via brace expansion fire synchronously', function (get, set) { var obj = {}; var observerCalled = 0; var mixin; // explicitly create a run loop so we do not inadvertently // trigger deferred behavior run['default'](function() { mixin = ember_metal__mixin.Mixin.create({ fooDidChange: function() { observerCalled++; equal(get(this, 'foo'), "barbaz", "newly set value is immediately available"); }.observesImmediately('{foo,bar}') }); mixin.apply(obj); properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { if (arguments.length > 1) { return value; } return "yes hello this is foo"; })); equal(get(obj, 'foo'), "yes hello this is foo", "precond - computed property returns a value"); equal(observerCalled, 0, "observer has not yet been called"); set(obj, 'foo', 'barbaz'); equal(observerCalled, 1, "observer was called once"); }); }); } props_helper.testBoth('immediate observers watching multiple properties via brace expansion fire synchronously', function (get, set) { var obj = {}; var observerCalled = 0; var mixin; // explicitly create a run loop so we do not inadvertently // trigger deferred behavior run['default'](function() { mixin = ember_metal__mixin.Mixin.create({ fooDidChange: ember_metal__mixin.immediateObserver('{foo,bar}', function() { observerCalled++; equal(get(this, 'foo'), "barbaz", "newly set value is immediately available"); }) }); mixin.apply(obj); properties.defineProperty(obj, 'foo', computed.computed(function(key, value) { if (arguments.length > 1) { return value; } return "yes hello this is foo"; })); equal(get(obj, 'foo'), "yes hello this is foo", "precond - computed property returns a value"); equal(observerCalled, 0, "observer has not yet been called"); set(obj, 'foo', 'barbaz'); equal(observerCalled, 1, "observer was called once"); }); }); props_helper.testBoth("immediate observers are for internal properties only", function(get, set) { expectAssertion(function() { ember_metal__mixin.immediateObserver('foo.bar', function() { return this; }); }, 'Immediate observers must observe internal properties only, not properties on other objects.'); }); QUnit.module("changeProperties"); props_helper.testBoth("observers added/removed during changeProperties should do the right thing.", function(get, set) { var obj = { foo: 0 }; function Observer() { this.willChangeCount = 0; this.didChangeCount = 0; } Observer.prototype = { add: function () { observer.addBeforeObserver(obj, 'foo', this, 'willChange'); observer.addObserver(obj, 'foo', this, 'didChange'); }, remove: function() { observer.removeBeforeObserver(obj, 'foo', this, 'willChange'); observer.removeObserver(obj, 'foo', this, 'didChange'); }, willChange: function () { this.willChangeCount++; }, didChange: function () { this.didChangeCount++; } }; var addedBeforeFirstChangeObserver = new Observer(); var addedAfterFirstChangeObserver = new Observer(); var addedAfterLastChangeObserver = new Observer(); var removedBeforeFirstChangeObserver = new Observer(); var removedBeforeLastChangeObserver = new Observer(); var removedAfterLastChangeObserver = new Observer(); removedBeforeFirstChangeObserver.add(); removedBeforeLastChangeObserver.add(); removedAfterLastChangeObserver.add(); property_events.changeProperties(function () { removedBeforeFirstChangeObserver.remove(); addedBeforeFirstChangeObserver.add(); set(obj, 'foo', 1); equal(addedBeforeFirstChangeObserver.willChangeCount, 1, 'addBeforeObserver called before the first change invoked immediately'); equal(addedBeforeFirstChangeObserver.didChangeCount, 0, 'addObserver called before the first change is deferred'); addedAfterFirstChangeObserver.add(); removedBeforeLastChangeObserver.remove(); set(obj, 'foo', 2); equal(addedAfterFirstChangeObserver.willChangeCount, 1, 'addBeforeObserver called after the first change invoked immediately'); equal(addedAfterFirstChangeObserver.didChangeCount, 0, 'addObserver called after the first change is deferred'); addedAfterLastChangeObserver.add(); removedAfterLastChangeObserver.remove(); }); equal(removedBeforeFirstChangeObserver.willChangeCount, 0, 'removeBeforeObserver called before the first change sees none'); equal(removedBeforeFirstChangeObserver.didChangeCount, 0, 'removeObserver called before the first change sees none'); equal(addedBeforeFirstChangeObserver.willChangeCount, 1, 'addBeforeObserver called before the first change sees only 1'); equal(addedBeforeFirstChangeObserver.didChangeCount, 1, 'addObserver called before the first change sees only 1'); equal(addedAfterFirstChangeObserver.willChangeCount, 1, 'addBeforeObserver called after the first change sees 1'); equal(addedAfterFirstChangeObserver.didChangeCount, 1, 'addObserver called after the first change sees 1'); equal(addedAfterLastChangeObserver.willChangeCount, 0, 'addBeforeObserver called after the last change sees none'); equal(addedAfterLastChangeObserver.didChangeCount, 0, 'addObserver called after the last change sees none'); equal(removedBeforeLastChangeObserver.willChangeCount, 1, 'removeBeforeObserver called before the last change still sees 1'); equal(removedBeforeLastChangeObserver.didChangeCount, 1, 'removeObserver called before the last change still sees 1'); equal(removedAfterLastChangeObserver.willChangeCount, 1, 'removeBeforeObserver called after the last change still sees 1'); equal(removedAfterLastChangeObserver.didChangeCount, 1, 'removeObserver called after the last change still sees 1'); }); }); enifed('ember-metal/tests/observer_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/observer_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/observer_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/observer_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/observer_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/observer_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/performance_test', ['ember-metal/property_set', 'ember-metal/property_get', 'ember-metal/computed', 'ember-metal/properties', 'ember-metal/property_events', 'ember-metal/observer'], function (property_set, property_get, computed, properties, property_events, observer) { 'use strict'; QUnit.module("Computed Properties - Number of times evaluated"); QUnit.test("computed properties that depend on multiple properties should run only once per run loop", function() { var obj = { a: 'a', b: 'b', c: 'c' }; var cpCount = 0; var obsCount = 0; properties.defineProperty(obj, 'abc', computed.computed(function(key) { cpCount++; return 'computed '+key; }).property('a', 'b', 'c')); property_get.get(obj, 'abc'); cpCount = 0; observer.addObserver(obj, 'abc', function() { obsCount++; }); property_events.beginPropertyChanges(); property_set.set(obj, 'a', 'aa'); property_set.set(obj, 'b', 'bb'); property_set.set(obj, 'c', 'cc'); property_events.endPropertyChanges(); property_get.get(obj, 'abc'); equal(cpCount, 1, "The computed property is only invoked once"); equal(obsCount, 1, "The observer is only invoked once"); }); QUnit.test("computed properties are not executed if they are the last segment of an observer chain pain", function() { var foo = { bar: { baz: { } } }; var count = 0; properties.defineProperty(foo.bar.baz, 'bam', computed.computed(function() { count++; })); observer.addObserver(foo, 'bar.baz.bam', function() {}); property_events.propertyDidChange(property_get.get(foo, 'bar.baz'), 'bam'); equal(count, 0, "should not have recomputed property"); }); }); enifed('ember-metal/tests/performance_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/performance_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/performance_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/performance_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/performance_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/performance_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/platform/create_test', ['ember-metal/platform/create'], function (create) { 'use strict'; QUnit.module("Ember.create()"); QUnit.test("should inherit the properties from the parent object", function() { var obj = { foo: 'FOO' }; var obj2 = create['default'](obj); ok(obj !== obj2, 'should be a new instance'); equal(obj2.foo, obj.foo, 'should inherit from parent'); obj2.foo = 'BAR'; equal(obj2.foo, 'BAR', 'should change foo'); equal(obj.foo, 'FOO', 'modifying obj2 should not modify obj'); }); // NOTE: jshint may interfere with this test since it defines its own Object.create if missing QUnit.test("passing additional property descriptors should define", function() { var obj = { foo: 'FOO', repl: 'obj' }; var obj2 = create['default'](obj, { bar: { value: 'BAR' }, repl: { value: 'obj2' } }); equal(obj2.bar, 'BAR', 'should have defined'); equal(obj2.repl, 'obj2', 'should have replaced parent'); }); QUnit.test("passing additional property descriptors should not pollute parent object", function() { var obj = { foo: 'FOO', repl: 'obj' }; var obj2 = create['default'](obj, { repl: { value: 'obj2' } }); notEqual(obj.repl, obj2.repl, 'should not pollute parent object'); }); }); enifed('ember-metal/tests/platform/create_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/platform'); test('ember-metal/tests/platform/create_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/platform/create_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/platform/create_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/platform'); test('ember-metal/tests/platform/create_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/platform/create_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/platform/define_property_test', ['ember-metal/platform/define_property', 'ember-metal/enumerable_utils'], function (define_property, EnumerableUtils) { 'use strict'; function isEnumerable(obj, keyName) { var keys = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); } } return EnumerableUtils['default'].indexOf(keys, keyName)>=0; } QUnit.module("defineProperty()"); QUnit.test("defining a simple property", function() { var obj = {}; define_property.defineProperty(obj, 'foo', { enumerable: true, writable: true, value: 'FOO' }); equal(obj.foo, 'FOO', 'should have added property'); obj.foo = "BAR"; equal(obj.foo, 'BAR', 'writable defined property should be writable'); equal(isEnumerable(obj, 'foo'), true, 'foo should be enumerable'); }); QUnit.test('defining a read only property', function() { var obj = {}; define_property.defineProperty(obj, 'foo', { enumerable: true, writable: false, value: 'FOO' }); equal(obj.foo, 'FOO', 'should have added property'); if (define_property.hasPropertyAccessors) { // cannot set read-only property in strict-mode try { obj.foo = "BAR"; } catch(e) { // do nothing (assertion still happens in finally) }finally { equal(obj.foo, 'FOO', 'real defined property should not be writable'); } } else { obj.foo = "BAR"; equal(obj.foo, 'BAR', 'simulated defineProperty should silently work'); } }); QUnit.test('defining a non enumerable property', function() { var obj = {}; define_property.defineProperty(obj, 'foo', { enumerable: false, writable: true, value: 'FOO' }); if (define_property.canDefineNonEnumerableProperties) { equal(isEnumerable(obj, 'foo'), false, 'real defineProperty will make property not-enumerable'); } else { equal(isEnumerable(obj, 'foo'), true, 'simulated defineProperty will leave properties enumerable'); } }); // If accessors don't exist, behavior that relies on getters // and setters don't do anything if (define_property.hasPropertyAccessors) { QUnit.test('defining a getter/setter', function() { var obj = {}; var getCnt = 0; var setCnt = 0; var v = 'FOO'; var desc = { enumerable: true, get: function() { getCnt++; return v; }, set: function(val) { setCnt++; v = val; } }; define_property.defineProperty(obj, 'foo', desc); equal(obj.foo, 'FOO', 'should return getter'); equal(getCnt, 1, 'should have invoked getter'); obj.foo = 'BAR'; equal(obj.foo, 'BAR', 'setter should have worked'); equal(setCnt, 1, 'should have invoked setter'); }); QUnit.test('defining getter/setter along with writable', function() { var obj ={}; throws(function() { define_property.defineProperty(obj, 'foo', { enumerable: true, get: function() {}, set: function() {}, writable: true }); }, Error, 'defining writable and get/set should throw exception'); }); QUnit.test('defining getter/setter along with value', function() { var obj ={}; throws(function() { define_property.defineProperty(obj, 'foo', { enumerable: true, get: function() {}, set: function() {}, value: 'FOO' }); }, Error, 'defining value and get/set should throw exception'); }); } }); enifed('ember-metal/tests/platform/define_property_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/platform'); test('ember-metal/tests/platform/define_property_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/platform/define_property_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/platform/define_property_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/platform'); test('ember-metal/tests/platform/define_property_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/platform/define_property_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/properties_test', ['ember-metal/platform/define_property', 'ember-metal/computed', 'ember-metal/properties', 'ember-metal/deprecate_property'], function (define_property, computed, properties, deprecate_property) { 'use strict'; QUnit.module('Ember.defineProperty'); QUnit.test('toString', function() { var obj = {}; properties.defineProperty(obj, 'toString', undefined, function() { return 'FOO'; }); equal(obj.toString(), 'FOO', 'should replace toString'); }); QUnit.test("for data properties, didDefineProperty hook should be called if implemented", function() { expect(2); var obj = { didDefineProperty: function(obj, keyName, value) { equal(keyName, 'foo', "key name should be foo"); equal(value, 'bar', "value should be bar"); } }; properties.defineProperty(obj, 'foo', undefined, "bar"); }); QUnit.test("for descriptor properties, didDefineProperty hook should be called if implemented", function() { expect(2); var computedProperty = computed.computed(function() { return this; }); var obj = { didDefineProperty: function(obj, keyName, value) { equal(keyName, 'foo', "key name should be foo"); strictEqual(value, computedProperty, "value should be passed descriptor"); } }; properties.defineProperty(obj, 'foo', computedProperty); }); if (define_property.hasPropertyAccessors) { QUnit.module('Ember.deprecateProperty'); QUnit.test("enables access to deprecated property and returns the value of the new property", function() { expect(3); var obj = { foo: 'bar' }; deprecate_property.deprecateProperty(obj, 'baz', 'foo'); expectDeprecation(); equal(obj.baz, obj.foo, 'baz and foo are equal'); obj.foo = 'blammo'; equal(obj.baz, obj.foo, 'baz and foo are equal'); }); QUnit.test("deprecatedKey is not enumerable", function() { expect(2); var obj = { foo: 'bar', blammo: 'whammy' }; deprecate_property.deprecateProperty(obj, 'baz', 'foo'); for (var prop in obj) { if (obj.hasOwnProperty(prop)) { notEqual(prop, 'baz'); } } }); QUnit.test("enables setter to deprecated property and updates the value of the new property", function() { expect(3); var obj = { foo: 'bar' }; deprecate_property.deprecateProperty(obj, 'baz', 'foo'); expectDeprecation(); obj.baz = 'bloop'; equal(obj.foo, 'bloop', 'updating baz updates foo'); equal(obj.baz, obj.foo, 'baz and foo are equal'); }); } }); enifed('ember-metal/tests/properties_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/properties_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/properties_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/properties_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/properties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/properties_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/props_helper', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set'], function (exports, Ember, property_get, property_set) { 'use strict'; var testBoth = function(testname, callback) { function emberget(x, y) { return property_get.get(x, y); } function emberset(x, y, z) { return property_set.set(x, y, z); } function aget(x, y) { return x[y]; } function aset(x, y, z) { return (x[y] = z); } QUnit.test(testname+' using getFromEmberMetal()/Ember.set()', function() { callback(emberget, emberset); }); QUnit.test(testname+' using accessors', function() { if (Ember['default'].USES_ACCESSORS) { callback(aget, aset); } else { ok('SKIPPING ACCESSORS'); } }); }; var testWithDefault = function(testname, callback) { function emberget(x, y) { return property_get.get(x, y); } function embergetwithdefault(x, y, z) { return property_get.getWithDefault(x, y, z); } function getwithdefault(x, y, z) { return x.getWithDefault(y, z); } function emberset(x, y, z) { return property_set.set(x, y, z); } function aget(x, y) { return x[y]; } function aset(x, y, z) { return (x[y] = z); } QUnit.test(testname+' using obj.get()', function() { callback(emberget, emberset); }); QUnit.test(testname+' using obj.getWithDefault()', function() { callback(getwithdefault, emberset); }); QUnit.test(testname+' using getFromEmberMetal()', function() { callback(emberget, emberset); }); QUnit.test(testname+' using Ember.getWithDefault()', function() { callback(embergetwithdefault, emberset); }); QUnit.test(testname+' using accessors', function() { if (Ember['default'].USES_ACCESSORS) { callback(aget, aset); } else { ok('SKIPPING ACCESSORS'); } }); }; exports.testWithDefault = testWithDefault; exports.testBoth = testBoth; }); enifed('ember-metal/tests/props_helper.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/props_helper.js should pass jscs', function() { ok(true, 'ember-metal/tests/props_helper.js should pass jscs.'); }); }); enifed('ember-metal/tests/props_helper.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/props_helper.js should pass jshint', function() { ok(true, 'ember-metal/tests/props_helper.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/add_queue_test', ['ember-metal/run_loop', 'ember-metal/array'], function (run, array) { 'use strict'; var originalQueues = run['default'].queues; var queues; QUnit.module('system/run_loop/add_queue_test', { setup: function() { run['default'].queues = queues = ['blork', 'bleep']; }, teardown: function() { run['default'].queues = originalQueues; } }); QUnit.test('adds a queue after a specified one', function() { run['default']._addQueue('testeroo', 'blork'); equal(array.indexOf.call(queues, 'testeroo'), 1, "new queue was added after specified queue"); }); QUnit.test('does not add the queue if it already exists', function() { run['default']._addQueue('testeroo', 'blork'); run['default']._addQueue('testeroo', 'blork'); equal(queues.length, 3, "queue was not added twice"); }); }); enifed('ember-metal/tests/run_loop/add_queue_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/add_queue_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/add_queue_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/add_queue_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/add_queue_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/add_queue_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/debounce_test', ['ember-metal/run_loop'], function (run) { 'use strict'; var originalDebounce = run['default'].backburner.debounce; var wasCalled = false; QUnit.module('Ember.run.debounce', { setup: function() { run['default'].backburner.debounce = function() { wasCalled = true; }; }, teardown: function() { run['default'].backburner.debounce = originalDebounce; } }); QUnit.test('Ember.run.debounce uses Backburner.debounce', function() { run['default'].debounce(function() {}); ok(wasCalled, 'Ember.run.debounce used'); }); }); enifed('ember-metal/tests/run_loop/debounce_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/debounce_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/debounce_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/debounce_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/debounce_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/debounce_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/later_test', ['ember-metal/is_none', 'ember-metal/run_loop'], function (isNone, run) { 'use strict'; var originalSetTimeout = window.setTimeout; var originalDateValueOf = Date.prototype.valueOf; function wait(callback, maxWaitCount) { maxWaitCount = isNone['default'](maxWaitCount) ? 100 : maxWaitCount; originalSetTimeout(function() { if (maxWaitCount > 0 && (run['default'].hasScheduledTimers() || run['default'].currentRunLoop)) { wait(callback, maxWaitCount - 1); return; } callback(); }, 10); } // Synchronous "sleep". This simulates work being done // after run.later was called but before the run loop // has flushed. In previous versions, this would have // caused the run.later callback to have run from // within the run loop flush, since by the time the // run loop has to flush, it would have considered // the timer already expired. function pauseUntil(time) { // jscs:disable while (+new Date() < time) { /* do nothing - sleeping */ } // jscs:enable } QUnit.module('run.later', { teardown: function() { window.setTimeout = originalSetTimeout; Date.prototype.valueOf = originalDateValueOf; } }); asyncTest('should invoke after specified period of time - function only', function() { var invoked = false; run['default'](function() { run['default'].later(function() { invoked = true; }, 100); }); wait(function() { QUnit.start(); equal(invoked, true, 'should have invoked later item'); }); }); asyncTest('should invoke after specified period of time - target/method', function() { var obj = { invoked: false }; run['default'](function() { run['default'].later(obj, function() { this.invoked = true; }, 100); }); wait(function() { QUnit.start(); equal(obj.invoked, true, 'should have invoked later item'); }); }); asyncTest('should invoke after specified period of time - target/method/args', function() { var obj = { invoked: 0 }; run['default'](function() { run['default'].later(obj, function(amt) { this.invoked += amt; }, 10, 100); }); wait(function() { QUnit.start(); equal(obj.invoked, 10, 'should have invoked later item'); }); }); asyncTest('should always invoke within a separate runloop', function() { var obj = { invoked: 0 }; var firstRunLoop, secondRunLoop; run['default'](function() { firstRunLoop = run['default'].currentRunLoop; run['default'].later(obj, function(amt) { this.invoked += amt; secondRunLoop = run['default'].currentRunLoop; }, 10, 1); pauseUntil(+new Date() + 100); }); ok(firstRunLoop, "first run loop captured"); ok(!run['default'].currentRunLoop, "shouldn't be in a run loop after flush"); equal(obj.invoked, 0, "shouldn't have invoked later item yet"); wait(function() { QUnit.start(); equal(obj.invoked, 10, "should have invoked later item"); ok(secondRunLoop, "second run loop took place"); ok(secondRunLoop !== firstRunLoop, "two different run loops took place"); }); }); // Our current implementation doesn't allow us to correctly enforce this ordering. // We should probably implement a queue to provide this guarantee. // See https://github.com/emberjs/ember.js/issues/3526 for more information. // asyncTest('callback order', function() { // var array = []; // function fn(val) { array.push(val); } // run(function() { // run.later(this, fn, 4, 5); // run.later(this, fn, 1, 1); // run.later(this, fn, 5, 10); // run.later(this, fn, 2, 3); // run.later(this, fn, 3, 3); // }); // deepEqual(array, []); // wait(function() { // QUnit.start(); // deepEqual(array, [1,2,3,4,5], 'callbacks were called in expected order'); // }); // }); // Out current implementation doesn't allow us to properly enforce what is tested here. // We should probably fix it, but it's not technically a bug right now. // See https://github.com/emberjs/ember.js/issues/3522 for more information. // asyncTest('callbacks coalesce into same run loop if expiring at the same time', function() { // var array = []; // function fn(val) { array.push(run.currentRunLoop); } // run(function() { // // Force +new Date to return the same result while scheduling // // run.later timers. Otherwise: non-determinism! // var now = +new Date(); // Date.prototype.valueOf = function() { return now; }; // run.later(this, fn, 10); // run.later(this, fn, 200); // run.later(this, fn, 200); // Date.prototype.valueOf = originalDateValueOf; // }); // deepEqual(array, []); // wait(function() { // QUnit.start(); // equal(array.length, 3, 'all callbacks called'); // ok(array[0] !== array[1], 'first two callbacks have different run loops'); // ok(array[0], 'first runloop present'); // ok(array[1], 'second runloop present'); // equal(array[1], array[2], 'last two callbacks got the same run loop'); // }); // }); asyncTest('inception calls to run.later should run callbacks in separate run loops', function() { var runLoop, finished; run['default'](function() { runLoop = run['default'].currentRunLoop; ok(runLoop); run['default'].later(function() { ok(run['default'].currentRunLoop && run['default'].currentRunLoop !== runLoop, 'first later callback has own run loop'); runLoop = run['default'].currentRunLoop; run['default'].later(function() { ok(run['default'].currentRunLoop && run['default'].currentRunLoop !== runLoop, 'second later callback has own run loop'); finished = true; }, 40); }, 40); }); wait(function() { QUnit.start(); ok(finished, 'all .later callbacks run'); }); }); asyncTest('setTimeout should never run with a negative wait', function() { // Rationale: The old run loop code was susceptible to an occasional // bug where invokeLaterTimers would be scheduled with a setTimeout // with a negative wait. Modern browsers normalize this to 0, but // older browsers (IE <= 8) break with a negative wait, which // happens when an expired timer callback takes a while to run, // which is what we simulate here. var newSetTimeoutUsed; window.setTimeout = function() { var wait = arguments[arguments.length - 1]; newSetTimeoutUsed = true; ok(!isNaN(wait) && wait >= 0, 'wait is a non-negative number'); // In IE8, `setTimeout.apply` is `undefined`. var apply = Function.prototype.apply; return apply.apply(originalSetTimeout, [this, arguments]); }; var count = 0; run['default'](function() { run['default'].later(function() { count++; // This will get run first. Waste some time. // This is intended to break invokeLaterTimers code by taking a // long enough time that other timers should technically expire. It's // fine that they're not called in this run loop; just need to // make sure that invokeLaterTimers doesn't end up scheduling // a negative setTimeout. pauseUntil(+new Date() + 60); }, 1); run['default'].later(function() { equal(count, 1, 'callbacks called in order'); }, 50); }); wait(function() { window.setTimeout = originalSetTimeout; QUnit.start(); ok(newSetTimeoutUsed, 'stub setTimeout was used'); }); }); }); enifed('ember-metal/tests/run_loop/later_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/later_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/later_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/later_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/later_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/later_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/next_test', ['ember-metal/run_loop'], function (run) { 'use strict'; QUnit.module('run.next'); asyncTest('should invoke immediately on next timeout', function() { var invoked = false; run['default'](function() { run['default'].next(function() { invoked = true; }); }); equal(invoked, false, 'should not have invoked yet'); setTimeout(function() { QUnit.start(); equal(invoked, true, 'should have invoked later item'); }, 20); }); asyncTest('callback should be called from within separate loop', function() { var firstRunLoop, secondRunLoop; run['default'](function() { firstRunLoop = run['default'].currentRunLoop; run['default'].next(function() { secondRunLoop = run['default'].currentRunLoop; }); }); setTimeout(function() { QUnit.start(); ok(secondRunLoop, 'callback was called from within run loop'); ok(firstRunLoop && secondRunLoop !== firstRunLoop, 'two separate run loops were invoked'); }, 20); }); asyncTest('multiple calls to run.next share coalesce callbacks into same run loop', function() { var firstRunLoop, secondRunLoop, thirdRunLoop; run['default'](function() { firstRunLoop = run['default'].currentRunLoop; run['default'].next(function() { secondRunLoop = run['default'].currentRunLoop; }); run['default'].next(function() { thirdRunLoop = run['default'].currentRunLoop; }); }); setTimeout(function() { QUnit.start(); ok(secondRunLoop && secondRunLoop === thirdRunLoop, 'callbacks coalesced into same run loop'); }, 20); }); }); enifed('ember-metal/tests/run_loop/next_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/next_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/next_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/next_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/next_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/next_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/once_test', ['ember-metal/run_loop'], function (run) { 'use strict'; QUnit.module('system/run_loop/once_test'); QUnit.test('calling invokeOnce more than once invokes only once', function() { var count = 0; run['default'](function() { var F = function() { count++; }; run['default'].once(F); run['default'].once(F); run['default'].once(F); }); equal(count, 1, 'should have invoked once'); }); QUnit.test('should differentiate based on target', function() { var A = { count: 0 }; var B = { count: 0 }; run['default'](function() { var F = function() { this.count++; }; run['default'].once(A, F); run['default'].once(B, F); run['default'].once(A, F); run['default'].once(B, F); }); equal(A.count, 1, 'should have invoked once on A'); equal(B.count, 1, 'should have invoked once on B'); }); QUnit.test('should ignore other arguments - replacing previous ones', function() { var A = { count: 0 }; var B = { count: 0 }; run['default'](function() { var F = function(amt) { this.count += amt; }; run['default'].once(A, F, 10); run['default'].once(B, F, 20); run['default'].once(A, F, 30); run['default'].once(B, F, 40); }); equal(A.count, 30, 'should have invoked once on A'); equal(B.count, 40, 'should have invoked once on B'); }); QUnit.test('should be inside of a runloop when running', function() { run['default'](function() { run['default'].once(function() { ok(!!run['default'].currentRunLoop, 'should have a runloop'); }); }); }); }); enifed('ember-metal/tests/run_loop/once_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/once_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/once_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/once_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/once_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/once_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/onerror_test', ['ember-metal', 'ember-metal/run_loop'], function (Ember, run) { 'use strict'; QUnit.module('system/run_loop/onerror_test'); QUnit.test('With Ember.onerror undefined, errors in Ember.run are thrown', function () { var thrown = new Error('Boom!'); var caught; try { run['default'](function() { throw thrown; }); } catch (error) { caught = error; } deepEqual(caught, thrown); }); QUnit.test('With Ember.onerror set, errors in Ember.run are caught', function () { var thrown = new Error('Boom!'); var caught; Ember['default'].onerror = function(error) { caught = error; }; run['default'](function() { throw thrown; }); deepEqual(caught, thrown); Ember['default'].onerror = undefined; }); }); enifed('ember-metal/tests/run_loop/onerror_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/onerror_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/onerror_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/onerror_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/onerror_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/onerror_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/run_bind_test', ['ember-metal/run_loop'], function (run) { 'use strict'; QUnit.module('system/run_loop/run_bind_test'); QUnit.test('Ember.run.bind builds a run-loop wrapped callback handler', function() { expect(3); var obj = { value: 0, increment: function(increment) { ok(run['default'].currentRunLoop, 'expected a run-loop'); return this.value += increment; } }; var proxiedFunction = run['default'].bind(obj, obj.increment, 1); equal(proxiedFunction(), 1); equal(obj.value, 1); }); QUnit.test('Ember.run.bind keeps the async callback arguments', function() { expect(4); var asyncCallback = function(increment, increment2, increment3) { ok(run['default'].currentRunLoop, 'expected a run-loop'); equal(increment, 1); equal(increment2, 2); equal(increment3, 3); }; var asyncFunction = function(fn) { fn(2, 3); }; asyncFunction(run['default'].bind(asyncCallback, asyncCallback, 1)); }); }); enifed('ember-metal/tests/run_loop/run_bind_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/run_bind_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/run_bind_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/run_bind_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/run_bind_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/run_bind_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/run_test', ['ember-metal/run_loop'], function (run) { 'use strict'; QUnit.module('system/run_loop/run_test'); QUnit.test('Ember.run invokes passed function, returning value', function() { var obj = { foo: function() { return [this.bar, 'FOO']; }, bar: 'BAR', checkArgs: function(arg1, arg2) { return [arg1, this.bar, arg2]; } }; equal(run['default'](function() { return 'FOO'; }), 'FOO', 'pass function only'); deepEqual(run['default'](obj, obj.foo), ['BAR', 'FOO'], 'pass obj and obj.method'); deepEqual(run['default'](obj, 'foo'), ['BAR', 'FOO'], 'pass obj and "method"'); deepEqual(run['default'](obj, obj.checkArgs, 'hello', 'world'), ['hello', 'BAR', 'world'], 'pass obj, obj.method, and extra arguments'); }); }); enifed('ember-metal/tests/run_loop/run_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/run_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/run_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/run_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/run_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/run_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/schedule_test', ['ember-metal/run_loop'], function (run) { 'use strict'; QUnit.module('system/run_loop/schedule_test'); QUnit.test('scheduling item in queue should defer until finished', function() { var cnt = 0; run['default'](function() { run['default'].schedule('actions', function() { cnt++; }); run['default'].schedule('actions', function() { cnt++; }); equal(cnt, 0, 'should not run action yet'); }); equal(cnt, 2, 'should flush actions now'); }); QUnit.test('nested runs should queue each phase independently', function() { var cnt = 0; run['default'](function() { run['default'].schedule('actions', function() { cnt++; }); equal(cnt, 0, 'should not run action yet'); run['default'](function() { run['default'].schedule('actions', function() { cnt++; }); }); equal(cnt, 1, 'should not run action yet'); }); equal(cnt, 2, 'should flush actions now'); }); QUnit.test('prior queues should be flushed before moving on to next queue', function() { var order = []; run['default'](function() { var runLoop = run['default'].currentRunLoop; ok(runLoop, 'run loop present'); run['default'].schedule('sync', function() { order.push('sync'); equal(runLoop, run['default'].currentRunLoop, 'same run loop used'); }); run['default'].schedule('actions', function() { order.push('actions'); equal(runLoop, run['default'].currentRunLoop, 'same run loop used'); run['default'].schedule('actions', function() { order.push('actions'); equal(runLoop, run['default'].currentRunLoop, 'same run loop used'); }); run['default'].schedule('sync', function() { order.push('sync'); equal(runLoop, run['default'].currentRunLoop, 'same run loop used'); }); }); run['default'].schedule('destroy', function() { order.push('destroy'); equal(runLoop, run['default'].currentRunLoop, 'same run loop used'); }); }); deepEqual(order, ['sync', 'actions', 'sync', 'actions', 'destroy']); }); QUnit.test('makes sure it does not trigger an autorun during testing', function() { expectAssertion(function() { run['default'].schedule('actions', function() {}); }, /wrap any code with asynchronous side-effects in a run/); // make sure not just the first violation is asserted. expectAssertion(function() { run['default'].schedule('actions', function() {}); }, /wrap any code with asynchronous side-effects in a run/); }); }); enifed('ember-metal/tests/run_loop/schedule_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/schedule_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/schedule_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/schedule_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/schedule_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/schedule_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/sync_test', ['ember-metal/run_loop'], function (run) { 'use strict'; QUnit.module('system/run_loop/sync_test'); QUnit.test('sync() will immediately flush the sync queue only', function() { var cnt = 0; run['default'](function() { function cntup() { cnt++; } function syncfunc() { if (++cnt<5) { run['default'].schedule('sync', syncfunc); } run['default'].schedule('actions', cntup); } syncfunc(); equal(cnt, 1, 'should not run action yet'); run['default'].sync(); equal(cnt, 5, 'should have run sync queue continuously'); }); equal(cnt, 10, 'should flush actions now too'); }); QUnit.test('calling sync() outside a run loop does not cause an error', function() { expect(0); run['default'].sync(); }); }); enifed('ember-metal/tests/run_loop/sync_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/sync_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/sync_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/sync_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/sync_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/sync_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/run_loop/unwind_test', ['ember-metal/run_loop', 'ember-metal/error'], function (run, EmberError) { 'use strict'; QUnit.module('system/run_loop/unwind_test'); QUnit.test('RunLoop unwinds despite unhandled exception', function() { var initialRunLoop = run['default'].currentRunLoop; throws(function() { run['default'](function() { run['default'].schedule('actions', function() { throw new EmberError['default']("boom!"); }); }); }, Error, "boom!"); // The real danger at this point is that calls to autorun will stick // tasks into the already-dead runloop, which will never get // flushed. I can't easily demonstrate this in a unit test because // autorun explicitly doesn't work in test mode. - ef4 equal(run['default'].currentRunLoop, initialRunLoop, "Previous run loop should be cleaned up despite exception"); // Prevent a failure in this test from breaking subsequent tests. run['default'].currentRunLoop = initialRunLoop; }); QUnit.test('run unwinds despite unhandled exception', function() { var initialRunLoop = run['default'].currentRunLoop; throws(function() { run['default'](function() { throw new EmberError['default']("boom!"); }); }, EmberError['default'], "boom!"); equal(run['default'].currentRunLoop, initialRunLoop, "Previous run loop should be cleaned up despite exception"); // Prevent a failure in this test from breaking subsequent tests. run['default'].currentRunLoop = initialRunLoop; }); }); enifed('ember-metal/tests/run_loop/unwind_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/unwind_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/run_loop/unwind_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/run_loop/unwind_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/unwind_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/unwind_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/set_properties_test', ['ember-metal/set_properties'], function (setProperties) { 'use strict'; QUnit.module('Ember.setProperties'); QUnit.test("supports setting multiple attributes at once", function() { deepEqual(setProperties['default'](null, null), null, 'noop for null properties and null object'); deepEqual(setProperties['default'](undefined, undefined), undefined, 'noop for undefined properties and undefined object'); deepEqual(setProperties['default']({}), {}, 'noop for no properties'); deepEqual(setProperties['default']({}, undefined), {}, 'noop for undefined'); deepEqual(setProperties['default']({}, null), {}, 'noop for null'); deepEqual(setProperties['default']({}, NaN), {}, 'noop for NaN'); deepEqual(setProperties['default']({}, {}), {}, 'meh'); deepEqual(setProperties['default']({}, { foo: 1 }), { foo: 1 }, 'Set a single property'); deepEqual(setProperties['default']({}, { foo: 1, bar: 1 }), { foo: 1, bar: 1 }, 'Set multiple properties'); deepEqual(setProperties['default']({ foo: 2, baz: 2 }, { foo: 1 }), { foo: 1, baz: 2 }, 'Set one of multiple properties'); deepEqual(setProperties['default']({ foo: 2, baz: 2 }, { bar: 2 }), { bar: 2, foo: 2, baz: 2 }, 'Set an additional, previously unset property'); }); }); enifed('ember-metal/tests/set_properties_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests'); test('ember-metal/tests/set_properties_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/set_properties_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/set_properties_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests'); test('ember-metal/tests/set_properties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/set_properties_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/streams/simple_stream_test', ['ember-metal/streams/stream', 'ember-metal/streams/simple'], function (Stream, SimpleStream) { 'use strict'; var source, value; QUnit.module('Simple Stream', { setup: function() { value = "zlurp"; source = new Stream['default'](function() { return value; }); source.setValue = function(_value) { value = _value; this.notify(); }; }, teardown: function() { value = undefined; source = undefined; } }); QUnit.test('supports a stream argument', function() { var stream = new SimpleStream['default'](source); equal(stream.value(), "zlurp"); stream.setValue("blorg"); equal(stream.value(), "blorg"); }); QUnit.test('supports a non-stream argument', function() { var stream = new SimpleStream['default'](value); equal(stream.value(), "zlurp"); stream.setValue("blorg"); equal(stream.value(), "zlurp"); }); }); enifed('ember-metal/tests/streams/simple_stream_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/streams'); test('ember-metal/tests/streams/simple_stream_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/streams/simple_stream_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/streams/simple_stream_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/streams'); test('ember-metal/tests/streams/simple_stream_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/streams/simple_stream_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/streams/stream_binding_test', ['ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/mixin', 'ember-metal/run_loop', 'ember-metal/streams/stream', 'ember-metal/streams/stream_binding'], function (property_get, property_set, mixin, run, Stream, StreamBinding) { 'use strict'; var source, value; QUnit.module('Stream Binding', { setup: function() { value = "zlurp"; source = new Stream['default'](function() { return value; }); source.setValue = function(_value) { value = _value; this.notify(); }; }, teardown: function() { value = undefined; source = undefined; } }); QUnit.test('basic', function() { var binding = new StreamBinding['default'](source); equal(binding.value(), "zlurp"); run['default'](function() { source.setValue("blorg"); }); equal(binding.value(), "blorg"); binding.destroy(); // destroy should not fail }); QUnit.test('the source stream can send values to a single subscriber', function() { var binding = new StreamBinding['default'](source); var obj = mixin.mixin({}, { toBinding: binding }); equal(property_get.get(obj, 'to'), "zlurp", "immediately syncs value forward on init"); run['default'](function() { source.setValue("blorg"); equal(property_get.get(obj, 'to'), "zlurp", "does not immediately sync value on set"); }); equal(property_get.get(obj, 'to'), "blorg", "value has synced after run loop"); }); QUnit.test('the source stream can send values to multiple subscribers', function() { var binding = new StreamBinding['default'](source); var obj1 = mixin.mixin({}, { toBinding: binding }); var obj2 = mixin.mixin({}, { toBinding: binding }); equal(property_get.get(obj1, 'to'), "zlurp", "immediately syncs value forward on init"); equal(property_get.get(obj2, 'to'), "zlurp", "immediately syncs value forward on init"); run['default'](function() { source.setValue("blorg"); equal(property_get.get(obj1, 'to'), "zlurp", "does not immediately sync value on set"); equal(property_get.get(obj2, 'to'), "zlurp", "does not immediately sync value on set"); }); equal(property_get.get(obj1, 'to'), "blorg", "value has synced after run loop"); equal(property_get.get(obj2, 'to'), "blorg", "value has synced after run loop"); }); QUnit.test('a subscriber can set the value on the source stream and notify the other subscribers', function() { var binding = new StreamBinding['default'](source); var obj1 = mixin.mixin({}, { toBinding: binding }); var obj2 = mixin.mixin({}, { toBinding: binding }); run['default'](function() { property_set.set(obj1, 'to', "blorg"); equal(property_get.get(obj2, 'to'), "zlurp", "does not immediately sync value on set"); equal(source.value(), "zlurp", "does not immediately sync value on set"); }); equal(property_get.get(obj2, 'to'), "blorg", "value has synced after run loop"); equal(source.value(), "blorg", "value has synced after run loop"); }); QUnit.test('if source and subscribers sync value, source wins', function() { var binding = new StreamBinding['default'](source); var obj1 = mixin.mixin({}, { toBinding: binding }); var obj2 = mixin.mixin({}, { toBinding: binding }); var obj3 = mixin.mixin({}, { toBinding: binding }); run['default'](function() { property_set.set(obj1, 'to', "blorg"); source.setValue("hoopla"); property_set.set(obj2, 'to', "flarp"); equal(property_get.get(obj3, 'to'), "zlurp", "does not immediately sync value on set"); }); equal(source.value(), "hoopla", "value has synced after run loop"); equal(property_get.get(obj1, 'to'), "hoopla", "value has synced after run loop"); equal(property_get.get(obj2, 'to'), "hoopla", "value has synced after run loop"); equal(property_get.get(obj3, 'to'), "hoopla", "value has synced after run loop"); }); QUnit.test('the last value sent by the source wins', function() { var binding = new StreamBinding['default'](source); var obj = mixin.mixin({}, { toBinding: binding }); run['default'](function() { source.setValue("blorg"); source.setValue("hoopla"); equal(property_get.get(obj, 'to'), "zlurp", "does not immediately sync value on set"); }); equal(source.value(), "hoopla", "value has synced after run loop"); equal(property_get.get(obj, 'to'), "hoopla", "value has synced after run loop"); }); QUnit.test('continues to notify subscribers after first consumption, even if not consumed', function() { var counter = 0; var binding = new StreamBinding['default'](source); binding.value(); binding.subscribe(function() { source.value(); counter++; }); equal(counter, 0); run['default'](function() { source.setValue("blorg"); equal(counter, 0); }); equal(counter, 1); run['default'](function() { source.setValue("hoopla"); source.setValue("zlurp"); equal(counter, 1); }); equal(counter, 2); }); }); enifed('ember-metal/tests/streams/stream_binding_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/streams'); test('ember-metal/tests/streams/stream_binding_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/streams/stream_binding_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/streams/stream_binding_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/streams'); test('ember-metal/tests/streams/stream_binding_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/streams/stream_binding_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/can_invoke_test', ['ember-metal/utils'], function (utils) { 'use strict'; var obj; QUnit.module("Ember.canInvoke", { setup: function() { obj = { foobar: "foobar", aMethodThatExists: function() {} }; }, teardown: function() { obj = undefined; } }); QUnit.test("should return false if the object doesn't exist", function() { equal(utils.canInvoke(undefined, 'aMethodThatDoesNotExist'), false); }); QUnit.test("should return true if the method exists on the object", function() { equal(utils.canInvoke(obj, 'aMethodThatExists'), true); }); QUnit.test("should return false if the method doesn't exist on the object", function() { equal(utils.canInvoke(obj, 'aMethodThatDoesNotExist'), false); }); QUnit.test("should return false if the property exists on the object but is a non-function", function() { equal(utils.canInvoke(obj, 'foobar'), false); }); }); enifed('ember-metal/tests/utils/can_invoke_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/can_invoke_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/can_invoke_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/can_invoke_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/can_invoke_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/can_invoke_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/generate_guid_test', ['ember-metal/utils'], function (utils) { 'use strict'; QUnit.module("Ember.generateGuid"); QUnit.test("Prefix", function() { var a = {}; ok(utils.generateGuid(a, 'tyrell').indexOf('tyrell') > -1, "guid can be prefixed"); }); }); enifed('ember-metal/tests/utils/generate_guid_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/generate_guid_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/generate_guid_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/generate_guid_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/generate_guid_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/generate_guid_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/guid_for_test', ['ember-metal/utils'], function (utils) { 'use strict'; QUnit.module("guidFor"); var sameGuid = function(a, b, message) { equal(utils.guidFor(a), utils.guidFor(b), message); }; var diffGuid = function(a, b, message) { ok(utils.guidFor(a) !== utils.guidFor(b), message); }; var nanGuid = function(obj) { var type = typeof obj; ok(isNaN(parseInt(utils.guidFor(obj), 0)), "guids for " + type + "don't parse to numbers"); }; QUnit.test("Object", function() { var a = {}; var b = {}; sameGuid(a, a, "same object always yields same guid"); diffGuid(a, b, "different objects yield different guids"); nanGuid(a); }); QUnit.test("strings", function() { var a = "string A"; var aprime = "string A"; var b = "String B"; sameGuid(a, a, "same string always yields same guid"); sameGuid(a, aprime, "identical strings always yield the same guid"); diffGuid(a, b, "different strings yield different guids"); nanGuid(a); }); QUnit.test("numbers", function() { var a = 23; var aprime = 23; var b = 34; sameGuid(a, a, "same numbers always yields same guid"); sameGuid(a, aprime, "identical numbers always yield the same guid"); diffGuid(a, b, "different numbers yield different guids"); nanGuid(a); }); QUnit.test("numbers", function() { var a = true; var aprime = true; var b = false; sameGuid(a, a, "same booleans always yields same guid"); sameGuid(a, aprime, "identical booleans always yield the same guid"); diffGuid(a, b, "different boolean yield different guids"); nanGuid(a); nanGuid(b); }); QUnit.test("null and undefined", function() { var a = null; var aprime = null; var b; sameGuid(a, a, "null always returns the same guid"); sameGuid(b, b, "undefined always returns the same guid"); sameGuid(a, aprime, "different nulls return the same guid"); diffGuid(a, b, "null and undefined return different guids"); nanGuid(a); nanGuid(b); }); QUnit.test("arrays", function() { var a = ["a", "b", "c"]; var aprime = ["a", "b", "c"]; var b = ["1", "2", "3"]; sameGuid(a, a, "same instance always yields same guid"); diffGuid(a, aprime, "identical arrays always yield the same guid"); diffGuid(a, b, "different arrays yield different guids"); nanGuid(a); }); }); enifed('ember-metal/tests/utils/guid_for_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/guid_for_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/guid_for_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/guid_for_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/guid_for_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/guid_for_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/is_array_test', ['ember-metal/utils'], function (utils) { 'use strict'; QUnit.module("Ember Type Checking"); var global = undefined; QUnit.test("Ember.isArray", function() { var numarray = [1,2,3]; var number = 23; var strarray = ["Hello", "Hi"]; var string = "Hello"; var object = {}; var length = { length: 12 }; var fn = function() {}; equal(utils.isArray(numarray), true, "[1,2,3]"); equal(utils.isArray(number), false, "23"); equal(utils.isArray(strarray), true, '["Hello", "Hi"]'); equal(utils.isArray(string), false, '"Hello"'); equal(utils.isArray(object), false, "{}"); equal(utils.isArray(length), true, "{ length: 12 }"); equal(utils.isArray(global), false, "global"); equal(utils.isArray(fn), false, "function() {}"); }); }); enifed('ember-metal/tests/utils/is_array_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/is_array_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/is_array_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/is_array_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/is_array_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/is_array_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/meta_test', ['ember-metal/core', 'ember-metal/platform/define_property', 'ember-metal/platform/create', 'ember-metal/utils'], function (Ember, define_property, create, utils) { 'use strict'; /*global jQuery*/ QUnit.module("Ember.meta"); QUnit.test("should return the same hash for an object", function() { var obj = {}; utils.meta(obj).foo = "bar"; equal(utils.meta(obj).foo, "bar", "returns same hash with multiple calls to Ember.meta()"); }); QUnit.module("Ember.metaPath"); QUnit.test("should not create nested objects if writable is false", function() { var obj = {}; ok(!utils.meta(obj).foo, "precond - foo property on meta does not yet exist"); expectDeprecation(function() { equal(utils.metaPath(obj, ['foo', 'bar', 'baz'], false), undefined, "should return undefined when writable is false and doesn't already exist"); }); equal(utils.meta(obj).foo, undefined, "foo property is not created"); }); QUnit.test("should create nested objects if writable is true", function() { var obj = {}; ok(!utils.meta(obj).foo, "precond - foo property on meta does not yet exist"); expectDeprecation(function() { equal(typeof utils.metaPath(obj, ['foo', 'bar', 'baz'], true), "object", "should return hash when writable is true and doesn't already exist"); }); ok(utils.meta(obj).foo.bar.baz['bat'] = true, "can set a property on the newly created hash"); }); QUnit.test("getMeta and setMeta", function() { var obj = {}; ok(!utils.getMeta(obj, 'foo'), "precond - foo property on meta does not yet exist"); utils.setMeta(obj, 'foo', "bar"); equal(utils.getMeta(obj, 'foo'), "bar", "foo property on meta now exists"); }); QUnit.module("Ember.meta enumerable"); if (define_property.canDefineNonEnumerableProperties) { QUnit.test("meta is not enumerable", function () { var proto, obj, props, prop; proto = { foo: 'bar' }; utils.meta(proto); obj = create['default'](proto); utils.meta(obj); obj.bar = 'baz'; props = []; for (prop in obj) { props.push(prop); } deepEqual(props.sort(), ['bar', 'foo']); if (typeof JSON !== 'undefined' && 'stringify' in JSON) { try { JSON.stringify(obj); } catch (e) { ok(false, 'meta should not fail JSON.stringify'); } } }); } else { // Tests fix for https://github.com/emberjs/ember.js/issues/344 // This is primarily for older browsers such as IE8 if (Ember['default'].imports.jQuery) { QUnit.test("meta is not jQuery.isPlainObject", function () { var proto, obj; proto = { foo: 'bar' }; equal(jQuery.isPlainObject(utils.meta(proto)), false, 'meta should not be isPlainObject when meta property cannot be marked as enumerable: false'); obj = create['default'](proto); equal(jQuery.isPlainObject(utils.meta(obj)), false, 'meta should not be isPlainObject when meta property cannot be marked as enumerable: false'); }); } } }); enifed('ember-metal/tests/utils/meta_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/meta_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/meta_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/meta_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/meta_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/meta_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/try_catch_finally_test', ['ember-metal/utils'], function (utils) { 'use strict'; var tryCount, catchCount, finalizeCount, tryable, catchable, finalizer, error, tryableResult, catchableResult, finalizerResult; QUnit.module("Ember.tryFinally", { setup: function() { error = new Error('Test Error'); tryCount = 0; finalizeCount = 0; catchCount = 0; tryableResult = 'tryable return value'; catchableResult = 'catchable return value'; finalizerResult = undefined; tryable = function() { tryCount++; return tryableResult; }; catchable = function() { catchCount++; return catchableResult; }; finalizer = function() { finalizeCount++; return finalizerResult; }; }, teardown: function() { tryCount = catchCount = finalizeCount = tryable = catchable = finalizer = finalizeCount = tryableResult = null; } }); function callTryCatchFinallyWithError() { var errorWasThrown; try { utils.tryCatchFinally(tryable, catchable, finalizer); } catch(e) { errorWasThrown = true; equal(e, error, 'correct error was thrown'); } equal(errorWasThrown, true, 'error was thrown'); } QUnit.test("no failure", function() { equal(utils.tryCatchFinally(tryable, catchable, finalizer), tryableResult, 'correct return value'); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 0, 'catchable was never called'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("no failure, return from finally", function() { finalizerResult = 'finalizer return value'; equal(utils.tryCatchFinally(tryable, catchable, finalizer), finalizerResult, 'correct return value'); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 0, 'catchable was never called'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("try failed", function() { tryable = function() { tryCount++; throw error; }; var result = utils.tryCatchFinally(tryable, catchable, finalizer); equal(result, catchableResult, 'correct return value'); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 1, 'catchable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("catch failed", function() { catchable = function() { catchCount++; throw error; }; utils.tryCatchFinally(tryable, catchable, finalizer); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 0, 'catchable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("try and catch failed", function() { tryable = function() { tryCount++; throw error; }; catchable = function() { catchCount++; throw error; }; callTryCatchFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 1, 'catchable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("finally failed", function() { finalizer = function() { finalizeCount++; throw error; }; callTryCatchFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 0, 'catchable was never called'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("finally and try failed", function() { tryable = function() { tryCount++; throw error; }; finalizer = function() { finalizeCount++; throw error; }; callTryCatchFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 1, 'catchable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("finally, catch and try failed", function() { tryable = function() { tryCount++; throw error; }; catchable = function() { catchCount++; throw error; }; finalizer = function() { finalizeCount++; throw error; }; callTryCatchFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(catchCount, 1, 'catchable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); }); enifed('ember-metal/tests/utils/try_catch_finally_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/try_catch_finally_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/try_catch_finally_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/try_catch_finally_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/try_catch_finally_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/try_catch_finally_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/try_finally_test', ['ember-metal/utils'], function (utils) { 'use strict'; var tryCount, finalizeCount, tryable, finalizer, error, tryableResult, finalizerResult; QUnit.module("Ember.tryFinally", { setup: function() { error = new Error('Test Error'); tryCount = 0; finalizeCount = 0; tryableResult = 'tryable return value'; finalizerResult = undefined; tryable = function() { tryCount++; return tryableResult; }; finalizer = function() { finalizeCount++; return finalizerResult; }; }, teardown: function() { tryCount = finalizeCount = tryable = finalizer = finalizeCount = tryableResult = null; } }); function callTryFinallyWithError() { var errorWasThrown; try { utils.tryFinally(tryable, finalizer); } catch(e) { errorWasThrown = true; equal(e, error, 'correct error was thrown'); } equal(errorWasThrown, true, 'error was thrown'); } QUnit.test("no failure", function() { equal(utils.tryFinally(tryable, finalizer), tryableResult, 'correct return value'); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("no failure, return from finally", function() { finalizerResult = 'finalizer return value'; equal(utils.tryFinally(tryable, finalizer), finalizerResult, 'crrect return value'); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("try failed", function() { tryable = function() { tryCount++; throw error; }; callTryFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("finally failed", function() { finalizer = function() { finalizeCount++; throw error; }; callTryFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); QUnit.test("finally and try failed", function() { tryable = function() { tryCount++; throw error; }; finalizer = function() { finalizeCount++; throw error; }; callTryFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); }); enifed('ember-metal/tests/utils/try_finally_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/try_finally_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/try_finally_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/try_finally_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/try_finally_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/try_finally_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/try_invoke_test', ['ember-metal/utils'], function (utils) { 'use strict'; var obj; QUnit.module("Ember.tryInvoke", { setup: function() { obj = { aMethodThatExists: function() { return true; }, aMethodThatTakesArguments: function(arg1, arg2) { return arg1 === arg2; } }; }, teardown: function() { obj = undefined; } }); QUnit.test("should return undefined when the object doesn't exist", function() { equal(utils.tryInvoke(undefined, 'aMethodThatDoesNotExist'), undefined); }); QUnit.test("should return undefined when asked to perform a method that doesn't exist on the object", function() { equal(utils.tryInvoke(obj, 'aMethodThatDoesNotExist'), undefined); }); QUnit.test("should return what the method returns when asked to perform a method that exists on the object", function() { equal(utils.tryInvoke(obj, 'aMethodThatExists'), true); }); QUnit.test("should return what the method returns when asked to perform a method that takes arguments and exists on the object", function() { equal(utils.tryInvoke(obj, 'aMethodThatTakesArguments', [true, true]), true); }); }); enifed('ember-metal/tests/utils/try_invoke_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/try_invoke_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/try_invoke_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/try_invoke_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/try_invoke_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/try_invoke_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/utils/type_of_test', ['ember-metal/utils'], function (utils) { 'use strict'; QUnit.module("Ember Type Checking"); QUnit.test("Ember.typeOf", function() { var MockedDate = function() { }; MockedDate.prototype = new Date(); var mockedDate = new MockedDate(); var date = new Date(); var error = new Error('boum'); var object = { a: 'b' }; equal(utils.typeOf(), 'undefined', "undefined"); equal(utils.typeOf(null), 'null', "null"); equal(utils.typeOf('Cyril'), 'string', "Cyril"); equal(utils.typeOf(101), 'number', "101"); equal(utils.typeOf(true), 'boolean', "true"); equal(utils.typeOf([1,2,90]), 'array', "[1,2,90]"); equal(utils.typeOf(/abc/), 'regexp', "/abc/"); equal(utils.typeOf(date), 'date', "new Date()"); equal(utils.typeOf(mockedDate), 'date', "mocked date"); equal(utils.typeOf(error), 'error', "error"); equal(utils.typeOf(object), 'object', "object"); if (Ember.Object) { var klass = Ember.Object.extend(); var instance = Ember.Object.create(); equal(utils.typeOf(klass), 'class', "class"); equal(utils.typeOf(instance), 'instance', "instance"); } }); }); enifed('ember-metal/tests/utils/type_of_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/utils'); test('ember-metal/tests/utils/type_of_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/utils/type_of_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/utils/type_of_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/type_of_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/type_of_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/watching/is_watching_test', ['ember-metal/computed', 'ember-metal/property_get', 'ember-metal/properties', 'ember-metal/mixin', 'ember-metal/observer', 'ember-metal/watching'], function (computed, property_get, properties, mixin, observer, watching) { 'use strict'; QUnit.module('isWatching'); function testObserver(setup, teardown, key) { var obj = {}; var fn = function() {}; key = key || 'foo'; equal(watching.isWatching(obj, key), false, "precond - isWatching is false by default"); setup(obj, key, fn); equal(watching.isWatching(obj, key), true, "isWatching is true when observers are added"); teardown(obj, key, fn); equal(watching.isWatching(obj, key), false, "isWatching is false after observers are removed"); } QUnit.test("isWatching is true for regular local observers", function() { testObserver(function(obj, key, fn) { mixin.Mixin.create({ didChange: mixin.observer(key, fn) }).apply(obj); }, function(obj, key, fn) { observer.removeObserver(obj, key, obj, fn); }); }); QUnit.test("isWatching is true for nonlocal observers", function() { testObserver(function(obj, key, fn) { observer.addObserver(obj, key, obj, fn); }, function(obj, key, fn) { observer.removeObserver(obj, key, obj, fn); }); }); QUnit.test("isWatching is true for chained observers", function() { testObserver(function(obj, key, fn) { observer.addObserver(obj, key + '.bar', obj, fn); }, function(obj, key, fn) { observer.removeObserver(obj, key + '.bar', obj, fn); }); }); QUnit.test("isWatching is true for computed properties", function() { testObserver(function(obj, key, fn) { properties.defineProperty(obj, 'computed', computed.computed(fn).property(key)); property_get.get(obj, 'computed'); }, function(obj, key, fn) { properties.defineProperty(obj, 'computed', null); }); }); QUnit.test("isWatching is true for chained computed properties", function() { testObserver(function(obj, key, fn) { properties.defineProperty(obj, 'computed', computed.computed(fn).property(key + '.bar')); property_get.get(obj, 'computed'); }, function(obj, key, fn) { properties.defineProperty(obj, 'computed', null); }); }); // can't watch length on Array - it is special... // But you should be able to watch a length property of an object QUnit.test("isWatching is true for 'length' property on object", function() { testObserver(function(obj, key, fn) { properties.defineProperty(obj, 'length', null, '26.2 miles'); observer.addObserver(obj, 'length', obj, fn); }, function(obj, key, fn) { observer.removeObserver(obj, 'length', obj, fn); }, 'length'); }); }); enifed('ember-metal/tests/watching/is_watching_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/watching'); test('ember-metal/tests/watching/is_watching_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/watching/is_watching_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/watching/is_watching_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/watching'); test('ember-metal/tests/watching/is_watching_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/watching/is_watching_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/watching/unwatch_test', ['ember-metal/tests/props_helper', 'ember-metal/watching', 'ember-metal/properties', 'ember-metal/events', 'ember-metal/computed', 'ember-metal/property_set'], function (props_helper, watching, properties, events, computed, property_set) { 'use strict'; var willCount, didCount; QUnit.module('unwatch', { setup: function() { willCount = didCount = 0; } }); function addListeners(obj, keyPath) { events.addListener(obj, keyPath + ':before', function() { willCount++; }); events.addListener(obj, keyPath + ':change', function() { didCount++; }); } props_helper.testBoth('unwatching a computed property - regular get/set', function(get, set) { var obj = {}; properties.defineProperty(obj, 'foo', computed.computed(function(keyName, value) { if (value !== undefined) { this.__foo = value; } return this.__foo; })); addListeners(obj, 'foo'); watching.watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); watching.unwatch(obj, 'foo'); willCount = didCount = 0; set(obj, 'foo', 'BAZ'); equal(willCount, 0, 'should NOT have invoked willCount'); equal(didCount, 0, 'should NOT have invoked didCount'); }); props_helper.testBoth('unwatching a regular property - regular get/set', function(get, set) { var obj = { foo: 'BIFF' }; addListeners(obj, 'foo'); watching.watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); watching.unwatch(obj, 'foo'); willCount = didCount = 0; set(obj, 'foo', 'BAZ'); equal(willCount, 0, 'should NOT have invoked willCount'); equal(didCount, 0, 'should NOT have invoked didCount'); }); QUnit.test('unwatching should be nested', function() { var obj = { foo: 'BIFF' }; addListeners(obj, 'foo'); watching.watch(obj, 'foo'); watching.watch(obj, 'foo'); property_set.set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); watching.unwatch(obj, 'foo'); willCount = didCount = 0; property_set.set(obj, 'foo', 'BAZ'); equal(willCount, 1, 'should NOT have invoked willCount'); equal(didCount, 1, 'should NOT have invoked didCount'); watching.unwatch(obj, 'foo'); willCount = didCount = 0; property_set.set(obj, 'foo', 'BAZ'); equal(willCount, 0, 'should NOT have invoked willCount'); equal(didCount, 0, 'should NOT have invoked didCount'); }); props_helper.testBoth('unwatching "length" property on an object', function(get, set) { var obj = { foo: 'RUN' }; addListeners(obj, 'length'); // Can watch length when it is undefined watching.watch(obj, 'length'); set(obj, 'length', '10k'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); // Should stop watching despite length now being defined (making object 'array-like') watching.unwatch(obj, 'length'); willCount = didCount = 0; set(obj, 'length', '5k'); equal(willCount, 0, 'should NOT have invoked willCount'); equal(didCount, 0, 'should NOT have invoked didCount'); }); }); enifed('ember-metal/tests/watching/unwatch_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/watching'); test('ember-metal/tests/watching/unwatch_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/watching/unwatch_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/watching/unwatch_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/watching'); test('ember-metal/tests/watching/unwatch_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/watching/unwatch_test.js should pass jshint.'); }); }); enifed('ember-metal/tests/watching/watch_test', ['ember-metal/core', 'ember-metal/tests/props_helper', 'ember-metal/enumerable_utils', 'ember-metal/events', 'ember-metal/watching'], function (Ember, props_helper, enumerable_utils, events, watching) { 'use strict'; var willCount, didCount, willKeys, didKeys, originalLookup, lookup, Global; QUnit.module('watch', { setup: function() { willCount = didCount = 0; willKeys = []; didKeys = []; originalLookup = Ember['default'].lookup; Ember['default'].lookup = lookup = {}; }, teardown: function() { Ember['default'].lookup = originalLookup; } }); function addListeners(obj, keyPath) { events.addListener(obj, keyPath + ':before', function() { willCount++; willKeys.push(keyPath); }); events.addListener(obj, keyPath + ':change', function() { didCount++; didKeys.push(keyPath); }); } props_helper.testBoth('watching a computed property', function(get, set) { var obj = {}; Ember['default'].defineProperty(obj, 'foo', Ember['default'].computed(function(keyName, value) { if (value !== undefined) { this.__foo = value; } return this.__foo; })); addListeners(obj, 'foo'); watching.watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); }); props_helper.testBoth('watching a regular defined property', function(get, set) { var obj = { foo: 'baz' }; addListeners(obj, 'foo'); watching.watch(obj, 'foo'); equal(get(obj, 'foo'), 'baz', 'should have original prop'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); equal(get(obj, 'foo'), 'bar', 'should get new value'); equal(obj.foo, 'bar', 'property should be accessible on obj'); }); props_helper.testBoth('watching a regular undefined property', function(get, set) { var obj = { }; addListeners(obj, 'foo'); watching.watch(obj, 'foo'); equal('foo' in obj, false, 'precond undefined'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); equal(get(obj, 'foo'), 'bar', 'should get new value'); equal(obj.foo, 'bar', 'property should be accessible on obj'); }); props_helper.testBoth('watches should inherit', function(get, set) { var obj = { foo: 'baz' }; var objB = Ember['default'].create(obj); addListeners(obj, 'foo'); watching.watch(obj, 'foo'); equal(get(obj, 'foo'), 'baz', 'should have original prop'); set(obj, 'foo', 'bar'); set(objB, 'foo', 'baz'); equal(willCount, 2, 'should have invoked willCount once only'); equal(didCount, 2, 'should have invoked didCount once only'); }); QUnit.test("watching an object THEN defining it should work also", function() { var obj = {}; addListeners(obj, 'foo'); watching.watch(obj, 'foo'); Ember['default'].defineProperty(obj, 'foo'); Ember['default'].set(obj, 'foo', 'bar'); equal(Ember['default'].get(obj, 'foo'), 'bar', 'should have set'); equal(willCount, 1, 'should have invoked willChange once'); equal(didCount, 1, 'should have invoked didChange once'); }); QUnit.test("watching a chain then defining the property", function () { var obj = {}; var foo = { bar: 'bar' }; addListeners(obj, 'foo.bar'); addListeners(foo, 'bar'); watching.watch(obj, 'foo.bar'); Ember['default'].defineProperty(obj, 'foo', undefined, foo); Ember['default'].set(foo, 'bar', 'baz'); deepEqual(willKeys, ['foo.bar', 'bar'], 'should have invoked willChange with bar, foo.bar'); deepEqual(didKeys, ['foo.bar', 'bar'], 'should have invoked didChange with bar, foo.bar'); equal(willCount, 2, 'should have invoked willChange twice'); equal(didCount, 2, 'should have invoked didChange twice'); }); QUnit.test("watching a chain then defining the nested property", function () { var bar = {}; var obj = { foo: bar }; var baz = { baz: 'baz' }; addListeners(obj, 'foo.bar.baz'); addListeners(baz, 'baz'); watching.watch(obj, 'foo.bar.baz'); Ember['default'].defineProperty(bar, 'bar', undefined, baz); Ember['default'].set(baz, 'baz', 'BOO'); deepEqual(willKeys, ['foo.bar.baz', 'baz'], 'should have invoked willChange with bar, foo.bar'); deepEqual(didKeys, ['foo.bar.baz', 'baz'], 'should have invoked didChange with bar, foo.bar'); equal(willCount, 2, 'should have invoked willChange twice'); equal(didCount, 2, 'should have invoked didChange twice'); }); props_helper.testBoth('watching an object value then unwatching should restore old value', function(get, set) { var obj = { foo: { bar: { baz: { biff: 'BIFF' } } } }; addListeners(obj, 'foo.bar.baz.biff'); watching.watch(obj, 'foo.bar.baz.biff'); var foo = Ember['default'].get(obj, 'foo'); equal(get(get(get(foo, 'bar'), 'baz'), 'biff'), 'BIFF', 'biff should exist'); watching.unwatch(obj, 'foo.bar.baz.biff'); equal(get(get(get(foo, 'bar'), 'baz'), 'biff'), 'BIFF', 'biff should exist'); }); props_helper.testBoth('watching a global object that does not yet exist should queue', function(get, set) { lookup['Global'] = Global = null; var obj = {}; addListeners(obj, 'Global.foo'); watching.watch(obj, 'Global.foo'); // only works on global chained props equal(willCount, 0, 'should not have fired yet'); equal(didCount, 0, 'should not have fired yet'); lookup['Global'] = Global = { foo: 'bar' }; addListeners(Global, 'foo'); watching.watch.flushPending(); // this will also be invoked automatically on ready equal(willCount, 0, 'should not have fired yet'); equal(didCount, 0, 'should not have fired yet'); set(Global, 'foo', 'baz'); // should fire twice because this is a chained property (once on key, once // on path) equal(willCount, 2, 'should be watching'); equal(didCount, 2, 'should be watching'); lookup['Global'] = Global = null; // reset }); QUnit.test('when watching a global object, destroy should remove chain watchers from the global object', function() { lookup['Global'] = Global = { foo: 'bar' }; var obj = {}; addListeners(obj, 'Global.foo'); watching.watch(obj, 'Global.foo'); var meta_Global = Ember['default'].meta(Global); var chainNode = Ember['default'].meta(obj).chains._chains.Global._chains.foo; var index = enumerable_utils.indexOf(meta_Global.chainWatchers.foo, chainNode); equal(meta_Global.watching.foo, 1, 'should be watching foo'); strictEqual(meta_Global.chainWatchers.foo[index], chainNode, 'should have chain watcher'); watching.destroy(obj); index = enumerable_utils.indexOf(meta_Global.chainWatchers.foo, chainNode); equal(meta_Global.watching.foo, 0, 'should not be watching foo'); equal(index, -1, 'should not have chain watcher'); lookup['Global'] = Global = null; // reset }); QUnit.test('when watching another object, destroy should remove chain watchers from the other object', function() { var objA = {}; var objB = { foo: 'bar' }; objA.b = objB; addListeners(objA, 'b.foo'); watching.watch(objA, 'b.foo'); var meta_objB = Ember['default'].meta(objB); var chainNode = Ember['default'].meta(objA).chains._chains.b._chains.foo; var index = enumerable_utils.indexOf(meta_objB.chainWatchers.foo, chainNode); equal(meta_objB.watching.foo, 1, 'should be watching foo'); strictEqual(meta_objB.chainWatchers.foo[index], chainNode, 'should have chain watcher'); watching.destroy(objA); index = enumerable_utils.indexOf(meta_objB.chainWatchers.foo, chainNode); equal(meta_objB.watching.foo, 0, 'should not be watching foo'); equal(index, -1, 'should not have chain watcher'); }); // TESTS for length property props_helper.testBoth('watching "length" property on an object', function(get, set) { var obj = { length: '26.2 miles' }; addListeners(obj, 'length'); watching.watch(obj, 'length'); equal(get(obj, 'length'), '26.2 miles', 'should have original prop'); set(obj, 'length', '10k'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); equal(get(obj, 'length'), '10k', 'should get new value'); equal(obj.length, '10k', 'property should be accessible on obj'); }); props_helper.testBoth('watching "length" property on an array', function(get, set) { var arr = []; addListeners(arr, 'length'); watching.watch(arr, 'length'); equal(get(arr, 'length'), 0, 'should have original prop'); set(arr, 'length', '10'); equal(willCount, 0, 'should NOT have invoked willCount'); equal(didCount, 0, 'should NOT have invoked didCount'); equal(get(arr, 'length'), 10, 'should get new value'); equal(arr.length, 10, 'property should be accessible on arr'); }); }); enifed('ember-metal/tests/watching/watch_test.jscs-test', function () { 'use strict'; module('JSCS - ember-metal/tests/watching'); test('ember-metal/tests/watching/watch_test.js should pass jscs', function() { ok(true, 'ember-metal/tests/watching/watch_test.js should pass jscs.'); }); }); enifed('ember-metal/tests/watching/watch_test.jshint', function () { 'use strict'; module('JSHint - ember-metal/tests/watching'); test('ember-metal/tests/watching/watch_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/watching/watch_test.js should pass jshint.'); }); }); enifed('ember-metal/utils.jscs-test', function () { 'use strict'; module('JSCS - ember-metal'); test('ember-metal/utils.js should pass jscs', function() { ok(true, 'ember-metal/utils.js should pass jscs.'); }); }); enifed('ember-metal/utils.jshint', function () { 'use strict'; module('JSHint - ember-metal'); test('ember-metal/utils.js should pass jshint', function() { ok(true, 'ember-metal/utils.js should pass jshint.'); }); }); enifed('ember-metal/watch_key.jscs-test', function () { 'use strict'; module('JSCS - ember-metal'); test('ember-metal/watch_key.js should pass jscs', function() { ok(true, 'ember-metal/watch_key.js should pass jscs.'); }); }); enifed('ember-metal/watch_key.jshint', function () { 'use strict'; module('JSHint - ember-metal'); test('ember-metal/watch_key.js should pass jshint', function() { ok(true, 'ember-metal/watch_key.js should pass jshint.'); }); }); enifed('ember-metal/watch_path.jscs-test', function () { 'use strict'; module('JSCS - ember-metal'); test('ember-metal/watch_path.js should pass jscs', function() { ok(true, 'ember-metal/watch_path.js should pass jscs.'); }); }); enifed('ember-metal/watch_path.jshint', function () { 'use strict'; module('JSHint - ember-metal'); test('ember-metal/watch_path.js should pass jshint', function() { ok(true, 'ember-metal/watch_path.js should pass jshint.'); }); }); enifed('ember-metal/watching.jscs-test', function () { 'use strict'; module('JSCS - ember-metal'); test('ember-metal/watching.js should pass jscs', function() { ok(true, 'ember-metal/watching.js should pass jscs.'); }); }); enifed('ember-metal/watching.jshint', function () { 'use strict'; module('JSHint - ember-metal'); test('ember-metal/watching.js should pass jshint', function() { ok(true, 'ember-metal/watching.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-routing-htmlbars.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-routing-htmlbars.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/helpers/action.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/action.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/helpers/action.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/helpers/action.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/action.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/helpers/action.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/helpers/link-to.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/link-to.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/helpers/link-to.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/helpers/link-to.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/link-to.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/helpers/link-to.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/helpers/outlet.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/outlet.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/helpers/outlet.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/helpers/outlet.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/outlet.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/helpers/outlet.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/helpers/query-params.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/query-params.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/helpers/query-params.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/helpers/query-params.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/query-params.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/helpers/query-params.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/helpers/render.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/render.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/helpers/render.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/helpers/render.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/helpers'); test('ember-routing-htmlbars/helpers/render.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/helpers/render.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/action_test', ['ember-metal/core', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-views/system/event_dispatcher', 'ember-views/system/action_manager', 'ember-runtime/system/container', 'ember-runtime/system/object', 'ember-runtime/controllers/controller', 'ember-runtime/controllers/array_controller', 'ember-template-compiler/system/compile', 'ember-views/views/view', 'ember-views/views/component', 'ember-views/system/jquery', 'ember-htmlbars/helpers', 'ember-routing-htmlbars/helpers/action', 'ember-runtime/tests/utils'], function (Ember, property_set, run, EventDispatcher, ActionManager, system__container, EmberObject, controllers__controller, EmberArrayController, compile, EmberView, EmberComponent, jQuery, helpers, action, utils) { 'use strict'; var dispatcher, view, originalActionHelper; var originalRegisterAction = action.ActionHelper.registerAction; QUnit.module("ember-routing-htmlbars: action helper", { setup: function() { originalActionHelper = helpers["default"]['action']; helpers.registerHelper('action', action.actionHelper); dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); }, teardown: function() { utils.runDestroy(view); utils.runDestroy(dispatcher); delete helpers["default"]['action']; helpers["default"]['action'] = originalActionHelper; action.ActionHelper.registerAction = originalRegisterAction; } }); QUnit.test("should output a data attribute with a guid", function() { view = EmberView['default'].create({ template: compile['default']('edit') }); utils.runAppend(view); ok(view.$('a').attr('data-ember-action').match(/\d+/), "A data-ember-action attribute with a guid was added"); }); QUnit.test("should by default register a click event", function() { var registeredEventName; action.ActionHelper.registerAction = function(actionName, options) { registeredEventName = options.eventName; }; view = EmberView['default'].create({ template: compile['default']('edit') }); utils.runAppend(view); equal(registeredEventName, 'click', "The click event was properly registered"); }); QUnit.test("should allow alternative events to be handled", function() { var registeredEventName; action.ActionHelper.registerAction = function(actionName, options) { registeredEventName = options.eventName; }; view = EmberView['default'].create({ template: compile['default']('edit') }); utils.runAppend(view); equal(registeredEventName, 'mouseUp', "The alternative mouseUp event was properly registered"); }); QUnit.test("should by default target the view's controller", function() { var registeredTarget; var controller = {}; action.ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target.value(); }; view = EmberView['default'].create({ controller: controller, template: compile['default']('edit') }); utils.runAppend(view); equal(registeredTarget, controller, "The controller was registered as the target"); }); QUnit.test("Inside a yield, the target points at the original target", function() { var watted = false; var component = EmberComponent['default'].extend({ boundText: "inner", truthy: true, obj: {}, layout: compile['default']("
    {{boundText}}
    {{#if truthy}}{{yield}}{{/if}}
    ") }); view = EmberView['default'].create({ controller: { boundText: "outer", truthy: true, wat: function() { watted = true; }, component: component }, template: compile['default']('{{#if truthy}}{{#view component}}{{#if truthy}}
    {{boundText}}
    {{/if}}{{/view}}{{/if}}') }); utils.runAppend(view); run['default'](function() { view.$(".wat").click(); }); equal(watted, true, "The action was called on the right context"); }); if (!Ember['default'].FEATURES.isEnabled('ember-htmlbars')) { // jscs:disable validateIndentation QUnit.test("should target the current controller inside an {{each}} loop [DEPRECATED]", function() { var registeredTarget; action.ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target.value(); }; var itemController = controllers__controller["default"].create(); var ArrayController = EmberArrayController['default'].extend({ itemController: 'stub', controllerAt: function(idx, object) { return itemController; } }); var controller = ArrayController.create({ model: Ember['default'].A([1]) }); view = EmberView['default'].create({ controller: controller, template: compile['default']('{{#each controller}}{{action "editTodo"}}{{/each}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); equal(registeredTarget, itemController, "the item controller is the target of action"); }); // jscs:enable validateIndentation } QUnit.test("should target the with-controller inside an {{#with controller='person'}} [DEPRECATED]", function() { var registeredTarget; action.ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target.value(); }; var PersonController = controllers__controller["default"].extend(); var registry = new system__container.Registry(); var container = registry.container(); var parentController = EmberObject['default'].create({ container: container }); view = EmberView['default'].create({ container: container, template: compile['default']('{{#with view.person controller="person"}}
    {{/with}}'), person: EmberObject['default'].create(), controller: parentController }); registry.register('controller:person', PersonController); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); ok(registeredTarget instanceof PersonController, "the with-controller is the target of action"); }); QUnit.test("should target the with-controller inside an {{each}} in a {{#with controller='person'}} [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); expectDeprecation('Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); var eventsCalled = []; var PeopleController = EmberArrayController['default'].extend({ actions: { robert: function() { eventsCalled.push('robert'); }, brian: function() { eventsCalled.push('brian'); } } }); var registry = new system__container.Registry(); var container = registry.container(); var parentController = EmberObject['default'].create({ container: container, people: Ember['default'].A([ { name: 'robert' }, { name: 'brian' } ]) }); view = EmberView['default'].create({ container: container, template: compile['default']('{{#with people controller="people"}}{{#each}}{{name}}{{/each}}{{/with}}'), controller: parentController }); registry.register('controller:people', PeopleController); utils.runAppend(view); view.$('a').trigger('click'); deepEqual(eventsCalled, ['robert', 'brian'], 'the events are fired properly'); }); QUnit.test("should allow a target to be specified", function() { var registeredTarget; action.ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target.value(); }; var anotherTarget = EmberView['default'].create(); view = EmberView['default'].create({ controller: {}, template: compile['default']('edit'), anotherTarget: anotherTarget }); utils.runAppend(view); equal(registeredTarget, anotherTarget, "The specified target was registered"); utils.runDestroy(anotherTarget); }); QUnit.test("should lazily evaluate the target", function() { var firstEdit = 0; var secondEdit = 0; var controller = {}; var first = { edit: function() { firstEdit++; } }; var second = { edit: function() { secondEdit++; } }; controller.theTarget = first; view = EmberView['default'].create({ controller: controller, template: compile['default']('edit') }); utils.runAppend(view); run['default'](function() { jQuery['default']('a').trigger('click'); }); equal(firstEdit, 1); run['default'](function() { property_set.set(controller, 'theTarget', second); }); run['default'](function() { jQuery['default']('a').trigger('click'); }); equal(firstEdit, 1); equal(secondEdit, 1); }); QUnit.test("should register an event handler", function() { var eventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('click me') }); utils.runAppend(view); var actionId = view.$('a[data-ember-action]').attr('data-ember-action'); ok(ActionManager['default'].registeredActions[actionId], "The action was registered"); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); QUnit.test("handles whitelisted modifier keys", function() { var eventHandlerWasCalled = false; var shortcutHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; }, shortcut: function() { shortcutHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('click me
    click me too
    ') }); utils.runAppend(view); var actionId = view.$('a[data-ember-action]').attr('data-ember-action'); ok(ActionManager['default'].registeredActions[actionId], "The action was registered"); var e = jQuery['default'].Event('click'); e.altKey = true; view.$('a').trigger(e); ok(eventHandlerWasCalled, "The event handler was called"); e = jQuery['default'].Event('click'); e.ctrlKey = true; view.$('div').trigger(e); ok(shortcutHandlerWasCalled, "The \"any\" shortcut's event handler was called"); }); QUnit.test("should be able to use action more than once for the same event within a view", function() { var editWasCalled = false; var deleteWasCalled = false; var originalEventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { editWasCalled = true; }, "delete": function() { deleteWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']( 'editdelete' ), click: function() { originalEventHandlerWasCalled = true; } }); utils.runAppend(view); view.$('#edit').trigger('click'); equal(editWasCalled, true, "The edit action was called"); equal(deleteWasCalled, false, "The delete action was not called"); editWasCalled = deleteWasCalled = originalEventHandlerWasCalled = false; view.$('#delete').trigger('click'); equal(editWasCalled, false, "The edit action was not called"); equal(deleteWasCalled, true, "The delete action was called"); editWasCalled = deleteWasCalled = originalEventHandlerWasCalled = false; view.$().trigger('click'); equal(editWasCalled, false, "The edit action was not called"); equal(deleteWasCalled, false, "The delete action was not called"); }); QUnit.test("the event should not bubble if `bubbles=false` is passed", function() { var editWasCalled = false; var deleteWasCalled = false; var originalEventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { editWasCalled = true; }, "delete": function() { deleteWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']( 'editdelete' ), click: function() { originalEventHandlerWasCalled = true; } }); utils.runAppend(view); view.$('#edit').trigger('click'); equal(editWasCalled, true, "The edit action was called"); equal(deleteWasCalled, false, "The delete action was not called"); equal(originalEventHandlerWasCalled, false, "The original event handler was not called"); editWasCalled = deleteWasCalled = originalEventHandlerWasCalled = false; view.$('#delete').trigger('click'); equal(editWasCalled, false, "The edit action was not called"); equal(deleteWasCalled, true, "The delete action was called"); equal(originalEventHandlerWasCalled, false, "The original event handler was not called"); editWasCalled = deleteWasCalled = originalEventHandlerWasCalled = false; view.$().trigger('click'); equal(editWasCalled, false, "The edit action was not called"); equal(deleteWasCalled, false, "The delete action was not called"); equal(originalEventHandlerWasCalled, true, "The original event handler was called"); }); QUnit.test("should work properly in an #each block", function() { var eventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, items: Ember['default'].A([1, 2, 3, 4]), template: compile['default']('{{#each item in view.items}}click me{{/each}}') }); utils.runAppend(view); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); QUnit.test("should work properly in a {{#with foo as bar}} block", function() { var eventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, something: { ohai: 'there' }, template: compile['default']('{{#with view.something as somethingElse}}click me{{/with}}') }); utils.runAppend(view); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); QUnit.test("should work properly in a #with block [DEPRECATED]", function() { var eventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, something: { ohai: 'there' }, template: compile['default']('{{#with view.something}}click me{{/with}}') }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); QUnit.test("should unregister event handlers on rerender", function() { var eventHandlerWasCalled = false; view = EmberView['default'].extend({ template: compile['default']('click me'), actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); utils.runAppend(view); var previousActionId = view.$('a[data-ember-action]').attr('data-ember-action'); run['default'](function() { view.rerender(); }); ok(!ActionManager['default'].registeredActions[previousActionId], "On rerender, the event handler was removed"); var newActionId = view.$('a[data-ember-action]').attr('data-ember-action'); ok(ActionManager['default'].registeredActions[newActionId], "After rerender completes, a new event handler was added"); }); QUnit.test("should unregister event handlers on inside virtual views", function() { var things = Ember['default'].A([ { name: 'Thingy' } ]); view = EmberView['default'].create({ template: compile['default']('{{#each thing in view.things}}click me{{/each}}'), things: things }); utils.runAppend(view); var actionId = view.$('a[data-ember-action]').attr('data-ember-action'); run['default'](function() { things.removeAt(0); }); ok(!ActionManager['default'].registeredActions[actionId], "After the virtual view was destroyed, the action was unregistered"); }); QUnit.test("should properly capture events on child elements of a container with an action", function() { var eventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('
    ') }); utils.runAppend(view); view.$('button').trigger('click'); ok(eventHandlerWasCalled, "Event on a child element triggered the action of its parent"); }); QUnit.test("should allow bubbling of events from action helper to original parent event", function() { var eventHandlerWasCalled = false; var originalEventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('click me'), click: function() { originalEventHandlerWasCalled = true; } }); utils.runAppend(view); view.$('a').trigger('click'); ok(eventHandlerWasCalled && originalEventHandlerWasCalled, "Both event handlers were called"); }); QUnit.test("should not bubble an event from action helper to original parent event if `bubbles=false` is passed", function() { var eventHandlerWasCalled = false; var originalEventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('click me'), click: function() { originalEventHandlerWasCalled = true; } }); utils.runAppend(view); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The child handler was called"); ok(!originalEventHandlerWasCalled, "The parent handler was not called"); }); QUnit.test("should allow 'send' as action name (#594)", function() { var eventHandlerWasCalled = false; var controller = controllers__controller["default"].extend({ send: function() { eventHandlerWasCalled = true; } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('send') }); utils.runAppend(view); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The view's send method was called"); }); QUnit.test("should send the view, event and current context to the action", function() { var passedTarget; var passedContext; var aTarget = controllers__controller["default"].extend({ actions: { edit: function(context) { passedTarget = this; passedContext = context; } } }).create(); var aContext = { aTarget: aTarget }; view = EmberView['default'].create({ context: aContext, template: compile['default']('edit') }); utils.runAppend(view); view.$('#edit').trigger('click'); strictEqual(passedTarget, aTarget, "the action is called with the target as this"); strictEqual(passedContext, aContext, "the parameter is passed along"); }); QUnit.test("should only trigger actions for the event they were registered on", function() { var editWasCalled = false; view = EmberView['default'].extend({ template: compile['default']('edit'), actions: { edit: function() { editWasCalled = true; } } }).create(); utils.runAppend(view); view.$('a').trigger('mouseover'); ok(!editWasCalled, "The action wasn't called"); }); QUnit.test("should unwrap controllers passed as a context", function() { var passedContext; var model = EmberObject['default'].create(); var controller = controllers__controller["default"].extend({ model: model, actions: { edit: function(context) { passedContext = context; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('') }); utils.runAppend(view); view.$('button').trigger('click'); equal(passedContext, model, "the action was passed the unwrapped model"); }); QUnit.test("should not unwrap controllers passed as `controller`", function() { var passedContext; var model = EmberObject['default'].create(); var controller = controllers__controller["default"].extend({ model: model, actions: { edit: function(context) { passedContext = context; } } }).create(); view = EmberView['default'].create({ controller: controller, template: compile['default']('') }); utils.runAppend(view); view.$('button').trigger('click'); equal(passedContext, controller, "the action was passed the controller"); }); QUnit.test("should allow multiple contexts to be specified", function() { var passedContexts; var models = [EmberObject['default'].create(), EmberObject['default'].create()]; var controller = controllers__controller["default"].extend({ actions: { edit: function() { passedContexts = [].slice.call(arguments); } } }).create(); view = EmberView['default'].create({ controller: controller, modelA: models[0], modelB: models[1], template: compile['default']('') }); utils.runAppend(view); view.$('button').trigger('click'); deepEqual(passedContexts, models, "the action was called with the passed contexts"); }); QUnit.test("should allow multiple contexts to be specified mixed with string args", function() { var passedParams; var model = EmberObject['default'].create(); var controller = controllers__controller["default"].extend({ actions: { edit: function() { passedParams = [].slice.call(arguments); } } }).create(); view = EmberView['default'].create({ controller: controller, modelA: model, template: compile['default']('') }); utils.runAppend(view); view.$('button').trigger('click'); deepEqual(passedParams, ["herp", model], "the action was called with the passed contexts"); }); QUnit.test("it does not trigger action with special clicks", function() { var showCalled = false; view = EmberView['default'].create({ template: compile['default']("Hi") }); var controller = controllers__controller["default"].extend({ actions: { show: function() { showCalled = true; } } }).create(); run['default'](function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); function checkClick(prop, value, expected) { var event = jQuery['default'].Event("click"); event[prop] = value; view.$('a').trigger(event); if (expected) { ok(showCalled, "should call action with "+prop+":"+value); ok(event.isDefaultPrevented(), "should prevent default"); } else { ok(!showCalled, "should not call action with "+prop+":"+value); ok(!event.isDefaultPrevented(), "should not prevent default"); } } checkClick('ctrlKey', true, false); checkClick('altKey', true, false); checkClick('metaKey', true, false); checkClick('shiftKey', true, false); checkClick('which', 2, false); checkClick('which', 1, true); checkClick('which', undefined, true); // IE <9 }); QUnit.test("it can trigger actions for keyboard events", function() { var showCalled = false; view = EmberView['default'].create({ template: compile['default']("") }); var controller = controllers__controller["default"].extend({ actions: { show: function() { showCalled = true; } } }).create(); run['default'](function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var event = jQuery['default'].Event("keyup"); event["char"] = 'a'; event.which = 65; view.$('input').trigger(event); ok(showCalled, "should call action with keyup"); }); QUnit.test("a quoteless parameter should allow dynamic lookup of the actionName", function() { expect(4); var lastAction; var actionOrder = []; view = EmberView['default'].create({ template: compile['default']("Hi") }); var controller = controllers__controller["default"].extend({ hookMeUp: 'biggityBoom', actions: { biggityBoom: function() { lastAction = 'biggityBoom'; actionOrder.push(lastAction); }, whompWhomp: function() { lastAction = 'whompWhomp'; actionOrder.push(lastAction); }, sloopyDookie: function() { lastAction = 'sloopyDookie'; actionOrder.push(lastAction); } } }).create(); run['default'](function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var testBoundAction = function(propertyValue) { run['default'](function() { controller.set('hookMeUp', propertyValue); }); run['default'](function() { view.$("#woot-bound-param").click(); }); equal(lastAction, propertyValue, 'lastAction set to ' + propertyValue); }; testBoundAction('whompWhomp'); testBoundAction('sloopyDookie'); testBoundAction('biggityBoom'); deepEqual(actionOrder, ['whompWhomp', 'sloopyDookie', 'biggityBoom'], 'action name was looked up properly'); }); QUnit.test("a quoteless parameter should lookup actionName in context [DEPRECATED]", function() { expect(5); var lastAction; var actionOrder = []; view = EmberView['default'].create({ template: compile['default']("{{#each allactions}}{{title}}{{/each}}") }); var controller = controllers__controller["default"].extend({ allactions: Ember['default'].A([{ title: 'Biggity Boom',name: 'biggityBoom' }, { title: 'Whomp Whomp',name: 'whompWhomp' }, { title: 'Sloopy Dookie',name: 'sloopyDookie' }]), actions: { biggityBoom: function() { lastAction = 'biggityBoom'; actionOrder.push(lastAction); }, whompWhomp: function() { lastAction = 'whompWhomp'; actionOrder.push(lastAction); }, sloopyDookie: function() { lastAction = 'sloopyDookie'; actionOrder.push(lastAction); } } }).create(); expectDeprecation(function() { run['default'](function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); var testBoundAction = function(propertyValue) { run['default'](function() { view.$("#"+propertyValue).click(); }); equal(lastAction, propertyValue, 'lastAction set to ' + propertyValue); }; testBoundAction('whompWhomp'); testBoundAction('sloopyDookie'); testBoundAction('biggityBoom'); deepEqual(actionOrder, ['whompWhomp', 'sloopyDookie', 'biggityBoom'], 'action name was looked up properly'); }); QUnit.test("a quoteless parameter should resolve actionName, including path", function() { expect(4); var lastAction; var actionOrder = []; view = EmberView['default'].create({ template: compile['default']("{{#each item in allactions}}{{item.title}}{{/each}}") }); var controller = controllers__controller["default"].extend({ allactions: Ember['default'].A([{ title: 'Biggity Boom',name: 'biggityBoom' }, { title: 'Whomp Whomp',name: 'whompWhomp' }, { title: 'Sloopy Dookie',name: 'sloopyDookie' }]), actions: { biggityBoom: function() { lastAction = 'biggityBoom'; actionOrder.push(lastAction); }, whompWhomp: function() { lastAction = 'whompWhomp'; actionOrder.push(lastAction); }, sloopyDookie: function() { lastAction = 'sloopyDookie'; actionOrder.push(lastAction); } } }).create(); run['default'](function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var testBoundAction = function(propertyValue) { run['default'](function() { view.$("#"+propertyValue).click(); }); equal(lastAction, propertyValue, 'lastAction set to ' + propertyValue); }; testBoundAction('whompWhomp'); testBoundAction('sloopyDookie'); testBoundAction('biggityBoom'); deepEqual(actionOrder, ['whompWhomp', 'sloopyDookie', 'biggityBoom'], 'action name was looked up properly'); }); QUnit.test("a quoteless parameter that does not resolve to a value asserts", function() { var triggeredAction; view = EmberView['default'].create({ template: compile['default']("Hi") }); var controller = controllers__controller["default"].extend({ actions: { ohNoeNotValid: function() { triggeredAction = true; } } }).create(); run['default'](function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); expectAssertion(function() { run['default'](function() { view.$("#oops-bound-param").click(); }); }, "You specified a quoteless path to the {{action}} helper " + "which did not resolve to an action name (a string). " + "Perhaps you meant to use a quoted actionName? (e.g. {{action 'save'}})."); }); QUnit.module("ember-routing-htmlbars: action helper - deprecated invoking directly on target", { setup: function() { originalActionHelper = helpers["default"]['action']; helpers.registerHelper('action', action.actionHelper); dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); }, teardown: function() { delete helpers["default"]['action']; helpers["default"]['action'] = originalActionHelper; utils.runDestroy(view); utils.runDestroy(dispatcher); } }); QUnit.test("should respect preventDefault=false option if provided", function() { view = EmberView['default'].create({ template: compile['default']("Hi") }); var controller = controllers__controller["default"].extend({ actions: { show: function() { } } }).create(); run['default'](function() { view.set('controller', controller); utils.runAppend(view); }); var event = jQuery['default'].Event("click"); view.$('a').trigger(event); equal(event.isDefaultPrevented(), false, "should not preventDefault"); }); }); enifed('ember-routing-htmlbars/tests/helpers/action_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/action_test.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/action_test.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/action_test.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/action_test.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/action_test.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/link-to_test', ['ember-routing-htmlbars', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-template-compiler/system/compile', 'ember-metal/property_set', 'ember-runtime/controllers/controller', 'ember-runtime/tests/utils'], function (__dep0__, run, EmberView, compile, property_set, Controller, utils) { 'use strict'; var view; QUnit.module("ember-routing-htmlbars: link-to helper", { teardown: function() { utils.runDestroy(view); } }); QUnit.test("should be able to be inserted in DOM when the router is not present", function() { var template = "{{#link-to 'index'}}Go to Index{{/link-to}}"; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'Go to Index'); }); QUnit.test("re-renders when title changes", function() { var template = "{{link-to title routeName}}"; view = EmberView['default'].create({ controller: { title: 'foo', routeName: 'index' }, template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'foo'); run['default'](function() { property_set.set(view, 'controller.title', 'bar'); }); equal(view.$().text(), 'bar'); }); QUnit.test("can read bound title", function() { var template = "{{link-to title routeName}}"; view = EmberView['default'].create({ controller: { title: 'foo', routeName: 'index' }, template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'foo'); }); QUnit.test("escaped inline form (double curlies) escapes link title", function() { view = EmberView['default'].create({ title: "blah", template: compile['default']("{{link-to view.title}}") }); utils.runAppend(view); equal(view.$('b').length, 0, 'no were found'); }); QUnit.test("unescaped inline form (triple curlies) does not escape link title", function() { view = EmberView['default'].create({ title: "blah", template: compile['default']("{{{link-to view.title}}}") }); utils.runAppend(view); equal(view.$('b').length, 1, ' was found'); }); QUnit.test("unwraps controllers", function() { var template = "{{#link-to 'index' view.otherController}}Text{{/link-to}}"; view = EmberView['default'].create({ otherController: Controller['default'].create({ model: 'foo' }), template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'Text'); }); }); enifed('ember-routing-htmlbars/tests/helpers/link-to_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/link-to_test.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/link-to_test.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/link-to_test.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/link-to_test.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/link-to_test.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/outlet_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-runtime/system/namespace', 'ember-views/views/metamorph_view', 'ember-routing/ext/view', 'ember-views/views/container_view', 'ember-views/system/jquery', 'ember-routing-htmlbars/helpers/outlet', 'ember-template-compiler/system/compile', 'ember-htmlbars/helpers', 'ember-runtime/tests/utils', 'ember-routing-htmlbars/tests/utils'], function (Ember, run, Namespace, _MetamorphView, EmberView, EmberContainerView, jQuery, outlet, compile, helpers, utils, tests__utils) { 'use strict'; var trim = jQuery['default'].trim; var view, registry, container, originalOutletHelper; QUnit.module("ember-routing-htmlbars: {{outlet}} helper", { setup: function() { originalOutletHelper = helpers['default']['outlet']; helpers.registerHelper('outlet', outlet.outletHelper); var namespace = Namespace['default'].create(); registry = tests__utils.buildRegistry(namespace); container = registry.container(); }, teardown: function() { delete helpers['default']['outlet']; helpers['default']['outlet'] = originalOutletHelper; utils.runDestroy(container); utils.runDestroy(view); registry = container = view = null; } }); QUnit.test("view should support connectOutlet for the main outlet", function() { var template = "

    HI

    {{outlet}}"; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'HI'); run['default'](function() { view.connectOutlet('main', EmberView['default'].create({ template: compile['default']("

    BYE

    ") })); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); QUnit.test("outlet should support connectOutlet in slots in prerender state", function() { var template = "

    HI

    {{outlet}}"; view = EmberView['default'].create({ template: compile['default'](template) }); view.connectOutlet('main', EmberView['default'].create({ template: compile['default']("

    BYE

    ") })); utils.runAppend(view); equal(view.$().text(), 'HIBYE'); }); QUnit.test("outlet should support an optional name", function() { var template = "

    HI

    {{outlet 'mainView'}}"; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'HI'); run['default'](function() { view.connectOutlet('mainView', EmberView['default'].create({ template: compile['default']("

    BYE

    ") })); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); QUnit.test("outlet should correctly lookup a view", function() { var template, ContainerView, childView; ContainerView = EmberContainerView['default'].extend(); registry.register("view:containerView", ContainerView); template = "

    HI

    {{outlet view='containerView'}}"; view = EmberView['default'].create({ template: compile['default'](template), container : container }); childView = EmberView['default'].create({ template: compile['default']("

    BYE

    ") }); utils.runAppend(view); equal(view.$().text(), 'HI'); run['default'](function() { view.connectOutlet('main', childView); }); ok(ContainerView.detectInstance(childView._parentView), "The custom view class should be used for the outlet"); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); QUnit.test("outlet should assert view is specified as a string", function() { var template = "

    HI

    {{outlet view=containerView}}"; expectAssertion(function () { view = EmberView['default'].create({ template: compile['default'](template), container : container }); utils.runAppend(view); }); }); QUnit.test("outlet should assert view path is successfully resolved", function() { var template = "

    HI

    {{outlet view='someViewNameHere'}}"; expectAssertion(function () { view = EmberView['default'].create({ template: compile['default'](template), container : container }); utils.runAppend(view); }); }); QUnit.test("outlet should support an optional view class", function() { var template = "

    HI

    {{outlet viewClass=view.outletView}}"; view = EmberView['default'].create({ template: compile['default'](template), outletView: EmberContainerView['default'].extend() }); utils.runAppend(view); equal(view.$().text(), 'HI'); var childView = EmberView['default'].create({ template: compile['default']("

    BYE

    ") }); run['default'](function() { view.connectOutlet('main', childView); }); ok(view.outletView.detectInstance(childView._parentView), "The custom view class should be used for the outlet"); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); QUnit.test("Outlets bind to the current view, not the current concrete view", function() { var parentTemplate = "

    HI

    {{outlet}}"; var middleTemplate = "

    MIDDLE

    {{outlet}}"; var bottomTemplate = "

    BOTTOM

    "; view = EmberView['default'].create({ template: compile['default'](parentTemplate) }); var middleView = _MetamorphView['default'].create({ template: compile['default'](middleTemplate) }); var bottomView = _MetamorphView['default'].create({ template: compile['default'](bottomTemplate) }); utils.runAppend(view); run['default'](function() { view.connectOutlet('main', middleView); }); run['default'](function() { middleView.connectOutlet('main', bottomView); }); var output = jQuery['default']('#qunit-fixture h1 ~ h2 ~ h3').text(); equal(output, "BOTTOM", "all templates were rendered"); }); QUnit.test("view should support disconnectOutlet for the main outlet", function() { var template = "

    HI

    {{outlet}}"; view = EmberView['default'].create({ template: compile['default'](template) }); utils.runAppend(view); equal(view.$().text(), 'HI'); run['default'](function() { view.connectOutlet('main', EmberView['default'].create({ template: compile['default']("

    BYE

    ") })); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); run['default'](function() { view.disconnectOutlet('main'); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HI'); }); // TODO: Remove flag when {{with}} is fixed. if (!Ember['default'].FEATURES.isEnabled('ember-htmlbars')) { // jscs:disable validateIndentation QUnit.test("Outlets bind to the current template's view, not inner contexts [DEPRECATED]", function() { var parentTemplate = "

    HI

    {{#if view.alwaysTrue}}{{#with this}}{{outlet}}{{/with}}{{/if}}"; var bottomTemplate = "

    BOTTOM

    "; view = EmberView['default'].create({ alwaysTrue: true, template: compile['default'](parentTemplate) }); var bottomView = _MetamorphView['default'].create({ template: compile['default'](bottomTemplate) }); expectDeprecation(function() { utils.runAppend(view); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead.'); run['default'](function() { view.connectOutlet('main', bottomView); }); var output = jQuery['default']('#qunit-fixture h1 ~ h3').text(); equal(output, "BOTTOM", "all templates were rendered"); }); // jscs:enable validateIndentation } QUnit.test("should support layouts", function() { var template = "{{outlet}}"; var layout = "

    HI

    {{yield}}"; view = EmberView['default'].create({ template: compile['default'](template), layout: compile['default'](layout) }); utils.runAppend(view); equal(view.$().text(), 'HI'); run['default'](function() { view.connectOutlet('main', EmberView['default'].create({ template: compile['default']("

    BYE

    ") })); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); QUnit.test("should not throw deprecations if {{outlet}} is used without a name", function() { expectNoDeprecation(); view = EmberView['default'].create({ template: compile['default']("{{outlet}}") }); utils.runAppend(view); }); QUnit.test("should not throw deprecations if {{outlet}} is used with a quoted name", function() { expectNoDeprecation(); view = EmberView['default'].create({ template: compile['default']("{{outlet \"foo\"}}") }); utils.runAppend(view); }); QUnit.test("should throw an assertion if {{outlet}} used with unquoted name", function() { view = EmberView['default'].create({ template: compile['default']("{{outlet foo}}") }); expectAssertion(function() { utils.runAppend(view); }, "Using {{outlet}} with an unquoted name is not supported."); }); }); enifed('ember-routing-htmlbars/tests/helpers/outlet_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/outlet_test.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/outlet_test.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/outlet_test.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/outlet_test.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/outlet_test.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/render_test', ['ember-metal/core', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-metal/platform/define_property', 'ember-metal/mixin', 'ember-runtime/system/namespace', 'ember-runtime/controllers/controller', 'ember-runtime/controllers/array_controller', 'ember-htmlbars/helpers', 'ember-template-compiler/system/compile', 'ember-routing/ext/view', 'ember-views/system/jquery', 'ember-views/system/action_manager', 'ember-routing-htmlbars/helpers/render', 'ember-routing-htmlbars/helpers/action', 'ember-routing-htmlbars/helpers/outlet', 'ember-routing-htmlbars/tests/utils', 'ember-runtime/tests/utils'], function (Ember, property_set, run, define_property, mixin, Namespace, controllers__controller, EmberArrayController, helpers, compile, EmberView, jQuery, ActionManager, render, helpers__action, outlet, utils, tests__utils) { 'use strict'; function runSet(object, key, value) { run['default'](function() { property_set.set(object, key, value); }); } var view, container, originalRenderHelper, originalActionHelper, originalOutletHelper; QUnit.module("ember-routing-htmlbars: {{render}} helper", { setup: function() { originalOutletHelper = helpers['default']['outlet']; helpers.registerHelper('outlet', outlet.outletHelper); originalRenderHelper = helpers['default']['render']; helpers.registerHelper('render', render.renderHelper); originalActionHelper = helpers['default']['action']; helpers.registerHelper('action', helpers__action.actionHelper); var namespace = Namespace['default'].create(); var registry = utils.buildRegistry(namespace); container = registry.container(); }, teardown: function() { delete helpers['default']['render']; helpers['default']['render'] = originalRenderHelper; delete helpers['default']['action']; helpers['default']['action'] = originalActionHelper; delete helpers['default']['outlet']; helpers['default']['outlet'] = originalOutletHelper; tests__utils.runDestroy(container); tests__utils.runDestroy(view); Ember['default'].TEMPLATES = {}; } }); QUnit.test("{{render}} helper should render given template", function() { var template = "

    HI

    {{render 'home'}}"; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); tests__utils.runAppend(view); equal(view.$().text(), 'HIBYE'); ok(container.lookup('router:main')._lookupActiveView('home'), 'should register home as active view'); }); QUnit.test("{{render}} helper should have assertion if neither template nor view exists", function() { var template = "

    HI

    {{render 'oops'}}"; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); expectAssertion(function() { tests__utils.runAppend(view); }, 'You used `{{render \'oops\'}}`, but \'oops\' can not be found as either a template or a view.'); }); QUnit.test("{{render}} helper should not have assertion if template is supplied in block-form", function() { var template = "

    HI

    {{#render 'good'}} {{name}}{{/render}}"; var controller = controllers__controller["default"].extend({ container: container }); container._registry.register('controller:good', controllers__controller["default"].extend({ name: 'Rob' })); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); tests__utils.runAppend(view); equal(view.$().text(), 'HI Rob'); }); QUnit.test("{{render}} helper should not have assertion if view exists without a template", function() { var template = "

    HI

    {{render 'oops'}}"; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); container._registry.register('view:oops', EmberView['default'].extend()); tests__utils.runAppend(view); equal(view.$().text(), 'HI'); }); QUnit.test("{{render}} helper should render given template with a supplied model", function() { var template = "

    HI

    {{render 'post' post}}"; var post = { title: "Rails is omakase" }; var Controller = controllers__controller["default"].extend({ container: container, post: post }); var controller = Controller.create({ }); view = EmberView['default'].create({ controller: controller, template: compile['default'](template) }); var PostController = controllers__controller["default"].extend(); container._registry.register('controller:post', PostController); Ember['default'].TEMPLATES['post'] = compile['default']("

    {{model.title}}

    "); tests__utils.runAppend(view); var postController = view._childViews[0].get('controller'); equal(view.$().text(), 'HIRails is omakase'); equal(postController.get('model'), post); runSet(controller, 'post', { title: "Rails is unagi" }); equal(view.$().text(), 'HIRails is unagi'); if (define_property.canDefineNonEnumerableProperties) { deepEqual(postController.get('model'), { title: "Rails is unagi" }); } else { equal(postController.get('model').title, "Rails is unagi"); } }); QUnit.test("{{render}} helper with a supplied model should not fire observers on the controller", function () { var template = "

    HI

    {{render 'post' post}}"; var post = { title: "Rails is omakase" }; view = EmberView['default'].create({ controller: controllers__controller["default"].create({ container: container, post: post }), template: compile['default'](template) }); var PostController = controllers__controller["default"].extend({ modelDidChange: mixin.observer('model', function() { modelDidChange++; }) }); container._registry.register('controller:post', PostController); Ember['default'].TEMPLATES['post'] = compile['default']("

    {{title}}

    "); var modelDidChange = 0; tests__utils.runAppend(view); equal(modelDidChange, 0, "model observer did not fire"); }); QUnit.test("{{render}} helper should raise an error when a given controller name does not resolve to a controller", function() { var template = '

    HI

    {{render "home" controller="postss"}}'; var controller = controllers__controller["default"].extend({ container: container }); container._registry.register('controller:posts', EmberArrayController['default'].extend()); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); expectAssertion(function() { tests__utils.runAppend(view); }, 'The controller name you supplied \'postss\' did not resolve to a controller.'); }); QUnit.test("{{render}} helper should render with given controller", function() { var template = '

    HI

    {{render "home" controller="posts"}}'; var controller = controllers__controller["default"].extend({ container: container }); container._registry.register('controller:posts', EmberArrayController['default'].extend()); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); tests__utils.runAppend(view); var renderedView = container.lookup('router:main')._lookupActiveView('home'); equal(container.lookup('controller:posts'), renderedView.get('controller'), 'rendered with correct controller'); }); QUnit.test("{{render}} helper should render a template without a model only once", function() { var template = "

    HI

    {{render 'home'}}
    {{render 'home'}}"; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); expectAssertion(function() { tests__utils.runAppend(view); }, /\{\{render\}\} helper once/i); }); QUnit.test("{{render}} helper should render templates with models multiple times", function() { var template = "

    HI

    {{render 'post' post1}} {{render 'post' post2}}"; var post1 = { title: "Me first" }; var post2 = { title: "Then me" }; var Controller = controllers__controller["default"].extend({ container: container, post1: post1, post2: post2 }); var controller = Controller.create(); view = EmberView['default'].create({ controller: controller, template: compile['default'](template) }); var PostController = controllers__controller["default"].extend(); container._registry.register('controller:post', PostController, { singleton: false }); Ember['default'].TEMPLATES['post'] = compile['default']("

    {{model.title}}

    "); tests__utils.runAppend(view); var postController1 = view._childViews[0].get('controller'); var postController2 = view._childViews[1].get('controller'); ok(view.$().text().match(/^HI ?Me first ?Then me$/)); equal(postController1.get('model'), post1); equal(postController2.get('model'), post2); runSet(controller, 'post1', { title: "I am new" }); ok(view.$().text().match(/^HI ?I am new ?Then me$/)); if (define_property.canDefineNonEnumerableProperties) { deepEqual(postController1.get('model'), { title: "I am new" }); } else { equal(postController1.get('model').title, "I am new"); } }); QUnit.test("{{render}} helper should not leak controllers", function() { var template = "

    HI

    {{render 'post' post1}}"; var post1 = { title: "Me first" }; var Controller = controllers__controller["default"].extend({ container: container, post1: post1 }); var controller = Controller.create(); view = EmberView['default'].create({ controller: controller, template: compile['default'](template) }); var PostController = controllers__controller["default"].extend(); container._registry.register('controller:post', PostController); Ember['default'].TEMPLATES['post'] = compile['default']("

    {{title}}

    "); tests__utils.runAppend(view); var postController1 = view._childViews[0].get('controller'); tests__utils.runDestroy(view); ok(postController1.isDestroyed, 'expected postController to be destroyed'); }); QUnit.test("{{render}} helper should not treat invocations with falsy contexts as context-less", function() { var template = "

    HI

    {{render 'post' zero}} {{render 'post' nonexistent}}"; view = EmberView['default'].create({ controller: controllers__controller["default"].createWithMixins({ container: container, zero: false }), template: compile['default'](template) }); var PostController = controllers__controller["default"].extend(); container._registry.register('controller:post', PostController, { singleton: false }); Ember['default'].TEMPLATES['post'] = compile['default']("

    {{#unless model}}NOTHING{{/unless}}

    "); tests__utils.runAppend(view); var postController1 = view._childViews[0].get('controller'); var postController2 = view._childViews[1].get('controller'); ok(view.$().text().match(/^HI ?NOTHING ?NOTHING$/)); equal(postController1.get('model'), 0); equal(postController2.get('model'), undefined); }); QUnit.test("{{render}} helper should render templates both with and without models", function() { var template = "

    HI

    {{render 'post'}} {{render 'post' post}}"; var post = { title: "Rails is omakase" }; var Controller = controllers__controller["default"].extend({ container: container, post: post }); var controller = Controller.create(); view = EmberView['default'].create({ controller: controller, template: compile['default'](template) }); var PostController = controllers__controller["default"].extend(); container._registry.register('controller:post', PostController, { singleton: false }); Ember['default'].TEMPLATES['post'] = compile['default']("

    Title:{{model.title}}

    "); tests__utils.runAppend(view); var postController1 = view._childViews[0].get('controller'); var postController2 = view._childViews[1].get('controller'); ok(view.$().text().match(/^HI ?Title: ?Title:Rails is omakase$/)); equal(postController1.get('model'), null); equal(postController2.get('model'), post); runSet(controller, 'post', { title: "Rails is unagi" }); ok(view.$().text().match(/^HI ?Title: ?Title:Rails is unagi$/)); if (define_property.canDefineNonEnumerableProperties) { deepEqual(postController2.get('model'), { title: "Rails is unagi" }); } else { equal(postController2.get('model').title, "Rails is unagi"); } }); QUnit.test("{{render}} helper should link child controllers to the parent controller", function() { var parentTriggered = 0; var template = '

    HI

    {{render "posts"}}'; var controller = controllers__controller["default"].extend({ container: container, actions: { parentPlease: function() { parentTriggered++; } }, role: "Mom" }); container._registry.register('controller:posts', EmberArrayController['default'].extend()); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['posts'] = compile['default'](''); tests__utils.runAppend(view); var button = jQuery['default']("#parent-action"); var actionId = button.data('ember-action'); var action = ActionManager['default'].registeredActions[actionId]; var handler = action.handler; equal(button.text(), "Go to Mom", "The parentController property is set on the child controller"); run['default'](null, handler, new jQuery['default'].Event("click")); equal(parentTriggered, 1, "The event bubbled to the parent"); }); QUnit.test("{{render}} helper should be able to render a template again when it was removed", function() { var template = "

    HI

    {{outlet}}"; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); tests__utils.runAppend(view); run['default'](function() { view.connectOutlet('main', EmberView['default'].create({ controller: controller.create(), template: compile['default']("
    1{{render 'home'}}
    ") })); }); equal(view.$().text(), 'HI1BYE'); run['default'](function() { view.connectOutlet('main', EmberView['default'].create({ controller: controller.create(), template: compile['default']("
    2{{render 'home'}}
    ") })); }); equal(view.$().text(), 'HI2BYE'); }); QUnit.test("{{render}} works with dot notation", function() { var template = '

    BLOG

    {{render "blog.post"}}'; var controller = controllers__controller["default"].extend({ container: container }); container._registry.register('controller:blog.post', controllers__controller["default"].extend()); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['blog.post'] = compile['default']("

    POST

    "); tests__utils.runAppend(view); var renderedView = container.lookup('router:main')._lookupActiveView('blog.post'); equal(renderedView.get('viewName'), 'blogPost', 'camelizes the view name'); equal(container.lookup('controller:blog.post'), renderedView.get('controller'), 'rendered with correct controller'); }); QUnit.test("{{render}} works with slash notation", function() { var template = '

    BLOG

    {{render "blog/post"}}'; var controller = controllers__controller["default"].extend({ container: container }); container._registry.register('controller:blog.post', controllers__controller["default"].extend()); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['blog.post'] = compile['default']("

    POST

    "); tests__utils.runAppend(view); var renderedView = container.lookup('router:main')._lookupActiveView('blog.post'); equal(renderedView.get('viewName'), 'blogPost', 'camelizes the view name'); equal(container.lookup('controller:blog.post'), renderedView.get('controller'), 'rendered with correct controller'); }); // jscs:disable validateIndentation QUnit.test("throws an assertion if {{render}} is called with an unquoted template name", function() { var template = '

    HI

    {{render home}}'; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); expectAssertion(function() { tests__utils.runAppend(view); }, "The first argument of {{render}} must be quoted, e.g. {{render \"sidebar\"}}."); }); QUnit.test("throws an assertion if {{render}} is called with a literal for a model", function() { var template = '

    HI

    {{render "home" "model"}}'; var controller = controllers__controller["default"].extend({ container: container }); view = EmberView['default'].create({ controller: controller.create(), template: compile['default'](template) }); Ember['default'].TEMPLATES['home'] = compile['default']("

    BYE

    "); expectAssertion(function() { tests__utils.runAppend(view); }, "The second argument of {{render}} must be a path, e.g. {{render \"post\" post}}."); }); // jscs:enable validateIndentation }); enifed('ember-routing-htmlbars/tests/helpers/render_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/render_test.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/render_test.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/tests/helpers/render_test.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/tests/helpers'); test('ember-routing-htmlbars/tests/helpers/render_test.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/tests/helpers/render_test.js should pass jshint.'); }); }); enifed('ember-routing-htmlbars/tests/utils', ['exports', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-runtime/system/string', 'container/registry', 'ember-runtime/controllers/controller', 'ember-runtime/controllers/object_controller', 'ember-runtime/controllers/array_controller', 'ember-views/views/metamorph_view', 'ember-routing/system/router', 'ember-routing/location/hash_location'], function (exports, property_get, property_set, string, Registry, Controller, ObjectController, ArrayController, _MetamorphView, EmberRouter, HashLocation) { 'use strict'; exports.resolverFor = resolverFor; exports.buildRegistry = buildRegistry; function resolverFor(namespace) { return function(fullName) { var nameParts = fullName.split(":"); var type = nameParts[0]; var name = nameParts[1]; if (type === "template") { var templateName = string.decamelize(name); if (Ember.TEMPLATES[templateName]) { return Ember.TEMPLATES[templateName]; } } var className = string.classify(name) + string.classify(type); var factory = property_get.get(namespace, className); if (factory) { return factory; } }; } function buildRegistry(namespace) { var registry = new Registry['default'](); registry.set = property_set.set; registry.resolver = resolverFor(namespace); registry.optionsForType("view", { singleton: false }); registry.optionsForType("template", { instantiate: false }); registry.register("application:main", namespace, { instantiate: false }); registry.injection("router:main", "namespace", "application:main"); registry.register("location:hash", HashLocation['default']); registry.register("controller:basic", Controller['default'], { instantiate: false }); registry.register("controller:object", ObjectController['default'], { instantiate: false }); registry.register("controller:array", ArrayController['default'], { instantiate: false }); registry.register("view:default", _MetamorphView['default']); registry.register("router:main", EmberRouter['default'].extend()); registry.typeInjection("route", "router", "router:main"); return registry; } }); enifed('ember-routing-htmlbars/tests/utils.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-htmlbars/tests'); test('ember-routing-htmlbars/tests/utils.js should pass jscs', function() { ok(true, 'ember-routing-htmlbars/tests/utils.js should pass jscs.'); }); }); enifed('ember-routing-htmlbars/tests/utils.jshint', function () { 'use strict'; module('JSHint - ember-routing-htmlbars/tests'); test('ember-routing-htmlbars/tests/utils.js should pass jshint', function() { ok(true, 'ember-routing-htmlbars/tests/utils.js should pass jshint.'); }); }); enifed('ember-routing-views.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-routing-views.js should pass jscs', function() { ok(true, 'ember-routing-views.js should pass jscs.'); }); }); enifed('ember-routing-views.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-routing-views.js should pass jshint', function() { ok(true, 'ember-routing-views.js should pass jshint.'); }); }); enifed('ember-routing-views/tests/main_test', ['ember-routing-views', 'ember-metal/core'], function (__dep0__, Ember) { 'use strict'; QUnit.module("ember-routing-views"); QUnit.test("exports correctly", function() { ok(Ember['default'].LinkView, "LinkView is exported correctly"); ok(Ember['default'].OutletView, "OutletView is exported correctly"); }); }); enifed('ember-routing-views/tests/main_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-views/tests'); test('ember-routing-views/tests/main_test.js should pass jscs', function() { ok(true, 'ember-routing-views/tests/main_test.js should pass jscs.'); }); }); enifed('ember-routing-views/tests/main_test.jshint', function () { 'use strict'; module('JSHint - ember-routing-views/tests'); test('ember-routing-views/tests/main_test.js should pass jshint', function() { ok(true, 'ember-routing-views/tests/main_test.js should pass jshint.'); }); }); enifed('ember-routing-views/views/link.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-views/views'); test('ember-routing-views/views/link.js should pass jscs', function() { ok(true, 'ember-routing-views/views/link.js should pass jscs.'); }); }); enifed('ember-routing-views/views/link.jshint', function () { 'use strict'; module('JSHint - ember-routing-views/views'); test('ember-routing-views/views/link.js should pass jshint', function() { ok(true, 'ember-routing-views/views/link.js should pass jshint.'); }); }); enifed('ember-routing-views/views/outlet.jscs-test', function () { 'use strict'; module('JSCS - ember-routing-views/views'); test('ember-routing-views/views/outlet.js should pass jscs', function() { ok(true, 'ember-routing-views/views/outlet.js should pass jscs.'); }); }); enifed('ember-routing-views/views/outlet.jshint', function () { 'use strict'; module('JSHint - ember-routing-views/views'); test('ember-routing-views/views/outlet.js should pass jshint', function() { ok(true, 'ember-routing-views/views/outlet.js should pass jshint.'); }); }); enifed('ember-routing.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-routing.js should pass jscs', function() { ok(true, 'ember-routing.js should pass jscs.'); }); }); enifed('ember-routing.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-routing.js should pass jshint', function() { ok(true, 'ember-routing.js should pass jshint.'); }); }); enifed('ember-routing/ext/controller.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/ext'); test('ember-routing/ext/controller.js should pass jscs', function() { ok(true, 'ember-routing/ext/controller.js should pass jscs.'); }); }); enifed('ember-routing/ext/controller.jshint', function () { 'use strict'; module('JSHint - ember-routing/ext'); test('ember-routing/ext/controller.js should pass jshint', function() { ok(true, 'ember-routing/ext/controller.js should pass jshint.'); }); }); enifed('ember-routing/ext/run_loop.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/ext'); test('ember-routing/ext/run_loop.js should pass jscs', function() { ok(true, 'ember-routing/ext/run_loop.js should pass jscs.'); }); }); enifed('ember-routing/ext/run_loop.jshint', function () { 'use strict'; module('JSHint - ember-routing/ext'); test('ember-routing/ext/run_loop.js should pass jshint', function() { ok(true, 'ember-routing/ext/run_loop.js should pass jshint.'); }); }); enifed('ember-routing/ext/view.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/ext'); test('ember-routing/ext/view.js should pass jscs', function() { ok(true, 'ember-routing/ext/view.js should pass jscs.'); }); }); enifed('ember-routing/ext/view.jshint', function () { 'use strict'; module('JSHint - ember-routing/ext'); test('ember-routing/ext/view.js should pass jshint', function() { ok(true, 'ember-routing/ext/view.js should pass jshint.'); }); }); enifed('ember-routing/location/api.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/location'); test('ember-routing/location/api.js should pass jscs', function() { ok(true, 'ember-routing/location/api.js should pass jscs.'); }); }); enifed('ember-routing/location/api.jshint', function () { 'use strict'; module('JSHint - ember-routing/location'); test('ember-routing/location/api.js should pass jshint', function() { ok(true, 'ember-routing/location/api.js should pass jshint.'); }); }); enifed('ember-routing/location/auto_location.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/location'); test('ember-routing/location/auto_location.js should pass jscs', function() { ok(true, 'ember-routing/location/auto_location.js should pass jscs.'); }); }); enifed('ember-routing/location/auto_location.jshint', function () { 'use strict'; module('JSHint - ember-routing/location'); test('ember-routing/location/auto_location.js should pass jshint', function() { ok(true, 'ember-routing/location/auto_location.js should pass jshint.'); }); }); enifed('ember-routing/location/feature_detect.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/location'); test('ember-routing/location/feature_detect.js should pass jscs', function() { ok(true, 'ember-routing/location/feature_detect.js should pass jscs.'); }); }); enifed('ember-routing/location/feature_detect.jshint', function () { 'use strict'; module('JSHint - ember-routing/location'); test('ember-routing/location/feature_detect.js should pass jshint', function() { ok(true, 'ember-routing/location/feature_detect.js should pass jshint.'); }); }); enifed('ember-routing/location/hash_location.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/location'); test('ember-routing/location/hash_location.js should pass jscs', function() { ok(true, 'ember-routing/location/hash_location.js should pass jscs.'); }); }); enifed('ember-routing/location/hash_location.jshint', function () { 'use strict'; module('JSHint - ember-routing/location'); test('ember-routing/location/hash_location.js should pass jshint', function() { ok(true, 'ember-routing/location/hash_location.js should pass jshint.'); }); }); enifed('ember-routing/location/history_location.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/location'); test('ember-routing/location/history_location.js should pass jscs', function() { ok(true, 'ember-routing/location/history_location.js should pass jscs.'); }); }); enifed('ember-routing/location/history_location.jshint', function () { 'use strict'; module('JSHint - ember-routing/location'); test('ember-routing/location/history_location.js should pass jshint', function() { ok(true, 'ember-routing/location/history_location.js should pass jshint.'); }); }); enifed('ember-routing/location/none_location.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/location'); test('ember-routing/location/none_location.js should pass jscs', function() { ok(true, 'ember-routing/location/none_location.js should pass jscs.'); }); }); enifed('ember-routing/location/none_location.jshint', function () { 'use strict'; module('JSHint - ember-routing/location'); test('ember-routing/location/none_location.js should pass jshint', function() { ok(true, 'ember-routing/location/none_location.js should pass jshint.'); }); }); enifed('ember-routing/system/cache.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/cache.js should pass jscs', function() { ok(true, 'ember-routing/system/cache.js should pass jscs.'); }); }); enifed('ember-routing/system/cache.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/cache.js should pass jshint', function() { ok(true, 'ember-routing/system/cache.js should pass jshint.'); }); }); enifed('ember-routing/system/controller_for.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/controller_for.js should pass jscs', function() { ok(true, 'ember-routing/system/controller_for.js should pass jscs.'); }); }); enifed('ember-routing/system/controller_for.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/controller_for.js should pass jshint', function() { ok(true, 'ember-routing/system/controller_for.js should pass jshint.'); }); }); enifed('ember-routing/system/dsl.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/dsl.js should pass jscs', function() { ok(true, 'ember-routing/system/dsl.js should pass jscs.'); }); }); enifed('ember-routing/system/dsl.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/dsl.js should pass jshint', function() { ok(true, 'ember-routing/system/dsl.js should pass jshint.'); }); }); enifed('ember-routing/system/generate_controller.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/generate_controller.js should pass jscs', function() { ok(true, 'ember-routing/system/generate_controller.js should pass jscs.'); }); }); enifed('ember-routing/system/generate_controller.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/generate_controller.js should pass jshint', function() { ok(true, 'ember-routing/system/generate_controller.js should pass jshint.'); }); }); enifed('ember-routing/system/query_params.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/query_params.js should pass jscs', function() { ok(true, 'ember-routing/system/query_params.js should pass jscs.'); }); }); enifed('ember-routing/system/query_params.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/query_params.js should pass jshint', function() { ok(true, 'ember-routing/system/query_params.js should pass jshint.'); }); }); enifed('ember-routing/system/route.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/route.js should pass jscs', function() { ok(true, 'ember-routing/system/route.js should pass jscs.'); }); }); enifed('ember-routing/system/route.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/route.js should pass jshint', function() { ok(true, 'ember-routing/system/route.js should pass jshint.'); }); }); enifed('ember-routing/system/router.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/router.js should pass jscs', function() { ok(true, 'ember-routing/system/router.js should pass jscs.'); }); }); enifed('ember-routing/system/router.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/router.js should pass jshint', function() { ok(true, 'ember-routing/system/router.js should pass jshint.'); }); }); enifed('ember-routing/system/router_state.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/system'); test('ember-routing/system/router_state.js should pass jscs', function() { ok(true, 'ember-routing/system/router_state.js should pass jscs.'); }); }); enifed('ember-routing/system/router_state.jshint', function () { 'use strict'; module('JSHint - ember-routing/system'); test('ember-routing/system/router_state.js should pass jshint', function() { ok(true, 'ember-routing/system/router_state.js should pass jshint.'); }); }); enifed('ember-routing/tests/location/auto_location_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-metal/environment', 'ember-metal/merge', 'ember-runtime/copy', 'ember-runtime/system/object', 'ember-routing/location/auto_location', 'ember-routing/location/api', 'ember-routing/location/feature_detect'], function (property_get, run, environment, merge, copy, EmberObject, AutoLocation, EmberLocation, feature_detect) { 'use strict'; var AutoTestLocation, location; var FakeHistoryLocation = EmberObject['default'].extend({ implementation: 'history' }); var FakeHashLocation = EmberObject['default'].extend({ implementation: 'hash' }); var FakeNoneLocation = EmberObject['default'].extend({ implementation: 'none' }); function createLocation(options) { if (!options) { options = {}; } if ('history' in options) { AutoTestLocation._getSupportsHistory = function() { return options.history; }; } if ('hashChange' in options) { AutoTestLocation._getSupportsHashChange = function() { return options.hashChange; }; } location = AutoTestLocation.create(options); } function mockBrowserLocation(overrides) { AutoTestLocation._location = merge['default']({ href: 'http://test.com/', pathname: '/', hash: '', search: '', replace: function () { ok(false, 'location.replace should not be called during testing'); } }, overrides); } function mockBrowserHistory(overrides) { AutoTestLocation._history = merge['default']({ pushState: function () { ok(false, 'history.pushState should not be called during testing'); }, replaceState: function () { ok(false, 'history.replaceState should not be called during testing'); } }, overrides); } QUnit.module("Ember.AutoLocation", { setup: function() { AutoTestLocation = copy['default'](AutoLocation['default']); AutoTestLocation._HistoryLocation = FakeHistoryLocation; AutoTestLocation._HashLocation = FakeHashLocation; AutoTestLocation._NoneLocation = FakeNoneLocation; }, teardown: function() { run['default'](function() { if (location && location.destroy) { location.destroy(); } location = AutoTestLocation = null; }); } }); QUnit.test("_replacePath cannot be used to redirect to a different origin (website)", function() { expect(1); var expectedURL; mockBrowserLocation({ protocol: 'http:', hostname: 'emberjs.com', port: '1337', replace: function (url) { equal(url, expectedURL); } }); expectedURL = 'http://emberjs.com:1337//google.com'; AutoTestLocation._replacePath('//google.com'); }); QUnit.test("AutoLocation.create() should return a HistoryLocation instance when pushStates are supported", function() { expect(2); mockBrowserLocation(); createLocation({ history: true, hashChange: true }); equal(property_get.get(location, 'implementation'), 'history'); equal(location instanceof FakeHistoryLocation, true); }); QUnit.test("AutoLocation.create() should return a HashLocation instance when pushStates are not supported, but hashchange events are and the URL is already in the HashLocation format", function() { expect(2); mockBrowserLocation({ hash: '#/testd' }); createLocation({ history: false, hashChange: true }); equal(property_get.get(location, 'implementation'), 'hash'); equal(location instanceof FakeHashLocation, true); }); QUnit.test("AutoLocation.create() should return a NoneLocation instance when neither history nor hashchange is supported.", function() { expect(2); mockBrowserLocation({ hash: '#/testd' }); createLocation({ history: false, hashChange: false }); equal(property_get.get(location, 'implementation'), 'none'); equal(location instanceof FakeNoneLocation, true); }); QUnit.test("AutoLocation.create() should consider an index path (i.e. '/\') without any location.hash as OK for HashLocation", function() { expect(2); mockBrowserLocation({ href: 'http://test.com/', pathname: '/', hash: '', search: '', replace: function (path) { ok(false, 'location.replace should not be called'); } }); createLocation({ history: false, hashChange: true }); equal(property_get.get(location, 'implementation'), 'hash'); equal(location instanceof FakeHashLocation, true); }); QUnit.test("Feature-detecting the history API", function() { equal(feature_detect.supportsHistory("", { pushState: true }), true, "returns true if not Android Gingerbread and history.pushState exists"); equal(feature_detect.supportsHistory("", {}), false, "returns false if history.pushState doesn't exist"); equal(feature_detect.supportsHistory("", undefined), false, "returns false if history doesn't exist"); equal(feature_detect.supportsHistory("Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", { pushState: true }), false, "returns false if Android Gingerbread stock browser claiming to support pushState"); }); QUnit.test("AutoLocation.create() should transform the URL for hashchange-only browsers viewing a HistoryLocation-formatted path", function() { expect(4); mockBrowserLocation({ hash: '', hostname: 'test.com', href: 'http://test.com/test', pathname: '/test', protocol: 'http:', port: '', search: '', replace: function (path) { equal(path, 'http://test.com/#/test', 'location.replace should be called with normalized HashLocation path'); } }); createLocation({ history: false, hashChange: true }); equal(property_get.get(location, 'implementation'), 'none', 'NoneLocation should be returned while we attempt to location.replace()'); equal(location instanceof FakeNoneLocation, true, 'NoneLocation should be returned while we attempt to location.replace()'); equal(property_get.get(location, 'cancelRouterSetup'), true, 'cancelRouterSetup should be set so the router knows.'); }); QUnit.test("AutoLocation.create() should replace the URL for pushState-supported browsers viewing a HashLocation-formatted url", function() { expect(2); mockBrowserLocation({ hash: '#/test', hostname: 'test.com', href: 'http://test.com/#/test', pathname: '/', protocol: 'http:', port: '', search: '' }); mockBrowserHistory({ replaceState: function (state, title, path) { equal(path, '/test', 'history.replaceState should be called with normalized HistoryLocation url'); } }); createLocation({ history: true, hashChange: true }); equal(property_get.get(location, 'implementation'), 'history'); }); QUnit.test("Feature-Detecting onhashchange", function() { mockBrowserLocation(); equal(feature_detect.supportsHashChange(undefined, { onhashchange: function() {} }), true, "When not in IE, use onhashchange existence as evidence of the feature"); equal(feature_detect.supportsHashChange(undefined, { }), false, "When not in IE, use onhashchange absence as evidence of the feature absence"); equal(feature_detect.supportsHashChange(7, { onhashchange: function() {} }), false, "When in IE7 compatibility mode, never report existence of the feature"); equal(feature_detect.supportsHashChange(8, { onhashchange: function() {} }), true, "When in IE8+, use onhashchange existence as evidence of the feature"); }); QUnit.test("AutoLocation._getPath() should normalize location.pathname, making sure it always returns a leading slash", function() { expect(2); mockBrowserLocation({ pathname: 'test' }); equal(AutoTestLocation._getPath(), '/test', 'When there is no leading slash, one is added.'); mockBrowserLocation({ pathname: '/test' }); equal(AutoTestLocation._getPath(), '/test', 'When a leading slash is already there, it isn\'t added again'); }); QUnit.test("AutoLocation._getHash() should be an alias to Ember.Location._getHash, otherwise it needs its own test!", function() { expect(1); equal(AutoTestLocation._getHash, EmberLocation['default']._getHash); }); QUnit.test("AutoLocation._getQuery() should return location.search as-is", function() { expect(1); mockBrowserLocation({ search: '?foo=bar' }); equal(AutoTestLocation._getQuery(), '?foo=bar'); }); QUnit.test("AutoLocation._getFullPath() should return full pathname including query and hash", function() { expect(1); mockBrowserLocation({ href: 'http://test.com/about?foo=bar#foo', pathname: '/about', search: '?foo=bar', hash: '#foo' }); equal(AutoTestLocation._getFullPath(), '/about?foo=bar#foo'); }); QUnit.test("AutoLocation._getHistoryPath() should return a normalized, HistoryLocation-supported path", function() { expect(3); AutoTestLocation.rootURL = '/app/'; mockBrowserLocation({ href: 'http://test.com/app/about?foo=bar#foo', pathname: '/app/about', search: '?foo=bar', hash: '#foo' }); equal(AutoTestLocation._getHistoryPath(), '/app/about?foo=bar#foo', 'URLs already in HistoryLocation form should come out the same'); mockBrowserLocation({ href: 'http://test.com/app/#/about?foo=bar#foo', pathname: '/app/', search: '', hash: '#/about?foo=bar#foo' }); equal(AutoTestLocation._getHistoryPath(), '/app/about?foo=bar#foo', 'HashLocation formed URLs should be normalized'); mockBrowserLocation({ href: 'http://test.com/app/#about?foo=bar#foo', pathname: '/app/', search: '', hash: '#about?foo=bar#foo' }); equal(AutoTestLocation._getHistoryPath(), '/app/#about?foo=bar#foo', 'URLs with a hash not following #/ convention shouldn\'t be normalized as a route'); }); QUnit.test("AutoLocation._getHashPath() should return a normalized, HashLocation-supported path", function() { expect(3); AutoTestLocation.rootURL = '/app/'; mockBrowserLocation({ href: 'http://test.com/app/#/about?foo=bar#foo', pathname: '/app/', search: '', hash: '#/about?foo=bar#foo' }); equal(AutoTestLocation._getHashPath(), '/app/#/about?foo=bar#foo', 'URLs already in HistoryLocation form should come out the same'); mockBrowserLocation({ href: 'http://test.com/app/about?foo=bar#foo', pathname: '/app/about', search: '?foo=bar', hash: '#foo' }); equal(AutoTestLocation._getHashPath(), '/app/#/about?foo=bar#foo', 'HistoryLocation formed URLs should be normalized'); mockBrowserLocation({ href: 'http://test.com/app/#about?foo=bar#foo', pathname: '/app/', search: '', hash: '#about?foo=bar#foo' }); equal(AutoTestLocation._getHashPath(), '/app/#/#about?foo=bar#foo', 'URLs with a hash not following #/ convention shouldn\'t be normalized as a route'); }); QUnit.test("AutoLocation.create requires any rootURL given to end in a trailing forward slash", function() { expect(3); mockBrowserLocation(); var expectedMsg = /rootURL must end with a trailing forward slash e.g. "\/app\/"/; expectAssertion(function() { createLocation({ rootURL: 'app' }); }, expectedMsg); expectAssertion(function() { createLocation({ rootURL: '/app' }); }, expectedMsg); expectAssertion(function() { // Note the trailing whitespace createLocation({ rootURL: '/app/ ' }); }, expectedMsg); }); /* These next two reference check tests are needed to ensure window.location/history are always kept, since we can't actually test them directly and we mock them both in all the tests above. Without these two, #9958 could happen again. */ QUnit.test("AutoLocation has valid references to window.location and window.history via environment", function() { expect(2); equal(AutoTestLocation._location, environment['default'].location, 'AutoLocation._location === environment.location'); equal(AutoTestLocation._history, environment['default'].history, 'AutoLocation._history === environment.history'); }); }); enifed('ember-routing/tests/location/auto_location_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/location'); test('ember-routing/tests/location/auto_location_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/location/auto_location_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/location/auto_location_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/location'); test('ember-routing/tests/location/auto_location_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/location/auto_location_test.js should pass jshint.'); }); }); enifed('ember-routing/tests/location/hash_location_test', ['ember-metal/core', 'ember-metal/property_get', 'ember-metal/utils', 'ember-metal/run_loop', 'ember-routing/location/hash_location'], function (Ember, property_get, utils, run, HashLocation) { 'use strict'; var HashTestLocation, location; function createLocation(options) { if (!options) { options = {}; } location = HashTestLocation.create(options); } function mockBrowserLocation(path) { // This is a neat trick to auto-magically extract the hostname from any // url by letting the browser do the work ;) var tmp = document.createElement('a'); tmp.href = path; var protocol = (!tmp.protocol || tmp.protocol === ':') ? 'http' : tmp.protocol; var pathname = (tmp.pathname.match(/^\//)) ? tmp.pathname : '/' + tmp.pathname; return { hash: tmp.hash, host: tmp.host || 'localhost', hostname: tmp.hostname || 'localhost', href: tmp.href, pathname: pathname, port: tmp.port || '', protocol: protocol, search: tmp.search }; } QUnit.module("Ember.HashLocation", { setup: function() { HashTestLocation = HashLocation['default'].extend({ _location: { href: 'http://test.com/', pathname: '/', hash: '', search: '', replace: function () { ok(false, 'location.replace should not be called during testing'); } } }); }, teardown: function() { run['default'](function() { if (location) { location.destroy(); } }); } }); QUnit.test("HashLocation.getURL() returns the current url", function() { expect(1); createLocation({ _location: mockBrowserLocation('/#/foo/bar') }); equal(location.getURL(), '/foo/bar'); }); QUnit.test("HashLocation.getURL() includes extra hashes", function() { expect(1); createLocation({ _location: mockBrowserLocation('/#/foo#bar#car') }); equal(location.getURL(), '/foo#bar#car'); }); QUnit.test("HashLocation.getURL() assumes location.hash without #/ prefix is not a route path", function() { expect(1); createLocation({ _location: mockBrowserLocation('/#foo#bar') }); equal(location.getURL(), '/#foo#bar'); }); QUnit.test("HashLocation.getURL() returns a normal forward slash when there is no location.hash", function() { expect(1); createLocation({ _location: mockBrowserLocation('/') }); equal(location.getURL(), '/'); }); QUnit.test("HashLocation.setURL() correctly sets the url", function() { expect(2); createLocation(); location.setURL('/bar'); equal(property_get.get(location, 'location.hash'), '/bar'); equal(property_get.get(location, 'lastSetURL'), '/bar'); }); QUnit.test("HashLocation.replaceURL() correctly replaces to the path with a page reload", function() { expect(2); createLocation({ _location: { replace: function(path) { equal(path, '#/foo'); } } }); location.replaceURL('/foo'); equal(property_get.get(location, 'lastSetURL'), '/foo'); }); QUnit.test("HashLocation.onUpdateURL() registers a hashchange callback", function() { expect(3); var oldJquery = Ember['default'].$; Ember['default'].$ = function (element) { equal(element, window); return { on: function(eventName, callback) { equal(eventName, 'hashchange.ember-location-' + guid); equal(Object.prototype.toString.call(callback), '[object Function]'); } }; }; createLocation({ // Mock so test teardown doesn't fail willDestroy: function () {} }); var guid = utils.guidFor(location); location.onUpdateURL(function () {}); // clean up Ember['default'].$ = oldJquery; }); QUnit.test("HashLocation.formatURL() prepends a # to the provided string", function() { expect(1); createLocation(); equal(location.formatURL('/foo#bar'), '#/foo#bar'); }); QUnit.test("HashLocation.willDestroy() cleans up hashchange event listener", function() { expect(2); var oldJquery = Ember['default'].$; Ember['default'].$ = function (element) { equal(element, window); return { off: function(eventName) { equal(eventName, 'hashchange.ember-location-' + guid); } }; }; createLocation(); var guid = utils.guidFor(location); location.willDestroy(); // noop so test teardown doesn't call our mocked jQuery again location.willDestroy = function() {}; // clean up Ember['default'].$ = oldJquery; }); }); enifed('ember-routing/tests/location/hash_location_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/location'); test('ember-routing/tests/location/hash_location_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/location/hash_location_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/location/hash_location_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/location'); test('ember-routing/tests/location/hash_location_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/location/hash_location_test.js should pass jshint.'); }); }); enifed('ember-routing/tests/location/history_location_test', ['ember-metal/property_set', 'ember-metal/run_loop', 'ember-routing/location/history_location'], function (property_set, run, HistoryLocation) { 'use strict'; var FakeHistory, HistoryTestLocation, location; function createLocation(options) { if (!options) { options = {}; } location = HistoryTestLocation.create(options); } function mockBrowserLocation(path) { // This is a neat trick to auto-magically extract the hostname from any // url by letting the browser do the work ;) var tmp = document.createElement('a'); tmp.href = path; var protocol = (!tmp.protocol || tmp.protocol === ':') ? 'http' : tmp.protocol; var pathname = (tmp.pathname.match(/^\//)) ? tmp.pathname : '/' + tmp.pathname; return { hash: tmp.hash, host: tmp.host || 'localhost', hostname: tmp.hostname || 'localhost', href: tmp.href, pathname: pathname, port: tmp.port || '', protocol: protocol, search: tmp.search }; } QUnit.module("Ember.HistoryLocation", { setup: function() { FakeHistory = { state: null, _states: [], replaceState: function(state, title, url) { this.state = state; this._states[0] = state; }, pushState: function(state, title, url) { this.state = state; this._states.unshift(state); } }; HistoryTestLocation = HistoryLocation['default'].extend({ history: FakeHistory }); }, teardown: function() { run['default'](function() { if (location) { location.destroy(); } }); } }); QUnit.test("HistoryLocation initState does not get fired on init", function() { expect(1); HistoryTestLocation.reopen({ init: function() { ok(true, 'init was called'); this._super.apply(this, arguments); }, initState: function() { ok(false, 'initState() should not be called automatically'); } }); createLocation(); }); QUnit.test("webkit doesn't fire popstate on page load", function() { expect(1); HistoryTestLocation.reopen({ initState: function() { this._super.apply(this, arguments); // these two should be equal to be able // to successfully detect webkit initial popstate equal(this._previousURL, this.getURL()); } }); createLocation(); location.initState(); }); QUnit.test("base URL is removed when retrieving the current pathname", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super.apply(this, arguments); property_set.set(this, 'location', mockBrowserLocation('/base/foo/bar')); property_set.set(this, 'baseURL', '/base/'); }, initState: function() { this._super.apply(this, arguments); equal(this.getURL(), '/foo/bar'); } }); createLocation(); location.initState(); }); QUnit.test("base URL is preserved when moving around", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super.apply(this, arguments); property_set.set(this, 'location', mockBrowserLocation('/base/foo/bar')); property_set.set(this, 'baseURL', '/base/'); } }); createLocation(); location.initState(); location.setURL('/one/two'); equal(location._historyState.path, '/base/one/two'); }); QUnit.test("setURL continues to set even with a null state (iframes may set this)", function() { expect(1); createLocation(); location.initState(); FakeHistory.pushState(null); location.setURL('/three/four'); equal(location._historyState.path, '/three/four'); }); QUnit.test("replaceURL continues to set even with a null state (iframes may set this)", function() { expect(1); createLocation(); location.initState(); FakeHistory.pushState(null); location.replaceURL('/three/four'); equal(location._historyState.path, '/three/four'); }); QUnit.test("HistoryLocation.getURL() returns the current url, excluding both rootURL and baseURL", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super.apply(this, arguments); property_set.set(this, 'location', mockBrowserLocation('/base/foo/bar')); property_set.set(this, 'rootURL', '/app/'); property_set.set(this, 'baseURL', '/base/'); } }); createLocation(); equal(location.getURL(), '/foo/bar'); }); QUnit.test("HistoryLocation.getURL() includes location.search", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super.apply(this, arguments); property_set.set(this, 'location', mockBrowserLocation('/foo/bar?time=morphin')); } }); createLocation(); equal(location.getURL(), '/foo/bar?time=morphin'); }); QUnit.test("HistoryLocation.getURL() includes location.hash", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super.apply(this, arguments); property_set.set(this, 'location', mockBrowserLocation('/foo/bar#pink-power-ranger')); } }); createLocation(); equal(location.getURL(), '/foo/bar#pink-power-ranger'); }); QUnit.test("HistoryLocation.getURL() includes location.hash and location.search", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super.apply(this, arguments); property_set.set(this, 'location', mockBrowserLocation('/foo/bar?time=morphin#pink-power-ranger')); } }); createLocation(); equal(location.getURL(), '/foo/bar?time=morphin#pink-power-ranger'); }); }); enifed('ember-routing/tests/location/history_location_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/location'); test('ember-routing/tests/location/history_location_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/location/history_location_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/location/history_location_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/location'); test('ember-routing/tests/location/history_location_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/location/history_location_test.js should pass jshint.'); }); }); enifed('ember-routing/tests/system/controller_for_test', ['ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'container/registry', 'ember-runtime/system/namespace', 'ember-runtime/system/string', 'ember-runtime/controllers/controller', 'ember-runtime/controllers/object_controller', 'ember-runtime/controllers/array_controller', 'ember-routing/system/controller_for', 'ember-routing/system/generate_controller'], function (Ember, property_get, property_set, run, Registry, Namespace, string, Controller, object_controller, ArrayController, controllerFor, generate_controller) { 'use strict'; var buildContainer = function(namespace) { var registry = new Registry['default'](); var container = registry.container(); registry.set = property_set.set; registry.resolver = resolverFor(namespace); registry.optionsForType('view', { singleton: false }); registry.register('application:main', namespace, { instantiate: false }); registry.register('controller:basic', Controller['default'], { instantiate: false }); registry.register('controller:object', object_controller["default"], { instantiate: false }); registry.register('controller:array', ArrayController['default'], { instantiate: false }); return container; }; function resolverFor(namespace) { return function(fullName) { var nameParts = fullName.split(":"); var type = nameParts[0]; var name = nameParts[1]; if (name === 'basic') { name = ''; } var className = string.classify(name) + string.classify(type); var factory = property_get.get(namespace, className); if (factory) { return factory; } }; } var container, appController, namespace; QUnit.module("Ember.controllerFor", { setup: function() { namespace = Namespace['default'].create(); container = buildContainer(namespace); container._registry.register('controller:app', Controller['default'].extend()); appController = container.lookup('controller:app'); }, teardown: function() { run['default'](function () { container.destroy(); namespace.destroy(); }); } }); QUnit.test("controllerFor should lookup for registered controllers", function() { var controller = controllerFor['default'](container, 'app'); equal(appController, controller, 'should find app controller'); }); QUnit.module("Ember.generateController", { setup: function() { namespace = Namespace['default'].create(); container = buildContainer(namespace); }, teardown: function() { run['default'](function () { container.destroy(); namespace.destroy(); }); } }); QUnit.test("generateController and generateControllerFactory are properties on the root namespace", function() { equal(Ember['default'].generateController, generate_controller["default"], 'should export generateController'); equal(Ember['default'].generateControllerFactory, generate_controller.generateControllerFactory, 'should export generateControllerFactory'); }); QUnit.test("generateController should create Ember.Controller", function() { var controller = generate_controller["default"](container, 'home'); ok(controller instanceof Controller['default'], 'should create controller'); }); QUnit.test("generateController should create Ember.ObjectController [DEPRECATED]", function() { var context = {}; var controller = generate_controller["default"](container, 'home', context); ok(controller instanceof object_controller["default"], 'should create controller'); }); QUnit.test("generateController should create Ember.ArrayController", function() { var context = Ember['default'].A(); var controller = generate_controller["default"](container, 'home', context); ok(controller instanceof ArrayController['default'], 'should create controller'); }); QUnit.test("generateController should create App.Controller if provided", function() { var controller; namespace.Controller = Controller['default'].extend(); controller = generate_controller["default"](container, 'home'); ok(controller instanceof namespace.Controller, 'should create controller'); }); QUnit.test("generateController should create App.ObjectController if provided", function() { var context = {}; var controller; namespace.ObjectController = object_controller["default"].extend(); controller = generate_controller["default"](container, 'home', context); ok(controller instanceof namespace.ObjectController, 'should create controller'); }); QUnit.test("generateController should create App.ArrayController if provided", function() { var context = Ember['default'].A(); var controller; namespace.ArrayController = ArrayController['default'].extend(); controller = generate_controller["default"](container, 'home', context); ok(controller instanceof namespace.ArrayController, 'should create controller'); }); }); enifed('ember-routing/tests/system/controller_for_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/system'); test('ember-routing/tests/system/controller_for_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/system/controller_for_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/system/controller_for_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/system'); test('ember-routing/tests/system/controller_for_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/system/controller_for_test.js should pass jshint.'); }); }); enifed('ember-routing/tests/system/dsl_test', ['ember-routing/system/router', 'ember-metal/enumerable_utils'], function (EmberRouter, enumerable_utils) { 'use strict'; var Router; QUnit.module("Ember Router DSL", { setup: function() { Router = EmberRouter['default'].extend(); }, teardown: function() { Router = null; } }); QUnit.test("should fail when using a reserved route name", function() { var reservedNames = ['array', 'basic', 'object']; expect(reservedNames.length * 2); enumerable_utils.forEach(reservedNames, function(reservedName) { expectAssertion(function() { Router = EmberRouter['default'].extend(); Router.map(function() { this.route(reservedName); }); var router = Router.create(); router._initRouterJs(); }, "'" + reservedName + "' cannot be used as a route name."); expectAssertion(function() { Router = EmberRouter['default'].extend(); Router.map(function() { this.resource(reservedName); }); var router = Router.create(); router._initRouterJs(); }, "'" + reservedName + "' cannot be used as a resource name."); }); }); QUnit.test("should reset namespace if nested with resource", function() { Router = Router.map(function() { this.resource('bleep', function() { this.resource('bloop', function() { this.resource('blork'); }); }); }); var router = Router.create(); router._initRouterJs(); ok(router.router.recognizer.names['bleep'], 'nested resources do not contain parent name'); ok(router.router.recognizer.names['bloop'], 'nested resources do not contain parent name'); ok(router.router.recognizer.names['blork'], 'nested resources do not contain parent name'); }); QUnit.test("should retain resource namespace if nested with routes", function() { Router = Router.map(function() { this.route('bleep', function() { this.route('bloop', function() { this.route('blork'); }); }); }); var router = Router.create(); router._initRouterJs(); ok(router.router.recognizer.names['bleep'], 'parent name was used as base of nested routes'); ok(router.router.recognizer.names['bleep.bloop'], 'parent name was used as base of nested routes'); ok(router.router.recognizer.names['bleep.bloop.blork'], 'parent name was used as base of nested routes'); }); // jscs:disable validateIndentation QUnit.test("should add loading and error routes if _isRouterMapResult is true", function() { Router.map(function() { this.route('blork'); }); var router = Router.create(); router._initRouterJs(true); ok(router.router.recognizer.names['blork'], 'main route was created'); ok(router.router.recognizer.names['blork_loading'], 'loading route was added'); ok(router.router.recognizer.names['blork_error'], 'error route was added'); }); QUnit.test("should not add loading and error routes if _isRouterMapResult is false", function() { Router.map(function() { this.route('blork'); }); var router = Router.create(); router._initRouterJs(false); ok(router.router.recognizer.names['blork'], 'main route was created'); ok(!router.router.recognizer.names['blork_loading'], 'loading route was not added'); ok(!router.router.recognizer.names['blork_error'], 'error route was not added'); }); // jscs:enable validateIndentation }); enifed('ember-routing/tests/system/dsl_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/system'); test('ember-routing/tests/system/dsl_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/system/dsl_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/system/dsl_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/system'); test('ember-routing/tests/system/dsl_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/system/dsl_test.js should pass jshint.'); }); }); enifed('ember-routing/tests/system/route_test', ['ember-runtime/tests/utils', 'container/registry', 'ember-runtime/system/service', 'ember-runtime/system/object', 'ember-routing/system/route', 'ember-runtime/inject'], function (utils, Registry, Service, EmberObject, EmberRoute, inject) { 'use strict'; var route, routeOne, routeTwo, lookupHash; function setup() { route = EmberRoute['default'].create(); } function teardown() { utils.runDestroy(route); } QUnit.module("Ember.Route", { setup: setup, teardown: teardown }); QUnit.test("default store utilizes the container to acquire the model factory", function() { expect(4); var Post = EmberObject['default'].extend(); var post = {}; Post.reopenClass({ find: function(id) { return post; } }); route.container = { has: function() { return true; }, lookupFactory: function(fullName) { equal(fullName, "model:post", "correct factory was looked up"); return Post; } }; route.set('_qp', null); equal(route.model({ post_id: 1 }), post); equal(route.findModel('post', 1), post, '#findModel returns the correct post'); }); QUnit.test("'store' can be injected by data persistence frameworks", function() { expect(8); utils.runDestroy(route); var registry = new Registry['default'](); var container = registry.container(); var post = { id: 1 }; var Store = EmberObject['default'].extend({ find: function(type, value) { ok(true, 'injected model was called'); equal(type, 'post', 'correct type was called'); equal(value, 1, 'correct value was called'); return post; } }); registry.register('route:index', EmberRoute['default']); registry.register('store:main', Store); registry.injection('route', 'store', 'store:main'); route = container.lookup('route:index'); equal(route.model({ post_id: 1 }), post, '#model returns the correct post'); equal(route.findModel('post', 1), post, '#findModel returns the correct post'); }); QUnit.test("assert if 'store.find' method is not found", function() { expect(1); utils.runDestroy(route); var registry = new Registry['default'](); var container = registry.container(); var Post = EmberObject['default'].extend(); registry.register('route:index', EmberRoute['default']); registry.register('model:post', Post); route = container.lookup('route:index'); expectAssertion(function() { route.findModel('post', 1); }, 'Post has no method `find`.'); }); QUnit.test("asserts if model class is not found", function() { expect(1); utils.runDestroy(route); var registry = new Registry['default'](); var container = registry.container(); registry.register('route:index', EmberRoute['default']); route = container.lookup('route:index'); expectAssertion(function() { route.model({ post_id: 1 }); }, "You used the dynamic segment post_id in your route undefined, but undefined.Post did not exist and you did not override your route's `model` hook."); }); QUnit.test("'store' does not need to be injected", function() { expect(1); utils.runDestroy(route); var registry = new Registry['default'](); var container = registry.container(); registry.register('route:index', EmberRoute['default']); route = container.lookup('route:index'); ignoreAssertion(function() { route.model({ post_id: 1 }); }); ok(true, 'no error was raised'); }); QUnit.test("modelFor doesn't require the router", function() { var registry = new Registry['default'](); var container = registry.container(); route.container = container; var foo = { name: 'foo' }; var fooRoute = EmberRoute['default'].extend({ container: container, currentModel: foo }); registry.register('route:foo', fooRoute); equal(route.modelFor('foo'), foo); }); QUnit.test(".send just calls an action if the router is absent", function() { expect(7); var route = EmberRoute['default'].createWithMixins({ actions: { returnsTrue: function(foo, bar) { equal(foo, 1); equal(bar, 2); equal(this, route); return true; }, returnsFalse: function() { ok(true, "returnsFalse was called"); return false; } } }); equal(true, route.send('returnsTrue', 1, 2)); equal(false, route.send('returnsFalse')); equal(undefined, route.send('nonexistent', 1, 2, 3)); }); QUnit.module("Ember.Route serialize", { setup: setup, teardown: teardown }); QUnit.test("returns the models properties if params does not include *_id", function() { var model = { id: 2, firstName: 'Ned', lastName: 'Ryerson' }; deepEqual(route.serialize(model, ['firstName', 'lastName']), { firstName: 'Ned', lastName: 'Ryerson' }, "serialized correctly"); }); QUnit.test("returns model.id if params include *_id", function() { var model = { id: 2 }; deepEqual(route.serialize(model, ['post_id']), { post_id: 2 }, "serialized correctly"); }); QUnit.test("returns checks for existence of model.post_id before trying model.id", function() { var model = { post_id: 3 }; deepEqual(route.serialize(model, ['post_id']), { post_id: 3 }, "serialized correctly"); }); QUnit.test("returns undefined if model is not set", function() { equal(route.serialize(undefined, ['post_id']), undefined, "serialized correctly"); }); QUnit.module("Ember.Route interaction", { setup: function() { var container = { lookup: function(fullName) { return lookupHash[fullName]; } }; routeOne = EmberRoute['default'].create({ container: container, routeName: 'one' }); routeTwo = EmberRoute['default'].create({ container: container, routeName: 'two' }); lookupHash = { 'route:one': routeOne, 'route:two': routeTwo }; }, teardown: function() { utils.runDestroy(routeOne); utils.runDestroy(routeTwo); } }); QUnit.test("controllerFor uses route's controllerName if specified", function() { var testController = {}; lookupHash['controller:test'] = testController; routeOne.controllerName = 'test'; equal(routeTwo.controllerFor('one'), testController); }); QUnit.module('Route injected properties'); QUnit.test("services can be injected into routes", function() { var registry = new Registry['default'](); var container = registry.container(); registry.register('route:application', EmberRoute['default'].extend({ authService: inject['default'].service('auth') })); registry.register('service:auth', Service['default'].extend()); var appRoute = container.lookup('route:application'); var authService = container.lookup('service:auth'); equal(authService, appRoute.get('authService'), "service.auth is injected"); }); }); enifed('ember-routing/tests/system/route_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/system'); test('ember-routing/tests/system/route_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/system/route_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/system/route_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/system'); test('ember-routing/tests/system/route_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/system/route_test.js should pass jshint.'); }); }); enifed('ember-routing/tests/system/router_test', ['ember-runtime/copy', 'ember-metal/merge', 'ember-metal/enumerable_utils', 'container/registry', 'ember-routing/location/hash_location', 'ember-routing/location/auto_location', 'ember-routing/system/router', 'ember-runtime/tests/utils'], function (copy, merge, enumerable_utils, Registry, HashLocation, AutoLocation, Router, utils) { 'use strict'; var registry, container; function createRouter(overrides) { var opts = merge['default']({ container: container }, overrides); var routerWithContainer = Router['default'].extend(); return routerWithContainer.create(opts); } QUnit.module("Ember Router", { setup: function() { registry = new Registry['default'](); container = registry.container(); //register the HashLocation (the default) registry.register('location:hash', HashLocation['default']); // ensure rootURL is injected into any locations registry.injection('location', 'rootURL', '-location-setting:root-url'); }, teardown: function() { utils.runDestroy(container); registry = container = null; } }); QUnit.test("can create a router without a container", function() { createRouter({ container: null }); ok(true, 'no errors were thrown when creating without a container'); }); QUnit.test("should not create a router.js instance upon init", function() { var router = createRouter(); ok(!router.router); }); QUnit.test("should destroy its location upon destroying the routers container.", function() { var router = createRouter(); var location = router.get('location'); utils.runDestroy(container); ok(location.isDestroyed, "location should be destroyed"); }); QUnit.test("should instantiate its location with its `rootURL`", function() { var router = createRouter({ rootURL: '/rootdir/' }); var location = router.get('location'); equal(location.get('rootURL'), '/rootdir/'); }); QUnit.test("Ember.AutoLocation._replacePath should be called with the right path", function() { expect(1); var AutoTestLocation = copy['default'](AutoLocation['default']); AutoTestLocation._location = { href: 'http://test.com/rootdir/welcome', origin: 'http://test.com', pathname: '/rootdir/welcome', hash: '', search: '', replace: function(url) { equal(url, 'http://test.com/rootdir/#/welcome'); } }; AutoTestLocation._getSupportsHistory = function() { return false; }; registry.register('location:auto', AutoTestLocation); createRouter({ location: 'auto', rootURL: '/rootdir/' }); }); QUnit.test("Ember.Router._routePath should consume identical prefixes", function() { createRouter(); expect(8); function routePath(s1, s2, s3) { var handlerInfos = enumerable_utils.map(arguments, function(s) { return { name: s }; }); handlerInfos.unshift({ name: 'ignored' }); return Router['default']._routePath(handlerInfos); } equal(routePath('foo'), 'foo'); equal(routePath('foo', 'bar', 'baz'), 'foo.bar.baz'); equal(routePath('foo', 'foo.bar'), 'foo.bar'); equal(routePath('foo', 'foo.bar', 'foo.bar.baz'), 'foo.bar.baz'); equal(routePath('foo', 'foo.bar', 'foo.bar.baz.wow'), 'foo.bar.baz.wow'); equal(routePath('foo', 'foo.bar.baz.wow'), 'foo.bar.baz.wow'); equal(routePath('foo.bar', 'bar.baz.wow'), 'foo.bar.baz.wow'); // This makes no sense, not trying to handle it, just // making sure it doesn't go boom. equal(routePath('foo.bar.baz', 'foo'), 'foo.bar.baz.foo'); }); QUnit.test("Router should cancel routing setup when the Location class says so via cancelRouterSetup", function() { expect(0); var router; var FakeLocation = { cancelRouterSetup: true, create: function () { return this; } }; registry.register('location:fake', FakeLocation); router = createRouter({ container: container, location: 'fake', _setupRouter: function () { ok(false, '_setupRouter should not be called'); } }); router.startRouting(); }); QUnit.test("AutoLocation should replace the url when it's not in the preferred format", function() { expect(1); var AutoTestLocation = copy['default'](AutoLocation['default']); AutoTestLocation._location = { href: 'http://test.com/rootdir/welcome', origin: 'http://test.com', pathname: '/rootdir/welcome', hash: '', search: '', replace: function(url) { equal(url, 'http://test.com/rootdir/#/welcome'); } }; AutoTestLocation._getSupportsHistory = function() { return false; }; registry.register('location:auto', AutoTestLocation); createRouter({ location: 'auto', rootURL: '/rootdir/' }); }); QUnit.test("Router#handleURL should remove any #hashes before doing URL transition", function() { expect(2); var router = createRouter({ container: container, _doURLTransition: function (routerJsMethod, url) { equal(routerJsMethod, 'handleURL'); equal(url, '/foo/bar?time=morphin'); } }); router.handleURL('/foo/bar?time=morphin#pink-power-ranger'); }); }); enifed('ember-routing/tests/system/router_test.jscs-test', function () { 'use strict'; module('JSCS - ember-routing/tests/system'); test('ember-routing/tests/system/router_test.js should pass jscs', function() { ok(true, 'ember-routing/tests/system/router_test.js should pass jscs.'); }); }); enifed('ember-routing/tests/system/router_test.jshint', function () { 'use strict'; module('JSHint - ember-routing/tests/system'); test('ember-routing/tests/system/router_test.js should pass jshint', function() { ok(true, 'ember-routing/tests/system/router_test.js should pass jshint.'); }); }); enifed('ember-routing/utils.jscs-test', function () { 'use strict'; module('JSCS - ember-routing'); test('ember-routing/utils.js should pass jscs', function() { ok(true, 'ember-routing/utils.js should pass jscs.'); }); }); enifed('ember-routing/utils.jshint', function () { 'use strict'; module('JSHint - ember-routing'); test('ember-routing/utils.js should pass jshint', function() { ok(true, 'ember-routing/utils.js should pass jshint.'); }); }); enifed('ember-runtime.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-runtime.js should pass jscs', function() { ok(true, 'ember-runtime.js should pass jscs.'); }); }); enifed('ember-runtime.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-runtime.js should pass jshint', function() { ok(true, 'ember-runtime.js should pass jshint.'); }); }); enifed('ember-runtime/compare.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime'); test('ember-runtime/compare.js should pass jscs', function() { ok(true, 'ember-runtime/compare.js should pass jscs.'); }); }); enifed('ember-runtime/compare.jshint', function () { 'use strict'; module('JSHint - ember-runtime'); test('ember-runtime/compare.js should pass jshint', function() { ok(true, 'ember-runtime/compare.js should pass jshint.'); }); }); enifed('ember-runtime/computed/array_computed.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/computed'); test('ember-runtime/computed/array_computed.js should pass jscs', function() { ok(true, 'ember-runtime/computed/array_computed.js should pass jscs.'); }); }); enifed('ember-runtime/computed/array_computed.jshint', function () { 'use strict'; module('JSHint - ember-runtime/computed'); test('ember-runtime/computed/array_computed.js should pass jshint', function() { ok(true, 'ember-runtime/computed/array_computed.js should pass jshint.'); }); }); enifed('ember-runtime/computed/reduce_computed.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/computed'); test('ember-runtime/computed/reduce_computed.js should pass jscs', function() { ok(true, 'ember-runtime/computed/reduce_computed.js should pass jscs.'); }); }); enifed('ember-runtime/computed/reduce_computed.jshint', function () { 'use strict'; module('JSHint - ember-runtime/computed'); test('ember-runtime/computed/reduce_computed.js should pass jshint', function() { ok(true, 'ember-runtime/computed/reduce_computed.js should pass jshint.'); }); }); enifed('ember-runtime/computed/reduce_computed_macros.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/computed'); test('ember-runtime/computed/reduce_computed_macros.js should pass jscs', function() { ok(true, 'ember-runtime/computed/reduce_computed_macros.js should pass jscs.'); }); }); enifed('ember-runtime/computed/reduce_computed_macros.jshint', function () { 'use strict'; module('JSHint - ember-runtime/computed'); test('ember-runtime/computed/reduce_computed_macros.js should pass jshint', function() { ok(true, 'ember-runtime/computed/reduce_computed_macros.js should pass jshint.'); }); }); enifed('ember-runtime/controllers/array_controller.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/controllers'); test('ember-runtime/controllers/array_controller.js should pass jscs', function() { ok(true, 'ember-runtime/controllers/array_controller.js should pass jscs.'); }); }); enifed('ember-runtime/controllers/array_controller.jshint', function () { 'use strict'; module('JSHint - ember-runtime/controllers'); test('ember-runtime/controllers/array_controller.js should pass jshint', function() { ok(true, 'ember-runtime/controllers/array_controller.js should pass jshint.'); }); }); enifed('ember-runtime/controllers/controller.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/controllers'); test('ember-runtime/controllers/controller.js should pass jscs', function() { ok(true, 'ember-runtime/controllers/controller.js should pass jscs.'); }); }); enifed('ember-runtime/controllers/controller.jshint', function () { 'use strict'; module('JSHint - ember-runtime/controllers'); test('ember-runtime/controllers/controller.js should pass jshint', function() { ok(true, 'ember-runtime/controllers/controller.js should pass jshint.'); }); }); enifed('ember-runtime/controllers/object_controller.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/controllers'); test('ember-runtime/controllers/object_controller.js should pass jscs', function() { ok(true, 'ember-runtime/controllers/object_controller.js should pass jscs.'); }); }); enifed('ember-runtime/controllers/object_controller.jshint', function () { 'use strict'; module('JSHint - ember-runtime/controllers'); test('ember-runtime/controllers/object_controller.js should pass jshint', function() { ok(true, 'ember-runtime/controllers/object_controller.js should pass jshint.'); }); }); enifed('ember-runtime/copy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime'); test('ember-runtime/copy.js should pass jscs', function() { ok(true, 'ember-runtime/copy.js should pass jscs.'); }); }); enifed('ember-runtime/copy.jshint', function () { 'use strict'; module('JSHint - ember-runtime'); test('ember-runtime/copy.js should pass jshint', function() { ok(true, 'ember-runtime/copy.js should pass jshint.'); }); }); enifed('ember-runtime/core.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime'); test('ember-runtime/core.js should pass jscs', function() { ok(true, 'ember-runtime/core.js should pass jscs.'); }); }); enifed('ember-runtime/core.jshint', function () { 'use strict'; module('JSHint - ember-runtime'); test('ember-runtime/core.js should pass jshint', function() { ok(true, 'ember-runtime/core.js should pass jshint.'); }); }); enifed('ember-runtime/ext/function.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/ext'); test('ember-runtime/ext/function.js should pass jscs', function() { ok(true, 'ember-runtime/ext/function.js should pass jscs.'); }); }); enifed('ember-runtime/ext/function.jshint', function () { 'use strict'; module('JSHint - ember-runtime/ext'); test('ember-runtime/ext/function.js should pass jshint', function() { ok(true, 'ember-runtime/ext/function.js should pass jshint.'); }); }); enifed('ember-runtime/ext/rsvp.jscs-test', function () { 'use strict'; }); enifed('ember-runtime/ext/rsvp.jshint', function () { 'use strict'; module('JSHint - ember-runtime/ext'); test('ember-runtime/ext/rsvp.js should pass jshint', function() { ok(true, 'ember-runtime/ext/rsvp.js should pass jshint.'); }); }); enifed('ember-runtime/ext/string.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/ext'); test('ember-runtime/ext/string.js should pass jscs', function() { ok(true, 'ember-runtime/ext/string.js should pass jscs.'); }); }); enifed('ember-runtime/ext/string.jshint', function () { 'use strict'; module('JSHint - ember-runtime/ext'); test('ember-runtime/ext/string.js should pass jshint', function() { ok(true, 'ember-runtime/ext/string.js should pass jshint.'); }); }); enifed('ember-runtime/inject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime'); test('ember-runtime/inject.js should pass jscs', function() { ok(true, 'ember-runtime/inject.js should pass jscs.'); }); }); enifed('ember-runtime/inject.jshint', function () { 'use strict'; module('JSHint - ember-runtime'); test('ember-runtime/inject.js should pass jshint', function() { ok(true, 'ember-runtime/inject.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/-proxy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/-proxy.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/-proxy.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/-proxy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/-proxy.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/-proxy.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/action_handler.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/action_handler.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/action_handler.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/action_handler.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/action_handler.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/action_handler.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/array.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/array.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/array.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/array.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/array.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/array.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/comparable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/comparable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/comparable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/comparable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/comparable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/comparable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/controller.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/controller.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/controller.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/controller.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/controller.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/controller.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/controller_content_model_alias_deprecation.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/controller_content_model_alias_deprecation.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/controller_content_model_alias_deprecation.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/controller_content_model_alias_deprecation.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/controller_content_model_alias_deprecation.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/controller_content_model_alias_deprecation.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/copyable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/copyable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/copyable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/copyable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/copyable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/copyable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/deferred.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/deferred.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/deferred.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/deferred.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/deferred.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/deferred.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/enumerable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/enumerable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/enumerable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/enumerable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/enumerable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/enumerable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/evented.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/evented.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/evented.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/evented.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/evented.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/evented.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/freezable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/freezable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/freezable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/freezable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/freezable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/freezable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/mutable_array.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/mutable_array.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/mutable_array.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/mutable_array.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/mutable_array.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/mutable_array.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/mutable_enumerable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/mutable_enumerable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/mutable_enumerable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/mutable_enumerable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/mutable_enumerable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/mutable_enumerable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/observable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/observable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/observable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/observable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/observable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/observable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/promise_proxy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/promise_proxy.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/promise_proxy.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/promise_proxy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/promise_proxy.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/promise_proxy.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/sortable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/sortable.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/sortable.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/sortable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/sortable.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/sortable.js should pass jshint.'); }); }); enifed('ember-runtime/mixins/target_action_support.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/mixins'); test('ember-runtime/mixins/target_action_support.js should pass jscs', function() { ok(true, 'ember-runtime/mixins/target_action_support.js should pass jscs.'); }); }); enifed('ember-runtime/mixins/target_action_support.jshint', function () { 'use strict'; module('JSHint - ember-runtime/mixins'); test('ember-runtime/mixins/target_action_support.js should pass jshint', function() { ok(true, 'ember-runtime/mixins/target_action_support.js should pass jshint.'); }); }); enifed('ember-runtime/system/application.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/application.js should pass jscs', function() { ok(true, 'ember-runtime/system/application.js should pass jscs.'); }); }); enifed('ember-runtime/system/application.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/application.js should pass jshint', function() { ok(true, 'ember-runtime/system/application.js should pass jshint.'); }); }); enifed('ember-runtime/system/array_proxy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/array_proxy.js should pass jscs', function() { ok(true, 'ember-runtime/system/array_proxy.js should pass jscs.'); }); }); enifed('ember-runtime/system/array_proxy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/array_proxy.js should pass jshint', function() { ok(true, 'ember-runtime/system/array_proxy.js should pass jshint.'); }); }); enifed('ember-runtime/system/container.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/container.js should pass jscs', function() { ok(true, 'ember-runtime/system/container.js should pass jscs.'); }); }); enifed('ember-runtime/system/container.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/container.js should pass jshint', function() { ok(true, 'ember-runtime/system/container.js should pass jshint.'); }); }); enifed('ember-runtime/system/core_object.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/core_object.js should pass jscs', function() { ok(true, 'ember-runtime/system/core_object.js should pass jscs.'); }); }); enifed('ember-runtime/system/core_object.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/core_object.js should pass jshint', function() { ok(true, 'ember-runtime/system/core_object.js should pass jshint.'); }); }); enifed('ember-runtime/system/deferred.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/deferred.js should pass jscs', function() { ok(true, 'ember-runtime/system/deferred.js should pass jscs.'); }); }); enifed('ember-runtime/system/deferred.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/deferred.js should pass jshint', function() { ok(true, 'ember-runtime/system/deferred.js should pass jshint.'); }); }); enifed('ember-runtime/system/each_proxy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/each_proxy.js should pass jscs', function() { ok(true, 'ember-runtime/system/each_proxy.js should pass jscs.'); }); }); enifed('ember-runtime/system/each_proxy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/each_proxy.js should pass jshint', function() { ok(true, 'ember-runtime/system/each_proxy.js should pass jshint.'); }); }); enifed('ember-runtime/system/lazy_load.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/lazy_load.js should pass jscs', function() { ok(true, 'ember-runtime/system/lazy_load.js should pass jscs.'); }); }); enifed('ember-runtime/system/lazy_load.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/lazy_load.js should pass jshint', function() { ok(true, 'ember-runtime/system/lazy_load.js should pass jshint.'); }); }); enifed('ember-runtime/system/namespace.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/namespace.js should pass jscs', function() { ok(true, 'ember-runtime/system/namespace.js should pass jscs.'); }); }); enifed('ember-runtime/system/namespace.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/namespace.js should pass jshint', function() { ok(true, 'ember-runtime/system/namespace.js should pass jshint.'); }); }); enifed('ember-runtime/system/native_array.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/native_array.js should pass jscs', function() { ok(true, 'ember-runtime/system/native_array.js should pass jscs.'); }); }); enifed('ember-runtime/system/native_array.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/native_array.js should pass jshint', function() { ok(true, 'ember-runtime/system/native_array.js should pass jshint.'); }); }); enifed('ember-runtime/system/object.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/object.js should pass jscs', function() { ok(true, 'ember-runtime/system/object.js should pass jscs.'); }); }); enifed('ember-runtime/system/object.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/object.js should pass jshint', function() { ok(true, 'ember-runtime/system/object.js should pass jshint.'); }); }); enifed('ember-runtime/system/object_proxy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/object_proxy.js should pass jscs', function() { ok(true, 'ember-runtime/system/object_proxy.js should pass jscs.'); }); }); enifed('ember-runtime/system/object_proxy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/object_proxy.js should pass jshint', function() { ok(true, 'ember-runtime/system/object_proxy.js should pass jshint.'); }); }); enifed('ember-runtime/system/service.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/service.js should pass jscs', function() { ok(true, 'ember-runtime/system/service.js should pass jscs.'); }); }); enifed('ember-runtime/system/service.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/service.js should pass jshint', function() { ok(true, 'ember-runtime/system/service.js should pass jshint.'); }); }); enifed('ember-runtime/system/set.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/set.js should pass jscs', function() { ok(true, 'ember-runtime/system/set.js should pass jscs.'); }); }); enifed('ember-runtime/system/set.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/set.js should pass jshint', function() { ok(true, 'ember-runtime/system/set.js should pass jshint.'); }); }); enifed('ember-runtime/system/string.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/string.js should pass jscs', function() { ok(true, 'ember-runtime/system/string.js should pass jscs.'); }); }); enifed('ember-runtime/system/string.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/string.js should pass jshint', function() { ok(true, 'ember-runtime/system/string.js should pass jshint.'); }); }); enifed('ember-runtime/system/subarray.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/subarray.js should pass jscs', function() { ok(true, 'ember-runtime/system/subarray.js should pass jscs.'); }); }); enifed('ember-runtime/system/subarray.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/subarray.js should pass jshint', function() { ok(true, 'ember-runtime/system/subarray.js should pass jshint.'); }); }); enifed('ember-runtime/system/tracked_array.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/system'); test('ember-runtime/system/tracked_array.js should pass jscs', function() { ok(true, 'ember-runtime/system/tracked_array.js should pass jscs.'); }); }); enifed('ember-runtime/system/tracked_array.jshint', function () { 'use strict'; module('JSHint - ember-runtime/system'); test('ember-runtime/system/tracked_array.js should pass jshint', function() { ok(true, 'ember-runtime/system/tracked_array.js should pass jshint.'); }); }); enifed('ember-runtime/tests/computed/computed_macros_test', ['ember-metal/computed_macros', 'ember-runtime/system/object', 'ember-metal/tests/props_helper'], function (computed_macros, EmberObject, props_helper) { 'use strict'; QUnit.module('CP macros'); props_helper.testBoth('Ember.computed.empty', function (get, set) { var obj = EmberObject['default'].extend({ bestLannister: null, lannisters: null, bestLannisterUnspecified: computed_macros.empty('bestLannister'), noLannistersKnown: computed_macros.empty('lannisters') }).create({ lannisters: Ember.A([]) }); equal(get(obj, 'bestLannisterUnspecified'), true, "bestLannister initially empty"); equal(get(obj, 'noLannistersKnown'), true, "lannisters initially empty"); get(obj, 'lannisters').pushObject('Tyrion'); set(obj, 'bestLannister', 'Tyrion'); equal(get(obj, 'bestLannisterUnspecified'), false, "empty respects strings"); equal(get(obj, 'noLannistersKnown'), false, "empty respects array mutations"); }); props_helper.testBoth('Ember.computed.notEmpty', function(get, set) { var obj = EmberObject['default'].extend({ bestLannister: null, lannisters: null, bestLannisterSpecified: computed_macros.notEmpty('bestLannister'), LannistersKnown: computed_macros.notEmpty('lannisters') }).create({ lannisters: Ember.A([]) }); equal(get(obj, 'bestLannisterSpecified'), false, "bestLannister initially empty"); equal(get(obj, 'LannistersKnown'), false, "lannisters initially empty"); get(obj, 'lannisters').pushObject('Tyrion'); set(obj, 'bestLannister', 'Tyrion'); equal(get(obj, 'bestLannisterSpecified'), true, "empty respects strings"); equal(get(obj, 'LannistersKnown'), true, "empty respects array mutations"); }); }); enifed('ember-runtime/tests/computed/computed_macros_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/computed_macros_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/computed/computed_macros_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/computed/computed_macros_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/computed_macros_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/computed/computed_macros_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/computed/reduce_computed_macros_test', ['ember-metal/core', 'ember-runtime/system/object', 'ember-metal/set_properties', 'ember-runtime/system/object_proxy', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-metal/observer', 'ember-metal/property_events', 'ember-metal/array', 'ember-metal/mixin', 'ember-runtime/computed/reduce_computed_macros'], function (Ember, EmberObject, setProperties, ObjectProxy, property_get, property_set, run, observer, property_events, ember_metal__array, mixin, reduce_computed_macros) { 'use strict'; var obj, sorted, sortProps, items, userFnCalls, todos, filtered, union; QUnit.module('computedMap', { setup: function() { run['default'](function() { userFnCalls = 0; obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([{ v: 1 }, { v: 3 }, { v: 2 }, { v: 1 }]), mapped: reduce_computed_macros.map('array.@each.v', function(item) { ++userFnCalls; return item.v; }), arrayObjects: Ember['default'].A([ EmberObject['default'].create({ v: { name: 'Robert' } }), EmberObject['default'].create({ v: { name: 'Leanna' } })]), mappedObjects: reduce_computed_macros.map('arrayObjects.@each.v', function (item) { return { name: item.v.name }; }) }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it maps simple properties", function() { deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 1]); run['default'](function() { obj.get('array').pushObject({ v: 5 }); }); deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 1, 5]); run['default'](function() { obj.get('array').removeAt(3); }); deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 5]); }); QUnit.test("it caches properly", function() { var array = property_get.get(obj, 'array'); property_get.get(obj, 'mapped'); equal(userFnCalls, 4, "precond - mapper called expected number of times"); run['default'](function() { array.addObject({ v: 7 }); }); equal(userFnCalls, 5, "precond - mapper called expected number of times"); property_get.get(obj, 'mapped'); equal(userFnCalls, 5, "computedMap caches properly"); }); QUnit.test("it maps simple unshifted properties", function() { var array = Ember['default'].A([]); run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: array, mapped: reduce_computed_macros.map('array', function (item) { return item.toUpperCase(); }) }); property_get.get(obj, 'mapped'); }); run['default'](function() { array.unshiftObject('c'); array.unshiftObject('b'); array.unshiftObject('a'); array.popObject(); }); deepEqual(property_get.get(obj, 'mapped'), ['A', 'B'], "properties unshifted in sequence are mapped correctly"); }); QUnit.test("it passes the index to the callback", function() { var array = Ember['default'].A(['a', 'b', 'c']); run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: array, mapped: reduce_computed_macros.map('array', function (item, index) { return index; }) }); property_get.get(obj, 'mapped'); }); deepEqual(property_get.get(obj, 'mapped'), [0, 1, 2], "index is passed to callback correctly"); }); QUnit.test("it maps objects", function() { deepEqual(property_get.get(obj, 'mappedObjects'), [{ name: 'Robert' }, { name: 'Leanna' }]); run['default'](function() { obj.get('arrayObjects').pushObject({ v: { name: 'Eddard' } }); }); deepEqual(property_get.get(obj, 'mappedObjects'), [{ name: 'Robert' }, { name: 'Leanna' }, { name: 'Eddard' }]); run['default'](function() { obj.get('arrayObjects').removeAt(1); }); deepEqual(property_get.get(obj, 'mappedObjects'), [{ name: 'Robert' }, { name: 'Eddard' }]); run['default'](function() { obj.get('arrayObjects').objectAt(0).set('v', { name: 'Stannis' }); }); deepEqual(property_get.get(obj, 'mappedObjects'), [{ name: 'Stannis' }, { name: 'Eddard' }]); }); QUnit.test("it maps unshifted objects with property observers", function() { var array = Ember['default'].A([]); var cObj = { v: 'c' }; run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: array, mapped: reduce_computed_macros.map('array.@each.v', function (item) { return property_get.get(item, 'v').toUpperCase(); }) }); property_get.get(obj, 'mapped'); }); run['default'](function() { array.unshiftObject(cObj); array.unshiftObject({ v: 'b' }); array.unshiftObject({ v: 'a' }); property_set.set(cObj, 'v', 'd'); }); deepEqual(array.mapBy('v'), ['a', 'b', 'd'], "precond - unmapped array is correct"); deepEqual(property_get.get(obj, 'mapped'), ['A', 'B', 'D'], "properties unshifted in sequence are mapped correctly"); }); QUnit.module('computedMapBy', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([{ v: 1 }, { v: 3 }, { v: 2 }, { v: 1 }]), mapped: reduce_computed_macros.mapBy('array', 'v') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it maps properties", function() { property_get.get(obj, 'mapped'); deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 1]); run['default'](function() { obj.get('array').pushObject({ v: 5 }); }); deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 1, 5]); run['default'](function() { obj.get('array').removeAt(3); }); deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 5]); }); QUnit.test("it is observable", function() { property_get.get(obj, 'mapped'); var calls = 0; deepEqual(property_get.get(obj, 'mapped'), [1, 3, 2, 1]); observer.addObserver(obj, 'mapped.@each', function() { calls++; }); run['default'](function() { obj.get('array').pushObject({ v: 5 }); }); equal(calls, 1, 'computedMapBy is observable'); }); QUnit.module('computedFilter', { setup: function() { run['default'](function() { userFnCalls = 0; obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([1, 2, 3, 4, 5, 6, 7, 8]), filtered: reduce_computed_macros.filter('array', function(item) { ++userFnCalls; return item % 2 === 0; }) }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it filters according to the specified filter function", function() { var filtered = property_get.get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "computedFilter filters by the specified function"); }); QUnit.test("it passes the index to the callback", function() { var array = Ember['default'].A(['a', 'b', 'c']); run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: array, filtered: reduce_computed_macros.filter('array', function (item, index) { return index === 1; }) }); property_get.get(obj, 'filtered'); }); deepEqual(property_get.get(obj, 'filtered'), ['b'], "index is passed to callback correctly"); }); QUnit.test("it passes the array to the callback", function() { var array = Ember['default'].A(['a', 'b', 'c']); run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: array, filtered: reduce_computed_macros.filter('array', function (item, index, array) { return index === array.get('length') - 2; }) }); property_get.get(obj, 'filtered'); }); deepEqual(property_get.get(obj, 'filtered'), ['b'], "array is passed to callback correctly"); }); QUnit.test("it caches properly", function() { var array = property_get.get(obj, 'array'); property_get.get(obj, 'filtered'); equal(userFnCalls, 8, "precond - filter called expected number of times"); run['default'](function() { array.addObject(11); }); equal(userFnCalls, 9, "precond - filter called expected number of times"); property_get.get(obj, 'filtered'); equal(userFnCalls, 9, "computedFilter caches properly"); }); QUnit.test("it updates as the array is modified", function() { var array = property_get.get(obj, 'array'); var filtered = property_get.get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run['default'](function() { array.addObject(11); }); deepEqual(filtered, [2,4,6,8], "objects not passing the filter are not added"); run['default'](function() { array.addObject(12); }); deepEqual(filtered, [2,4,6,8,12], "objects passing the filter are added"); run['default'](function() { array.removeObject(3); array.removeObject(4); }); deepEqual(filtered, [2,6,8,12], "objects removed from the dependent array are removed from the computed array"); }); QUnit.test("the dependent array can be cleared one at a time", function() { var array = property_get.get(obj, 'array'); var filtered = property_get.get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run['default'](function() { // clear 1-8 but in a random order array.removeObject(3); array.removeObject(1); array.removeObject(2); array.removeObject(4); array.removeObject(8); array.removeObject(6); array.removeObject(5); array.removeObject(7); }); deepEqual(filtered, [], "filtered array cleared correctly"); }); QUnit.test("the dependent array can be `clear`ed directly (#3272)", function() { var array = property_get.get(obj, 'array'); var filtered = property_get.get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run['default'](function() { array.clear(); }); deepEqual(filtered, [], "filtered array cleared correctly"); }); QUnit.test("it updates as the array is replaced", function() { property_get.get(obj, 'array'); var filtered = property_get.get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run['default'](function() { property_set.set(obj, 'array', Ember['default'].A([20,21,22,23,24])); }); deepEqual(filtered, [20,22,24], "computed array is updated when array is changed"); }); QUnit.module('computedFilterBy', { setup: function() { obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([ { name: "one", a: 1, b: false }, { name: "two", a: 2, b: false }, { name: "three", a: 1, b: true }, { name: "four", b: true } ]), a1s: reduce_computed_macros.filterBy('array', 'a', 1), as: reduce_computed_macros.filterBy('array', 'a'), bs: reduce_computed_macros.filterBy('array', 'b') }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("properties can be filtered by truthiness", function() { var array = property_get.get(obj, 'array'); var as = property_get.get(obj, 'as'); var bs = property_get.get(obj, 'bs'); deepEqual(as.mapBy('name'), ['one', 'two', 'three'], "properties can be filtered by existence"); deepEqual(bs.mapBy('name'), ['three', 'four'], "booleans can be filtered"); run['default'](function() { property_set.set(array.objectAt(0), 'a', undefined); property_set.set(array.objectAt(3), 'a', true); property_set.set(array.objectAt(0), 'b', true); property_set.set(array.objectAt(3), 'b', false); }); deepEqual(as.mapBy('name'), ['two', 'three', 'four'], "arrays computed by filter property respond to property changes"); deepEqual(bs.mapBy('name'), ['one', 'three'], "arrays computed by filtered property respond to property changes"); run['default'](function() { array.pushObject({ name: "five", a: 6, b: true }); }); deepEqual(as.mapBy('name'), ['two', 'three', 'four', 'five'], "arrays computed by filter property respond to added objects"); deepEqual(bs.mapBy('name'), ['one', 'three', 'five'], "arrays computed by filtered property respond to added objects"); run['default'](function() { array.popObject(); }); deepEqual(as.mapBy('name'), ['two', 'three', 'four'], "arrays computed by filter property respond to removed objects"); deepEqual(bs.mapBy('name'), ['one', 'three'], "arrays computed by filtered property respond to removed objects"); run['default'](function() { property_set.set(obj, 'array', Ember['default'].A([{ name: "six", a: 12, b: true }])); }); deepEqual(as.mapBy('name'), ['six'], "arrays computed by filter property respond to array changes"); deepEqual(bs.mapBy('name'), ['six'], "arrays computed by filtered property respond to array changes"); }); QUnit.test("properties can be filtered by values", function() { var array = property_get.get(obj, 'array'); var a1s = property_get.get(obj, 'a1s'); deepEqual(a1s.mapBy('name'), ['one', 'three'], "properties can be filtered by matching value"); run['default'](function() { array.pushObject({ name: "five", a: 1 }); }); deepEqual(a1s.mapBy('name'), ['one', 'three', 'five'], "arrays computed by matching value respond to added objects"); run['default'](function() { array.popObject(); }); deepEqual(a1s.mapBy('name'), ['one', 'three'], "arrays computed by matching value respond to removed objects"); run['default'](function() { property_set.set(array.objectAt(1), 'a', 1); property_set.set(array.objectAt(2), 'a', 2); }); deepEqual(a1s.mapBy('name'), ['one', 'two'], "arrays computed by matching value respond to modified properties"); }); QUnit.test("properties values can be replaced", function() { obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([]), a1s: reduce_computed_macros.filterBy('array', 'a', 1), a1bs: reduce_computed_macros.filterBy('a1s', 'b') }); var a1bs = property_get.get(obj, 'a1bs'); deepEqual(a1bs.mapBy('name'), [], "properties can be filtered by matching value"); run['default'](function() { property_set.set(obj, 'array', Ember['default'].A([{ name: 'item1', a: 1, b: true }])); }); a1bs = property_get.get(obj, 'a1bs'); deepEqual(a1bs.mapBy('name'), ['item1'], "properties can be filtered by matching value"); }); ember_metal__array.forEach.call([['uniq', reduce_computed_macros.uniq], ['union', reduce_computed_macros.union]], function (tuple) { var alias = tuple[0]; var testedFunc = tuple[1]; QUnit.module('computed.' + alias, { setup: function() { run['default'](function() { union = testedFunc('array', 'array2', 'array3'); obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([1,2,3,4,5,6]), array2: Ember['default'].A([4,5,6,7,8,9,4,5,6,7,8,9]), array3: Ember['default'].A([1,8,10]), union: union }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("does not include duplicates", function() { var array = property_get.get(obj, 'array'); var array2 = property_get.get(obj, 'array2'); property_get.get(obj, 'array3'); var union = property_get.get(obj, 'union'); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], alias + " does not include duplicates"); run['default'](function() { array.pushObject(8); }); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], alias + " does not add existing items"); run['default'](function() { array.pushObject(11); }); deepEqual(union, [1,2,3,4,5,6,7,8,9,10,11], alias + " adds new items"); run['default'](function() { array2.removeAt(6); // remove 7 }); deepEqual(union, [1,2,3,4,5,6,7,8,9,10,11], alias + " does not remove items that are still in the dependent array"); run['default'](function() { array2.removeObject(7); }); deepEqual(union, [1,2,3,4,5,6,8,9,10,11], alias + " removes items when their last instance is gone"); }); QUnit.test("has set-union semantics", function() { var array = property_get.get(obj, 'array'); property_get.get(obj, 'array2'); property_get.get(obj, 'array3'); var union = property_get.get(obj, 'union'); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], alias + " is initially correct"); run['default'](function() { array.removeObject(6); }); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], "objects are not removed if they exist in other dependent arrays"); run['default'](function() { array.clear(); }); deepEqual(union, [1,4,5,6,7,8,9,10], "objects are removed when they are no longer in any dependent array"); }); QUnit.test("does not need to query the accumulated array while building it", function() { var indexOfCalls = []; var CountIndexOfCalls = mixin.Mixin.create({ indexOf: function() { indexOfCalls.push(arguments); return this._super.apply(this, arguments); } }); union.initialValue = function() { return CountIndexOfCalls.apply(Ember['default'].A([])); }; property_get.get(obj, 'union'); ok(indexOfCalls.length === 0, "Ember.computed." + alias + " should not need to query the union as it is being built"); }); }); QUnit.module('computed.intersect', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([1,2,3,4,5,6]), array2: Ember['default'].A([3,3,3,4,5]), array3: Ember['default'].A([3,5,6,7,8]), intersection: reduce_computed_macros.intersect('array', 'array2', 'array3') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it has set-intersection semantics", function() { property_get.get(obj, 'array'); var array2 = property_get.get(obj, 'array2'); var array3 = property_get.get(obj, 'array3'); var intersection = property_get.get(obj, 'intersection'); deepEqual(intersection, [3,5], "intersection is initially correct"); run['default'](function() { array2.shiftObject(); }); deepEqual(intersection, [3,5], "objects are not removed when they are still in all dependent arrays"); run['default'](function() { array2.shiftObject(); }); deepEqual(intersection, [3,5], "objects are not removed when they are still in all dependent arrays"); run['default'](function() { array2.shiftObject(); }); deepEqual(intersection, [5], "objects are removed once they are gone from all dependent arrays"); run['default'](function() { array2.pushObject(1); }); deepEqual(intersection, [5], "objects are not added as long as they are missing from any dependent array"); run['default'](function() { array3.pushObject(1); }); deepEqual(intersection, [5,1], "objects added once they belong to all dependent arrays"); }); QUnit.module('computedSetDiff', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([1,2,3,4,5,6,7]), array2: Ember['default'].A([3,4,5,10]), diff: reduce_computed_macros.setDiff('array', 'array2') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it throws an error if given fewer or more than two dependent properties", function() { throws(function () { EmberObject['default'].createWithMixins({ array: Ember['default'].A([1,2,3,4,5,6,7]), array2: Ember['default'].A([3,4,5]), diff: reduce_computed_macros.setDiff('array') }); }, /requires exactly two dependent arrays/, "setDiff requires two dependent arrays"); throws(function () { EmberObject['default'].createWithMixins({ array: Ember['default'].A([1,2,3,4,5,6,7]), array2: Ember['default'].A([3,4,5]), array3: Ember['default'].A([7]), diff: reduce_computed_macros.setDiff('array', 'array2', 'array3') }); }, /requires exactly two dependent arrays/, "setDiff requires two dependent arrays"); }); QUnit.test("it has set-diff semantics", function() { var array1 = property_get.get(obj, 'array'); var array2 = property_get.get(obj, 'array2'); var diff = property_get.get(obj, 'diff'); deepEqual(diff, [1, 2, 6, 7], "set-diff is initially correct"); run['default'](function() { array2.popObject(); }); deepEqual(diff, [1,2,6,7], "removing objects from the remove set has no effect if the object is not in the keep set"); run['default'](function() { array2.shiftObject(); }); deepEqual(diff, [1, 2, 6, 7, 3], "removing objects from the remove set adds them if they're in the keep set"); run['default'](function() { array1.removeObject(3); }); deepEqual(diff, [1, 2, 6, 7], "removing objects from the keep array removes them from the computed array"); run['default'](function() { array1.pushObject(5); }); deepEqual(diff, [1, 2, 6, 7], "objects added to the keep array that are in the remove array are not added to the computed array"); run['default'](function() { array1.pushObject(22); }); deepEqual(diff, [1, 2, 6, 7, 22], "objects added to the keep array not in the remove array are added to the computed array"); }); function commonSortTests() { QUnit.test("arrays are initially sorted", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "array is initially sorted"); }); QUnit.test("changing the dependent array updates the sorted array", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(obj, 'items', Ember['default'].A([{ fname: 'Roose', lname: 'Bolton' }, { fname: 'Theon', lname: 'Greyjoy' }, { fname: 'Ramsey', lname: 'Bolton' }, { fname: 'Stannis', lname: 'Baratheon' }])); }); deepEqual(sorted.mapBy('fname'), ['Stannis', 'Ramsey', 'Roose', 'Theon'], "changing dependent array updates sorted array"); }); QUnit.test("adding to the dependent array updates the sorted array", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items = property_get.get(obj, 'items'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { items.pushObject({ fname: 'Tyrion', lname: 'Lannister' }); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb'], "Adding to the dependent array updates the sorted array"); }); QUnit.test("removing from the dependent array updates the sorted array", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items = property_get.get(obj, 'items'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { items.popObject(); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Robb'], "Removing from the dependent array updates the sorted array"); }); QUnit.test("distinct items may be sort-equal, although their relative order will not be guaranteed", function() { var jaime, jaimeInDisguise; run['default'](function() { // We recreate jaime and "Cersei" here only for test stability: we want // their guid-ordering to be deterministic jaimeInDisguise = EmberObject['default'].create({ fname: 'Cersei', lname: 'Lannister', age: 34 }); jaime = EmberObject['default'].create({ fname: 'Jaime', lname: 'Lannister', age: 34 }); items = property_get.get(obj, 'items'); items.replace(0, 1, jaime); items.replace(1, 1, jaimeInDisguise); sorted = property_get.get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { // comparator will now return 0. // Apparently it wasn't a very good disguise. jaimeInDisguise.set('fname', 'Jaime'); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Jaime', 'Bran', 'Robb'], "sorted array is updated"); run['default'](function() { // comparator will again return non-zero jaimeInDisguise.set('fname', 'Cersei'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "sorted array is updated"); }); QUnit.test("guid sort-order fallback with a search proxy is not confused by non-search ObjectProxys", function() { var tyrion = { fname: "Tyrion", lname: "Lannister" }; var tyrionInDisguise = ObjectProxy['default'].create({ fname: "Yollo", lname: "", content: tyrion }); items = property_get.get(obj, 'items'); run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items.pushObject(tyrion); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']); run['default'](function() { items.pushObject(tyrionInDisguise); }); deepEqual(sorted.mapBy('fname'), ['Yollo', 'Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']); }); } QUnit.module('computedSort - sortProperties', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ itemSorting: Ember['default'].A(['lname', 'fname']), items: Ember['default'].A([{ fname: "Jaime", lname: "Lannister", age: 34 }, { fname: "Cersei", lname: "Lannister", age: 34 }, { fname: "Robb", lname: "Stark", age: 16 }, { fname: "Bran", lname: "Stark", age: 8 }]), sortedItems: reduce_computed_macros.sort('items', 'itemSorting') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); commonSortTests(); QUnit.test("updating sort properties updates the sorted array", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(obj, 'itemSorting', Ember['default'].A(['fname:desc'])); }); deepEqual(sorted.mapBy('fname'), ['Robb', 'Jaime', 'Cersei', 'Bran'], "after updating sort properties array is updated"); }); QUnit.test("updating sort properties in place updates the sorted array", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); sortProps = property_get.get(obj, 'itemSorting'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { sortProps.clear(); sortProps.pushObject('fname'); }); deepEqual(sorted.mapBy('fname'), ['Bran', 'Cersei', 'Jaime', 'Robb'], "after updating sort properties array is updated"); }); QUnit.test("updating new sort properties in place updates the sorted array", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(obj, 'itemSorting', Ember['default'].A(['age:desc', 'fname:asc'])); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Robb', 'Bran'], "precond - array is correct after item sorting is changed"); run['default'](function() { items = property_get.get(obj, 'items'); var cersei = items.objectAt(1); property_set.set(cersei, 'age', 29); // how vain }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Cersei', 'Robb', 'Bran'], "after updating sort properties array is updated"); }); QUnit.test("sort direction defaults to ascending", function() { run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(obj, 'itemSorting', Ember['default'].A(['fname'])); }); deepEqual(sorted.mapBy('fname'), ['Bran', 'Cersei', 'Jaime', 'Robb'], "sort direction defaults to ascending"); }); QUnit.test("updating an item's sort properties updates the sorted array", function() { var tyrionInDisguise; run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items = property_get.get(obj, 'items'); }); tyrionInDisguise = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(tyrionInDisguise, 'fname', 'Tyrion'); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Tyrion', 'Bran', 'Robb'], "updating an item's sort properties updates the sorted array"); }); QUnit.test("updating several of an item's sort properties updated the sorted array", function() { var sansaInDisguise; run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items = property_get.get(obj, 'items'); }); sansaInDisguise = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { setProperties['default'](sansaInDisguise, { fname: 'Sansa', lname: 'Stark' }); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Bran', 'Robb', 'Sansa'], "updating an item's sort properties updates the sorted array"); }); QUnit.test("updating an item's sort properties does not error when binary search does a self compare (#3273)", function() { var jaime, cersei; run['default'](function() { jaime = EmberObject['default'].create({ name: 'Jaime', status: 1 }); cersei = EmberObject['default'].create({ name: 'Cersei', status: 2 }); obj = EmberObject['default'].createWithMixins({ people: Ember['default'].A([jaime, cersei]), sortProps: Ember['default'].A(['status']), sortedPeople: reduce_computed_macros.sort('people', 'sortProps') }); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei], "precond - array is initially sorted"); run['default'](function() { cersei.set('status', 3); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei], "array is sorted correctly"); run['default'](function() { cersei.set('status', 2); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei], "array is sorted correctly"); }); QUnit.test("property paths in sort properties update the sorted array", function () { var jaime, cersei, sansa; run['default'](function () { jaime = EmberObject['default'].create({ relatedObj: EmberObject['default'].create({ status: 1, firstName: 'Jaime', lastName: 'Lannister' }) }); cersei = EmberObject['default'].create({ relatedObj: EmberObject['default'].create({ status: 2, firstName: 'Cersei', lastName: 'Lannister' }) }); sansa = EmberObject['default'].create({ relatedObj: EmberObject['default'].create({ status: 3, firstName: 'Sansa', lastName: 'Stark' }) }); obj = EmberObject['default'].createWithMixins({ people: Ember['default'].A([jaime, cersei, sansa]), sortProps: Ember['default'].A(['relatedObj.status']), sortedPeople: reduce_computed_macros.sort('people', 'sortProps') }); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei, sansa], "precond - array is initially sorted"); run['default'](function () { cersei.set('status', 3); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei, sansa], "array is sorted correctly"); run['default'](function () { cersei.set('status', 1); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei, sansa], "array is sorted correctly"); run['default'](function () { sansa.set('status', 1); }); deepEqual(property_get.get(obj, 'sortedPeople'), [jaime, cersei, sansa], "array is sorted correctly"); run['default'](function () { obj.set('sortProps', Ember['default'].A(['relatedObj.firstName'])); }); deepEqual(property_get.get(obj, 'sortedPeople'), [cersei, jaime, sansa], "array is sorted correctly"); }); function sortByLnameFname(a, b) { var lna = property_get.get(a, 'lname'); var lnb = property_get.get(b, 'lname'); if (lna !== lnb) { return lna > lnb ? 1 : -1; } return sortByFnameAsc(a, b); } function sortByFnameAsc(a, b) { var fna = property_get.get(a, 'fname'); var fnb = property_get.get(b, 'fname'); if (fna === fnb) { return 0; } return fna > fnb ? 1 : -1; } QUnit.module('computedSort - sort function', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A([{ fname: "Jaime", lname: "Lannister", age: 34 }, { fname: "Cersei", lname: "Lannister", age: 34 }, { fname: "Robb", lname: "Stark", age: 16 }, { fname: "Bran", lname: "Stark", age: 8 }]), sortedItems: reduce_computed_macros.sort('items.@each.fname', sortByLnameFname) }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); commonSortTests(); QUnit.test("changing item properties specified via @each triggers a resort of the modified item", function() { var tyrionInDisguise; run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items = property_get.get(obj, 'items'); }); tyrionInDisguise = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(tyrionInDisguise, 'fname', 'Tyrion'); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Tyrion', 'Bran', 'Robb'], "updating a specified property on an item resorts it"); }); QUnit.test("changing item properties not specified via @each does not trigger a resort", function() { var cersei; run['default'](function() { sorted = property_get.get(obj, 'sortedItems'); items = property_get.get(obj, 'items'); }); cersei = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run['default'](function() { property_set.set(cersei, 'lname', 'Stark'); // plot twist! (possibly not canon) }); // The array has become unsorted. If your sort function is sensitive to // properties, they *must* be specified as dependent item property keys or // we'll be doing binary searches on unsorted arrays. deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "updating an unspecified property on an item does not resort it"); }); QUnit.module('computedSort - stability', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A(Ember['default'].A([{ name: "A", count: 1 }, { name: "B", count: 1 }, { name: "C", count: 1 }, { name: "D", count: 1 }]).map(function(elt) { return EmberObject['default'].create(elt); })), sortProps: Ember['default'].A(['count', 'name']), sortedItems: reduce_computed_macros.sort('items', 'sortProps') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("sorts correctly as only one property changes", function() { var sorted; run['default'](function() { sorted = obj.get('sortedItems'); }); deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], "initial"); obj.get('items').objectAt(3).set('count', 2); run['default'](function() { sorted = obj.get('sortedItems'); }); deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], "final"); }); QUnit.module('computedSort - concurrency', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A(Ember['default'].A([{ name: "A", count: 1 }, { name: "B", count: 2 }, { name: "C", count: 3 }, { name: "D", count: 4 }]).map(function(elt) { return EmberObject['default'].create(elt); })), sortProps: Ember['default'].A(['count']), sortedItems: reduce_computed_macros.sort('items', 'sortProps'), customSortedItems: reduce_computed_macros.sort('items.@each.count', function(a, b) { return property_get.get(a, 'count') - property_get.get(b, 'count'); }) }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("sorts correctly when there are concurrent changes", function() { var sorted; run['default'](function() { sorted = obj.get('sortedItems'); }); deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], "initial"); Ember['default'].changeProperties(function() { obj.get('items').objectAt(1).set('count', 5); obj.get('items').objectAt(2).set('count', 6); }); run['default'](function() { sorted = obj.get('sortedItems'); }); deepEqual(sorted.mapBy('name'), ['A', 'D', 'B', 'C'], "final"); }); QUnit.test("sorts correctly with a user-provided comparator when there are concurrent changes", function() { var sorted; run['default'](function() { sorted = obj.get('customSortedItems'); deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], "initial"); }); run['default'](function() { Ember['default'].changeProperties(function() { obj.get('items').objectAt(1).set('count', 5); obj.get('items').objectAt(2).set('count', 6); }); sorted = obj.get('customSortedItems'); deepEqual(sorted.mapBy('name'), ['A', 'D', 'B', 'C'], "final"); }); }); QUnit.module('computedMax', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A([1,2,3]), max: reduce_computed_macros.max('items') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("max tracks the max number as objects are added", function() { equal(property_get.get(obj, 'max'), 3, "precond - max is initially correct"); run['default'](function() { items = property_get.get(obj, 'items'); }); run['default'](function() { items.pushObject(5); }); equal(property_get.get(obj, 'max'), 5, "max updates when a larger number is added"); run['default'](function() { items.pushObject(2); }); equal(property_get.get(obj, 'max'), 5, "max does not update when a smaller number is added"); }); QUnit.test("max recomputes when the current max is removed", function() { equal(property_get.get(obj, 'max'), 3, "precond - max is initially correct"); run['default'](function() { items = property_get.get(obj, 'items'); items.removeObject(2); }); equal(property_get.get(obj, 'max'), 3, "max is unchanged when a non-max item is removed"); run['default'](function() { items.removeObject(3); }); equal(property_get.get(obj, 'max'), 1, "max is recomputed when the current max is removed"); }); QUnit.module('computedMin', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A([1,2,3]), min: reduce_computed_macros.min('items') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("min tracks the min number as objects are added", function() { equal(property_get.get(obj, 'min'), 1, "precond - min is initially correct"); run['default'](function() { items = property_get.get(obj, 'items'); }); run['default'](function() { items.pushObject(-2); }); equal(property_get.get(obj, 'min'), -2, "min updates when a smaller number is added"); run['default'](function() { items.pushObject(2); }); equal(property_get.get(obj, 'min'), -2, "min does not update when a larger number is added"); }); QUnit.test("min recomputes when the current min is removed", function() { equal(property_get.get(obj, 'min'), 1, "precond - min is initially correct"); run['default'](function() { items = property_get.get(obj, 'items'); items.removeObject(2); }); equal(property_get.get(obj, 'min'), 1, "min is unchanged when a non-min item is removed"); run['default'](function() { items.removeObject(1); }); equal(property_get.get(obj, 'min'), 3, "min is recomputed when the current min is removed"); }); QUnit.module('Ember.arrayComputed - mixed sugar', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A([{ fname: "Jaime", lname: "Lannister", age: 34 }, { fname: "Cersei", lname: "Lannister", age: 34 }, { fname: "Robb", lname: "Stark", age: 16 }, { fname: "Bran", lname: "Stark", age: 8 }]), lannisters: reduce_computed_macros.filterBy('items', 'lname', 'Lannister'), lannisterSorting: Ember['default'].A(['fname']), sortedLannisters: reduce_computed_macros.sort('lannisters', 'lannisterSorting'), starks: reduce_computed_macros.filterBy('items', 'lname', 'Stark'), starkAges: reduce_computed_macros.mapBy('starks', 'age'), oldestStarkAge: reduce_computed_macros.max('starkAges') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("filtering and sorting can be combined", function() { run['default'](function() { items = property_get.get(obj, 'items'); sorted = property_get.get(obj, 'sortedLannisters'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime'], "precond - array is initially filtered and sorted"); run['default'](function() { items.pushObject({ fname: 'Tywin', lname: 'Lannister' }); items.pushObject({ fname: 'Lyanna', lname: 'Stark' }); items.pushObject({ fname: 'Gerion', lname: 'Lannister' }); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Gerion', 'Jaime', 'Tywin'], "updates propagate to array"); }); QUnit.test("filtering, sorting and reduce (max) can be combined", function() { run['default'](function() { items = property_get.get(obj, 'items'); }); equal(16, property_get.get(obj, 'oldestStarkAge'), "precond - end of chain is initially correct"); run['default'](function() { items.pushObject({ fname: 'Rickon', lname: 'Stark', age: 5 }); }); equal(16, property_get.get(obj, 'oldestStarkAge'), "chain is updated correctly"); run['default'](function() { items.pushObject({ fname: 'Eddard', lname: 'Stark', age: 35 }); }); equal(35, property_get.get(obj, 'oldestStarkAge'), "chain is updated correctly"); }); function todo(name, priority) { return EmberObject['default'].create({ name: name, priority: priority }); } function priorityComparator(todoA, todoB) { var pa = parseInt(property_get.get(todoA, 'priority'), 10); var pb = parseInt(property_get.get(todoB, 'priority'), 10); return pa - pb; } function evenPriorities(todo) { var p = parseInt(property_get.get(todo, 'priority'), 10); return p % 2 === 0; } QUnit.module('Ember.arrayComputed - chains', { setup: function() { obj = EmberObject['default'].createWithMixins({ todos: Ember['default'].A([todo('E', 4), todo('D', 3), todo('C', 2), todo('B', 1), todo('A', 0)]), sorted: reduce_computed_macros.sort('todos.@each.priority', priorityComparator), filtered: reduce_computed_macros.filter('sorted.@each.priority', evenPriorities) }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it can filter and sort when both depend on the same item property", function() { run['default'](function() { filtered = property_get.get(obj, 'filtered'); sorted = property_get.get(obj, 'sorted'); todos = property_get.get(obj, 'todos'); }); deepEqual(todos.mapProperty('name'), ['E', 'D', 'C', 'B', 'A'], "precond - todos initially correct"); deepEqual(sorted.mapProperty('name'), ['A', 'B', 'C', 'D', 'E'], "precond - sorted initially correct"); deepEqual(filtered.mapProperty('name'), ['A', 'C', 'E'], "precond - filtered initially correct"); run['default'](function() { property_events.beginPropertyChanges(); // here we trigger several changes // A. D.priority 3 -> 6 // 1. updated sorted from item property change // a. remove D; reinsert D // b. update filtered from sorted change // 2. update filtered from item property change // // If 1.b happens before 2 it should invalidate 2 todos.objectAt(1).set('priority', 6); property_events.endPropertyChanges(); }); deepEqual(todos.mapProperty('name'), ['E', 'D', 'C', 'B', 'A'], "precond - todos remain correct"); deepEqual(sorted.mapProperty('name'), ['A', 'B', 'C', 'E', 'D'], "precond - sorted updated correctly"); deepEqual(filtered.mapProperty('name'), ['A', 'C', 'E', 'D'], "filtered updated correctly"); }); QUnit.module('Chaining array and reduced CPs', { setup: function() { run['default'](function() { userFnCalls = 0; obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([{ v: 1 }, { v: 3 }, { v: 2 }, { v: 1 }]), mapped: reduce_computed_macros.mapBy('array', 'v'), max: reduce_computed_macros.max('mapped'), maxDidChange: mixin.observer('max', function() { userFnCalls++; }) }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("it computes interdependent array computed properties", function() { property_get.get(obj, 'mapped'); equal(property_get.get(obj, 'max'), 3, 'sanity - it properly computes the maximum value'); equal(userFnCalls, 0, 'observer is not called on initialisation'); var calls = 0; observer.addObserver(obj, 'max', function() { calls++; }); run['default'](function() { obj.get('array').pushObject({ v: 5 }); }); equal(property_get.get(obj, 'max'), 5, 'maximum value is updated correctly'); equal(userFnCalls, 1, 'object defined observers fire'); equal(calls, 1, 'runtime created observers fire'); }); QUnit.module('computedSum', { setup: function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ array: Ember['default'].A([1, 2, 3]), total: reduce_computed_macros.sum('array') }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test('sums the values in the dependentKey', function() { var sum = property_get.get(obj, 'total'); equal(sum, 6, 'sums the values'); }); QUnit.test('updates when array is modified', function() { var sum = function() { return property_get.get(obj, 'total'); }; run['default'](function() { property_get.get(obj, 'array').pushObject(1); }); equal(sum(), 7, 'recomputed when elements are added'); run['default'](function() { property_get.get(obj, 'array').popObject(); }); equal(sum(), 6, 'recomputes when elements are removed'); }); }); enifed('ember-runtime/tests/computed/reduce_computed_macros_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/reduce_computed_macros_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/computed/reduce_computed_macros_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/computed/reduce_computed_macros_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/reduce_computed_macros_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/computed/reduce_computed_macros_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/computed/reduce_computed_test', ['ember-metal/core', 'ember-metal/enumerable_utils', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/run_loop', 'ember-metal/mixin', 'ember-metal/keys', 'ember-runtime/system/object', 'ember-metal/computed', 'ember-runtime/computed/array_computed', 'ember-runtime/computed/reduce_computed', 'ember-runtime/system/array_proxy', 'ember-runtime/system/subarray'], function (Ember, enumerable_utils, property_get, property_set, utils, run, mixin, keys, EmberObject, computed, array_computed, reduce_computed, ArrayProxy, SubArray) { 'use strict'; var obj, addCalls, removeCalls, callbackItems, shared; QUnit.module('arrayComputed', { setup: function () { addCalls = removeCalls = 0; obj = EmberObject['default'].createWithMixins({ numbers: Ember['default'].A([1, 2, 3, 4, 5, 6]), otherNumbers: Ember['default'].A([7, 8, 9]), // Users would obviously just use `Ember.computed.map` // This implementation is fine for these tests, but doesn't properly work as // it's not index based. evenNumbers: array_computed.arrayComputed('numbers', { addedItem: function (array, item) { addCalls++; if (item % 2 === 0) { array.pushObject(item); } return array; }, removedItem: function (array, item) { removeCalls++; array.removeObject(item); return array; } }), evenNumbersMultiDep: array_computed.arrayComputed('numbers', 'otherNumbers', { addedItem: function (array, item) { if (item % 2 === 0) { array.pushObject(item); } return array; } }), nestedNumbers: Ember['default'].A(enumerable_utils.map([1,2,3,4,5,6], function (n) { return EmberObject['default'].create({ p: 'otherProperty', v: n }); })), evenNestedNumbers: array_computed.arrayComputed({ addedItem: function (array, item, keyName) { var value = item.get('v'); if (value % 2 === 0) { array.pushObject(value); } return array; }, removedItem: function (array, item, keyName) { array.removeObject(item.get('v')); return array; } }).property('nestedNumbers.@each.v') }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("array computed properties are instances of ComputedProperty", function() { ok(array_computed.arrayComputed({}) instanceof computed.ComputedProperty); }); QUnit.test("when the dependent array is null or undefined, `addedItem` is not called and only the initial value is returned", function() { obj = EmberObject['default'].createWithMixins({ numbers: null, doubledNumbers: array_computed.arrayComputed('numbers', { addedItem: function (array, n) { addCalls++; array.pushObject(n * 2); return array; } }) }); deepEqual(property_get.get(obj, 'doubledNumbers'), [], "When the dependent array is null, the initial value is returned"); equal(addCalls, 0, "`addedItem` is not called when the dependent array is null"); run['default'](function() { property_set.set(obj, 'numbers', Ember['default'].A([1,2])); }); deepEqual(property_get.get(obj, 'doubledNumbers'), [2,4], "An initially null dependent array can still be set later"); equal(addCalls, 2, "`addedItem` is called when the dependent array is initially set"); }); QUnit.test("on first retrieval, array computed properties are computed", function() { deepEqual(property_get.get(obj, 'evenNumbers'), [2,4,6], "array computed properties are correct on first invocation"); }); QUnit.test("on first retrieval, array computed properties with multiple dependent keys are computed", function() { deepEqual(property_get.get(obj, 'evenNumbersMultiDep'), [2, 4, 6, 8], "array computed properties are correct on first invocation"); }); QUnit.test("on first retrieval, array computed properties dependent on nested objects are computed", function() { deepEqual(property_get.get(obj, 'evenNestedNumbers'), [2,4,6], "array computed properties are correct on first invocation"); }); QUnit.test("after the first retrieval, array computed properties observe additions to dependent arrays", function() { var numbers = property_get.get(obj, 'numbers'); // set up observers var evenNumbers = property_get.get(obj, 'evenNumbers'); run['default'](function() { numbers.pushObjects([7, 8]); }); deepEqual(evenNumbers, [2, 4, 6, 8], "array computed properties watch dependent arrays"); }); QUnit.test("after the first retrieval, array computed properties observe removals from dependent arrays", function() { var numbers = property_get.get(obj, 'numbers'); // set up observers var evenNumbers = property_get.get(obj, 'evenNumbers'); run['default'](function() { numbers.removeObjects([3, 4]); }); deepEqual(evenNumbers, [2, 6], "array computed properties watch dependent arrays"); }); QUnit.test("after first retrieval, array computed properties can observe properties on array items", function() { var nestedNumbers = property_get.get(obj, 'nestedNumbers'); var evenNestedNumbers = property_get.get(obj, 'evenNestedNumbers'); deepEqual(evenNestedNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run['default'](function() { nestedNumbers.objectAt(0).set('v', 22); }); deepEqual(nestedNumbers.mapBy('v'), [22, 2, 3, 4, 5, 6], 'nested numbers is updated'); deepEqual(evenNestedNumbers, [2, 4, 6, 22], 'adds new number'); }); QUnit.test("changes to array computed properties happen synchronously", function() { var nestedNumbers = property_get.get(obj, 'nestedNumbers'); var evenNestedNumbers = property_get.get(obj, 'evenNestedNumbers'); deepEqual(evenNestedNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run['default'](function() { nestedNumbers.objectAt(0).set('v', 22); deepEqual(nestedNumbers.mapBy('v'), [22, 2, 3, 4, 5, 6], 'nested numbers is updated'); deepEqual(evenNestedNumbers, [2, 4, 6, 22], 'adds new number'); }); }); QUnit.test("multiple dependent keys can be specified via brace expansion", function() { var obj = EmberObject['default'].createWithMixins({ bar: Ember['default'].A(), baz: Ember['default'].A(), foo: reduce_computed.reduceComputed({ initialValue: Ember['default'].A(), addedItem: function(array, item) { array.pushObject('a:' + item); return array; }, removedItem: function(array, item) { array.pushObject('r:' + item); return array; } }).property('{bar,baz}') }); deepEqual(property_get.get(obj, 'foo'), [], "initially empty"); property_get.get(obj, 'bar').pushObject(1); deepEqual(property_get.get(obj, 'foo'), ['a:1'], "added item from brace-expanded dependency"); property_get.get(obj, 'baz').pushObject(2); deepEqual(property_get.get(obj, 'foo'), ['a:1', 'a:2'], "added item from brace-expanded dependency"); property_get.get(obj, 'bar').popObject(); deepEqual(property_get.get(obj, 'foo'), ['a:1', 'a:2', 'r:1'], "removed item from brace-expanded dependency"); property_get.get(obj, 'baz').popObject(); deepEqual(property_get.get(obj, 'foo'), ['a:1', 'a:2', 'r:1', 'r:2'], "removed item from brace-expanded dependency"); }); QUnit.test("multiple item property keys can be specified via brace expansion", function() { var expected = Ember['default'].A(); var item = { propA: 'A', propB: 'B', propC: 'C' }; var obj = EmberObject['default'].createWithMixins({ bar: Ember['default'].A([item]), foo: reduce_computed.reduceComputed({ initialValue: Ember['default'].A(), addedItem: function(array, item, changeMeta) { array.pushObject('a:' + property_get.get(item, 'propA') + ':' + property_get.get(item, 'propB') + ':' + property_get.get(item, 'propC')); return array; }, removedItem: function(array, item, changeMeta) { array.pushObject('r:' + property_get.get(item, 'propA') + ':' + property_get.get(item, 'propB') + ':' + property_get.get(item, 'propC')); return array; } }).property('bar.@each.{propA,propB}') }); expected.pushObjects(['a:A:B:C']); deepEqual(property_get.get(obj, 'foo'), expected, "initially added dependent item"); property_set.set(item, 'propA', 'AA'); expected.pushObjects(['r:AA:B:C', 'a:AA:B:C']); deepEqual(property_get.get(obj, 'foo'), expected, "observing item property key specified via brace expansion"); property_set.set(item, 'propB', 'BB'); expected.pushObjects(['r:AA:BB:C', 'a:AA:BB:C']); deepEqual(property_get.get(obj, 'foo'), expected, "observing item property key specified via brace expansion"); property_set.set(item, 'propC', 'CC'); deepEqual(property_get.get(obj, 'foo'), expected, "not observing unspecified item properties"); }); QUnit.test("doubly nested item property keys (@each.foo.@each) are not supported", function() { run['default'](function() { obj = EmberObject['default'].createWithMixins({ peopleByOrdinalPosition: Ember['default'].A([{ first: Ember['default'].A([EmberObject['default'].create({ name: "Jaime Lannister" })]) }]), people: array_computed.arrayComputed({ addedItem: function (array, item) { array.pushObject(property_get.get(item, 'first.firstObject')); return array; } }).property('peopleByOrdinalPosition.@each.first'), names: array_computed.arrayComputed({ addedItem: function (array, item) { equal(property_get.get(item, 'name'), 'Jaime Lannister'); array.pushObject(item.get('name')); return array; } }).property('people.@each.name') }); }); equal(obj.get('names.firstObject'), 'Jaime Lannister', "Doubly nested item properties can be retrieved manually"); throws(function() { obj = EmberObject['default'].createWithMixins({ people: [{ first: Ember['default'].A([EmberObject['default'].create({ name: "Jaime Lannister" })]) }], names: array_computed.arrayComputed({ addedItem: function (array, item) { array.pushObject(item); return array; } }).property('people.@each.first.@each.name') }); }, /Nested @each/, "doubly nested item property keys are not supported"); }); QUnit.test("after the first retrieval, array computed properties observe dependent arrays", function() { property_get.get(obj, 'numbers'); var evenNumbers = property_get.get(obj, 'evenNumbers'); deepEqual(evenNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run['default'](function() { property_set.set(obj, 'numbers', Ember['default'].A([20, 23, 28])); }); deepEqual(evenNumbers, [20, 28], "array computed properties watch dependent arrays"); }); QUnit.test("array observers are torn down when dependent arrays change", function() { var numbers = property_get.get(obj, 'numbers'); property_get.get(obj, 'evenNumbers'); equal(addCalls, 6, 'precond - add has been called for each item in the array'); equal(removeCalls, 0, 'precond - removed has not been called'); run['default'](function() { property_set.set(obj, 'numbers', Ember['default'].A([20, 23, 28])); }); equal(addCalls, 9, 'add is called for each item in the new array'); equal(removeCalls, 0, 'remove is not called when the array is reset'); numbers.replace(0, numbers.get('length'), Ember['default'].A([7,8,9,10])); equal(addCalls, 9, 'add is not called'); equal(removeCalls, 0, 'remove is not called'); }); QUnit.test("modifying properties on dependent array items triggers observers exactly once", function() { var numbers = property_get.get(obj, 'numbers'); var evenNumbers = property_get.get(obj, 'evenNumbers'); equal(addCalls, 6, 'precond - add has not been called for each item in the array'); equal(removeCalls, 0, 'precond - removed has not been called'); run['default'](function() { numbers.replace(0, 2, [7,8,9,10]); }); equal(addCalls, 10, 'add is called for each item added'); equal(removeCalls, 2, 'removed is called for each item removed'); deepEqual(evenNumbers, [4,6,8,10], 'sanity check - dependent arrays are updated'); }); QUnit.test("multiple array computed properties on the same object can observe dependent arrays", function() { var numbers = property_get.get(obj, 'numbers'); var otherNumbers = property_get.get(obj, 'otherNumbers'); deepEqual(property_get.get(obj, 'evenNumbers'), [2,4,6], "precond - evenNumbers is initially correct"); deepEqual(property_get.get(obj, 'evenNumbersMultiDep'), [2, 4, 6, 8], "precond - evenNumbersMultiDep is initially correct"); run['default'](function() { numbers.pushObject(12); otherNumbers.pushObject(14); }); deepEqual(property_get.get(obj, 'evenNumbers'), [2,4,6,12], "evenNumbers is updated"); deepEqual(property_get.get(obj, 'evenNumbersMultiDep'), [2, 4, 6, 8, 12, 14], "evenNumbersMultiDep is updated"); }); QUnit.test("an error is thrown when a reduceComputed is defined without an initialValue property", function() { var defineExploder = function() { EmberObject['default'].createWithMixins({ collection: Ember['default'].A(), exploder: reduce_computed.reduceComputed('collection', { initialize: function(initialValue, changeMeta, instanceMeta) {}, addedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { return item; }, removedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { return item; } }) }); }; throws(defineExploder, /declared\ without\ an\ initial\ value/, "an error is thrown when the reduceComputed is defined without an initialValue"); }); QUnit.test("dependent arrays with multiple item properties are not double-counted", function() { var obj = EmberObject['default'].extend({ items: Ember['default'].A([{ foo: true }, { bar: false }, { bar: true }]), countFooOrBar: reduce_computed.reduceComputed({ initialValue: 0, addedItem: function (acc) { ++addCalls; return acc; }, removedItem: function (acc) { ++removeCalls; return acc; } }).property('items.@each.foo', 'items.@each.bar', 'items') }).create(); equal(0, addCalls, "precond - no adds yet"); equal(0, removeCalls, "precond - no removes yet"); property_get.get(obj, 'countFooOrBar'); equal(3, addCalls, "all items added once"); equal(0, removeCalls, "no removes yet"); }); QUnit.test("dependent arrays can use `replace` with an out of bounds index to add items", function() { var dependentArray = Ember['default'].A(); var array; obj = EmberObject['default'].extend({ dependentArray: dependentArray, computed: array_computed.arrayComputed('dependentArray', { addedItem: function (acc, item, changeMeta) { acc.insertAt(changeMeta.index, item); return acc; }, removedItem: function (acc) { return acc; } }) }).create(); array = property_get.get(obj, 'computed'); deepEqual(array, [], "precond - computed array is initially empty"); dependentArray.replace(100, 0, [1, 2]); deepEqual(array, [1, 2], "index >= length treated as a push"); dependentArray.replace(-100, 0, [3, 4]); deepEqual(array, [3, 4, 1, 2], "index < 0 treated as an unshift"); }); QUnit.test("dependent arrays can use `replace` with a negative index to remove items indexed from the right", function() { var dependentArray = Ember['default'].A([1,2,3,4,5]); var array; obj = EmberObject['default'].extend({ dependentArray: dependentArray, computed: array_computed.arrayComputed('dependentArray', { addedItem: function (acc, item) { return acc; }, removedItem: function (acc, item) { acc.pushObject(item); return acc; } }) }).create(); array = property_get.get(obj, 'computed'); deepEqual(array, [], "precond - no items have been removed initially"); dependentArray.replace(-3, 2); deepEqual(array, [4,3], "index < 0 used as a right index for removal"); }); QUnit.test("dependent arrays that call `replace` with an out of bounds index to remove items is a no-op", function() { var dependentArray = Ember['default'].A([1, 2]); var array; obj = EmberObject['default'].extend({ dependentArray: dependentArray, computed: array_computed.arrayComputed('dependentArray', { addedItem: function (acc, item, changeMeta) { return acc; }, removedItem: function (acc) { ok(false, "no items have been removed"); } }) }).create(); array = property_get.get(obj, 'computed'); deepEqual(array, [], "precond - computed array is initially empty"); dependentArray.replace(100, 2); }); QUnit.test("dependent arrays that call `replace` with a too-large removedCount a) works and b) still right-truncates", function() { var dependentArray = Ember['default'].A([1, 2]); var array; obj = EmberObject['default'].extend({ dependentArray: dependentArray, computed: array_computed.arrayComputed('dependentArray', { addedItem: function (acc, item) { return acc; }, removedItem: function (acc, item) { acc.pushObject(item); return acc; } }) }).create(); array = property_get.get(obj, 'computed'); deepEqual(array, [], "precond - computed array is initially empty"); dependentArray.replace(1, 200); deepEqual(array, [2], "array was correctly right-truncated"); }); QUnit.test("removedItem is not erroneously called for dependent arrays during a recomputation", function() { function addedItem(array, item, changeMeta) { array.insertAt(changeMeta.index, item); return array; } function removedItem(array, item, changeMeta) { ok(property_get.get(array, 'length') > changeMeta.index, "removedItem not called with invalid index"); array.removeAt(changeMeta.index, 1); return array; } var options = { addedItem: addedItem, removedItem: removedItem }; obj = EmberObject['default'].extend({ dependentArray: Ember['default'].A([1, 2]), identity0: array_computed.arrayComputed('dependentArray', options), identity1: array_computed.arrayComputed('identity0', options) }).create(); property_get.get(obj, 'identity1'); run['default'](function() { obj.notifyPropertyChange('dependentArray'); }); ok(true, "removedItem not invoked with invalid index"); }); QUnit.module('arrayComputed - recomputation DKs', { setup: function() { obj = EmberObject['default'].extend({ people: Ember['default'].A([{ name: 'Jaime Lannister', title: 'Kingsguard' }, { name: 'Cersei Lannister', title: 'Queen' }]), titles: array_computed.arrayComputed('people', { addedItem: function (acc, person) { acc.pushObject(property_get.get(person, 'title')); return acc; } }) }).create(); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("recomputations from `arrayComputed` observers add back dependent keys", function() { var meta = utils.meta(obj); property_get.get(obj, 'people'); var titles; equal(meta.deps, undefined, "precond - nobody depends on people'"); equal(meta.watching.people, undefined, "precond - nobody is watching people"); titles = property_get.get(obj, 'titles'); deepEqual(titles, ["Kingsguard", "Queen"], "precond - value is correct"); ok(meta.deps.people !== undefined, "people has dependencies"); deepEqual(keys['default'](meta.deps.people), ["titles"], "only titles depends on people"); equal(meta.deps.people.titles, 1, "titles depends on people exactly once"); equal(meta.watching.people, 2, "people has two watchers: the array listener and titles"); run['default'](function() { property_set.set(obj, 'people', Ember['default'].A()); }); // Regular CPs are invalidated when their dependent keys change, but array // computeds keep refs up to date deepEqual(titles, [], "value is correct"); equal(meta.cache.titles, titles, "value remains cached"); ok(meta.deps.people !== undefined, "people has dependencies"); deepEqual(keys['default'](meta.deps.people), ["titles"], "meta.deps.people is unchanged"); equal(meta.deps.people.titles, 1, "deps.people.titles is unchanged"); equal(meta.watching.people, 2, "watching.people is unchanged"); }); QUnit.module('Ember.arryComputed - self chains', { setup: function() { var a = EmberObject['default'].create({ name: 'a' }); var b = EmberObject['default'].create({ name: 'b' }); obj = ArrayProxy['default'].createWithMixins({ content: Ember['default'].A([a, b]), names: array_computed.arrayComputed('@this.@each.name', { addedItem: function (array, item, changeMeta, instanceMeta) { var mapped = property_get.get(item, 'name'); array.insertAt(changeMeta.index, mapped); return array; }, removedItem: function(array, item, changeMeta, instanceMeta) { array.removeAt(changeMeta.index, 1); return array; } }) }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("@this can be used to treat the object as the array itself", function() { var names = property_get.get(obj, 'names'); deepEqual(names, ['a', 'b'], "precond - names is initially correct"); run['default'](function() { obj.objectAt(1).set('name', 'c'); }); deepEqual(names, ['a', 'c'], "@this can be used with item property observers"); run['default'](function() { obj.pushObject({ name: 'd' }); }); deepEqual(names, ['a', 'c', 'd'], "@this observes new items"); }); QUnit.module('arrayComputed - changeMeta property observers', { setup: function() { callbackItems = []; run['default'](function() { obj = EmberObject['default'].createWithMixins({ items: Ember['default'].A([EmberObject['default'].create({ n: 'zero' }), EmberObject['default'].create({ n: 'one' })]), itemsN: array_computed.arrayComputed('items.@each.n', { addedItem: function (array, item, changeMeta, instanceMeta) { callbackItems.push('add:' + changeMeta.index + ":" + property_get.get(changeMeta.item, 'n')); }, removedItem: function (array, item, changeMeta, instanceMeta) { callbackItems.push('remove:' + changeMeta.index + ":" + property_get.get(changeMeta.item, 'n')); } }) }); }); }, teardown: function() { run['default'](function() { obj.destroy(); }); } }); QUnit.test("changeMeta includes item and index", function() { var expected, items, item; items = property_get.get(obj, 'items'); // initial computation add0 add1 run['default'](function() { obj.get('itemsN'); }); // add2 run['default'](function() { items.pushObject(EmberObject['default'].create({ n: 'two' })); }); // remove2 run['default'](function() { items.popObject(); }); // remove0 add0 run['default'](function() { property_set.set(property_get.get(items, 'firstObject'), 'n', "zero'"); }); expected = ["add:0:zero", "add:1:one", "add:2:two", "remove:2:two", "remove:0:zero'", "add:0:zero'"]; deepEqual(callbackItems, expected, "changeMeta includes items"); // [zero', one] -> [zero', one, five, six] // add2 add3 run['default'](function() { items.pushObject(EmberObject['default'].create({ n: 'five' })); items.pushObject(EmberObject['default'].create({ n: 'six' })); }); // remove0 add0 run['default'](function() { items.objectAt(0).set('n', "zero''"); }); expected = expected.concat(['add:2:five', 'add:3:six', "remove:0:zero''", "add:0:zero''"]); deepEqual(callbackItems, expected, "changeMeta includes items"); // [zero'', one, five, six] -> [zero'', five, six] // remove1 run['default'](function() { item = items.objectAt(1); items.removeAt(1, 1); }); run['default'](function() { // observer should have been removed from the deleted item item.set('n', 'ten thousand'); }); // [zero'', five, six] -> [zero'', five, seven] // remove2 add2 run['default'](function() { items.objectAt(2).set('n', "seven"); }); // observer should have been added to the new item expected = expected.concat(['remove:1:one', 'remove:2:seven', 'add:2:seven']); deepEqual(callbackItems, expected, "changeMeta includes items"); // reset (does not call remove) run['default'](function() { item = items.objectAt(1); property_set.set(obj, 'items', Ember['default'].A([])); }); run['default'](function() { // observers should have been removed from the items in the old array property_set.set(item, 'n', 'eleven thousand'); }); deepEqual(callbackItems, expected, "items removed from the array had observers removed"); }); QUnit.test("changeMeta includes changedCount and arrayChanged", function() { var obj = EmberObject['default'].createWithMixins({ letters: Ember['default'].A(['a', 'b']), lettersArrayComputed: array_computed.arrayComputed('letters', { addedItem: function (array, item, changeMeta, instanceMeta) { callbackItems.push('add:' + changeMeta.changedCount + ":" + changeMeta.arrayChanged.join('')); }, removedItem: function (array, item, changeMeta, instanceMeta) { callbackItems.push('remove:' + changeMeta.changedCount + ":" + changeMeta.arrayChanged.join('')); } }) }); var letters = property_get.get(obj, 'letters'); obj.get('lettersArrayComputed'); letters.pushObject('c'); letters.popObject(); letters.replace(0, 1, ['d']); letters.removeAt(0, letters.length); var expected = ["add:2:ab", "add:2:ab", "add:1:abc", "remove:1:abc", "remove:1:ab", "add:1:db", "remove:2:db", "remove:2:db"]; deepEqual(callbackItems, expected, "changeMeta has count and changed"); }); QUnit.test("`updateIndexes` is not over-eager about skipping retain:n (#4620)", function() { var tracked = Ember['default'].A(); obj = EmberObject['default'].extend({ content: Ember['default'].A([{ n: "one" }, { n: "two" }]), items: array_computed.arrayComputed('content.@each.n', { addedItem: function (array, item, changeMeta) { tracked.push('+' + property_get.get(item, 'n') + '@' + changeMeta.index); array.insertAt(changeMeta.index, item); return array; }, removedItem: function (array, item, changeMeta) { tracked.push('-' + (changeMeta.previousValues ? changeMeta.previousValues.n : property_get.get(item, 'n')) + '@' + changeMeta.index); array.removeAt(changeMeta.index); return array; } }) }).create(); run['default'](function () { obj.get('items'); }); deepEqual(tracked, ["+one@0", "+two@1"], "precond - array is set up correctly"); run['default'](function () { obj.get('content').shiftObject(); }); deepEqual(tracked, ["+one@0", "+two@1", "-one@0"], "array handles unshift correctly"); run['default'](function () { property_set.set(obj, 'content.lastObject.n', 'three'); }); deepEqual(tracked, ["+one@0", "+two@1", "-one@0", "-two@0", "+three@0"], "array handles a change when operations are delete:m retain:n-m"); }); QUnit.test("when initialValue is undefined, everything works as advertised", function() { var chars = EmberObject['default'].createWithMixins({ letters: Ember['default'].A(), firstUpper: reduce_computed.reduceComputed('letters', { initialValue: undefined, initialize: function(initialValue, changeMeta, instanceMeta) { instanceMeta.matchingItems = Ember['default'].A(); instanceMeta.subArray = new SubArray['default'](); instanceMeta.firstMatch = function() { return property_get.getWithDefault(instanceMeta.matchingItems, 'firstObject', initialValue); }; }, addedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { var filterIndex; filterIndex = instanceMeta.subArray.addItem(changeMeta.index, item.toUpperCase() === item); if (filterIndex > -1) { instanceMeta.matchingItems.insertAt(filterIndex, item); } return instanceMeta.firstMatch(); }, removedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { var filterIndex = instanceMeta.subArray.removeItem(changeMeta.index); if (filterIndex > -1) { instanceMeta.matchingItems.removeAt(filterIndex); } return instanceMeta.firstMatch(); } }) }); equal(property_get.get(chars, 'firstUpper'), undefined, "initialValue is undefined"); property_get.get(chars, 'letters').pushObjects(['a', 'b', 'c']); equal(property_get.get(chars, 'firstUpper'), undefined, "result is undefined when no matches are present"); property_get.get(chars, 'letters').pushObjects(['A', 'B', 'C']); equal(property_get.get(chars, 'firstUpper'), 'A', "result is the first match when matching objects are present"); property_get.get(chars, 'letters').removeAt(3); equal(property_get.get(chars, 'firstUpper'), 'B', "result is the next match when the first matching object is removed"); }); QUnit.module('arrayComputed - completely invalidating dependencies', { setup: function () { addCalls = removeCalls = 0; } }); QUnit.test("non-array dependencies completely invalidate a reduceComputed CP", function() { var dependentArray = Ember['default'].A(); obj = EmberObject['default'].extend({ nonArray: 'v0', dependentArray: dependentArray, computed: array_computed.arrayComputed('dependentArray', 'nonArray', { addedItem: function (array) { ++addCalls; return array; }, removedItem: function (array) { --removeCalls; return array; } }) }).create(); property_get.get(obj, 'computed'); equal(addCalls, 0, "precond - add has not initially been called"); equal(removeCalls, 0, "precond - remove has not initially been called"); dependentArray.pushObjects([1, 2]); equal(addCalls, 2, "add called one-at-a-time for dependent array changes"); equal(removeCalls, 0, "remove not called"); run['default'](function() { property_set.set(obj, 'nonArray', 'v1'); }); equal(addCalls, 4, "array completely recomputed when non-array dependency changed"); equal(removeCalls, 0, "remove not called"); }); QUnit.test("array dependencies specified with `.[]` completely invalidate a reduceComputed CP", function() { var dependentArray = Ember['default'].A(); var totallyInvalidatingDependentArray = Ember['default'].A(); obj = EmberObject['default'].extend({ totallyInvalidatingDependentArray: totallyInvalidatingDependentArray, dependentArray: dependentArray, computed: array_computed.arrayComputed('dependentArray', 'totallyInvalidatingDependentArray.[]', { addedItem: function (array, item) { ok(item !== 3, "totally invalidating items are never passed to the one-at-a-time callbacks"); ++addCalls; return array; }, removedItem: function (array, item) { ok(item !== 3, "totally invalidating items are never passed to the one-at-a-time callbacks"); --removeCalls; return array; } }) }).create(); property_get.get(obj, 'computed'); equal(addCalls, 0, "precond - add has not initially been called"); equal(removeCalls, 0, "precond - remove has not initially been called"); dependentArray.pushObjects([1, 2]); equal(addCalls, 2, "add called one-at-a-time for dependent array changes"); equal(removeCalls, 0, "remove not called"); run['default'](function() { totallyInvalidatingDependentArray.pushObject(3); }); equal(addCalls, 4, "array completely recomputed when totally invalidating dependent array modified"); equal(removeCalls, 0, "remove not called"); }); QUnit.test("returning undefined in addedItem/removedItem completely invalidates a reduceComputed CP", function() { var dependentArray = Ember['default'].A([3,2,1]); var counter = 0; obj = EmberObject['default'].extend({ dependentArray: dependentArray, computed: reduce_computed.reduceComputed('dependentArray', { initialValue: Infinity, addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { return Math.min(accumulatedValue, item); }, removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { if (item > accumulatedValue) { return accumulatedValue; } } }), computedDidChange: mixin.observer('computed', function() { counter++; }) }).create(); property_get.get(obj, 'computed'); equal(property_get.get(obj, 'computed'), 1); equal(counter, 0); dependentArray.pushObject(10); equal(property_get.get(obj, 'computed'), 1); equal(counter, 0); dependentArray.removeObject(10); equal(property_get.get(obj, 'computed'), 1); equal(counter, 0); dependentArray.removeObject(1); equal(property_get.get(obj, 'computed'), 2); equal(counter, 1); }); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.Array) { QUnit.test("reduceComputed complains about array dependencies that are not `Ember.Array`s", function() { var Type = EmberObject['default'].extend({ rc: reduce_computed.reduceComputed('array', { initialValue: 0, addedItem: function(v) { return v; }, removedItem: function(v) { return v; } }) }); expectAssertion(function() { obj = Type.create({ array: [] }); property_get.get(obj, 'rc'); }, /must be an `Ember.Array`/, "Ember.reduceComputed complains about dependent non-extended native arrays"); }); } QUnit.module('arrayComputed - misc', { setup: function () { callbackItems = []; shared = Ember['default'].Object.create({ flag: false }); var Item = Ember['default'].Object.extend({ shared: shared, flag: computed.computed('shared.flag', function () { return this.get('shared.flag'); }) }); obj = Ember['default'].Object.extend({ upstream: Ember['default'].A([ Item.create(), Item.create() ]), arrayCP: array_computed.arrayComputed('upstream.@each.flag', { addedItem: function (array, item) { callbackItems.push('add:' + item.get('flag')); return array; }, removedItem: function (array, item) { callbackItems.push('remove:' + item.get('flag')); return array; } }) }).create(); }, teardown: function () { run['default'](function () { obj.destroy(); }); } }); QUnit.test("item property change flushes are gated by a semaphore", function() { obj.get('arrayCP'); deepEqual(callbackItems, ['add:false', 'add:false'], "precond - calls are initially correct"); callbackItems.splice(0, 2); shared.set('flag', true); deepEqual(callbackItems, ['remove:true', 'add:true', 'remove:true', 'add:true'], "item property flushes that depend on a shared prop are gated by a semaphore"); }); }); enifed('ember-runtime/tests/computed/reduce_computed_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/reduce_computed_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/computed/reduce_computed_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/computed/reduce_computed_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/reduce_computed_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/computed/reduce_computed_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/controllers/array_controller_test', ['ember-metal/core', 'ember-runtime/tests/suites/mutable_array', 'ember-runtime/controllers/array_controller'], function (Ember, MutableArrayTests, ArrayController) { 'use strict'; QUnit.module("ember-runtime/controllers/array_controller_test"); MutableArrayTests['default'].extend({ name: 'Ember.ArrayController', newObject: function(ary) { var ret = ary ? ary.slice() : this.newFixture(3); return ArrayController['default'].create({ model: Ember['default'].A(ret) }); }, mutate: function(obj) { obj.pushObject(Ember['default'].get(obj, 'length')+1); }, toArray: function(obj) { return obj.toArray ? obj.toArray() : obj.slice(); } }).run(); QUnit.test("defaults its `model` to an empty array", function () { var Controller = ArrayController['default'].extend(); deepEqual(Controller.create().get("model"), [], "`ArrayController` defaults its model to an empty array"); equal(Controller.create().get('firstObject'), undefined, 'can fetch firstObject'); equal(Controller.create().get('lastObject'), undefined, 'can fetch lastObject'); }); QUnit.test("Ember.ArrayController length property works even if model was not set initially", function() { var controller = ArrayController['default'].create(); controller.pushObject('item'); equal(controller.get('length'), 1); }); }); enifed('ember-runtime/tests/controllers/array_controller_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/array_controller_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/controllers/array_controller_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/controllers/array_controller_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/array_controller_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/controllers/array_controller_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/controllers/controller_test', ['ember-runtime/controllers/controller', 'ember-runtime/system/service', 'ember-runtime/controllers/object_controller', 'ember-metal/mixin', 'ember-runtime/system/object', 'ember-runtime/system/container', 'ember-runtime/inject', 'ember-metal/property_get'], function (Controller, Service, object_controller, Mixin, Object, system__container, inject, property_get) { 'use strict'; /* global EmberDev */ QUnit.module('Controller event handling'); QUnit.test("Action can be handled by a function on actions object", function() { expect(1); var TestController = Controller['default'].extend({ actions: { poke: function() { ok(true, 'poked'); } } }); var controller = TestController.create({}); controller.send("poke"); }); // TODO: Can we support this? // QUnit.test("Actions handlers can be configured to use another name", function() { // expect(1); // var TestController = Controller.extend({ // actionsProperty: 'actionHandlers', // actionHandlers: { // poke: function() { // ok(true, 'poked'); // } // } // }); // var controller = TestController.create({}); // controller.send("poke"); // }); QUnit.test("When `_actions` is provided, `actions` is left alone", function() { expect(2); var TestController = Controller['default'].extend({ actions: ['foo', 'bar'], _actions: { poke: function() { ok(true, 'poked'); } } }); var controller = TestController.create({}); controller.send("poke"); equal('foo', controller.get("actions")[0], 'actions property is not untouched'); }); QUnit.test("Actions object doesn't shadow a proxied object's 'actions' property", function() { expectDeprecation(object_controller.objectControllerDeprecation); var TestController = object_controller["default"].extend({ model: { actions: 'foo' }, actions: { poke: function() { console.log('ouch'); } } }); var controller = TestController.create({}); equal(controller.get("actions"), 'foo', "doesn't shadow the content's actions property"); }); QUnit.test("A handled action can be bubbled to the target for continued processing", function() { expect(2); var TestController = Controller['default'].extend({ actions: { poke: function() { ok(true, 'poked 1'); return true; } } }); var controller = TestController.create({ target: Controller['default'].extend({ actions: { poke: function() { ok(true, 'poked 2'); } } }).create() }); controller.send("poke"); }); QUnit.test("Action can be handled by a superclass' actions object", function() { expect(4); var SuperController = Controller['default'].extend({ actions: { foo: function() { ok(true, 'foo'); }, bar: function(msg) { equal(msg, "HELLO"); } } }); var BarControllerMixin = Mixin['default'].create({ actions: { bar: function(msg) { equal(msg, "HELLO"); this._super(msg); } } }); var IndexController = SuperController.extend(BarControllerMixin, { actions: { baz: function() { ok(true, 'baz'); } } }); var controller = IndexController.create({}); controller.send("foo"); controller.send("bar", "HELLO"); controller.send("baz"); }); QUnit.module('Controller deprecations'); QUnit.module('Controller Content -> Model Alias'); QUnit.test("`model` is aliased as `content`", function() { expect(1); var controller = Controller['default'].extend({ model: 'foo-bar' }).create(); equal(controller.get('content'), 'foo-bar', 'content is an alias of model'); }); QUnit.test("`content` is moved to `model` when `model` is unset", function() { expect(2); var controller; ignoreDeprecation(function() { controller = Controller['default'].extend({ content: 'foo-bar' }).create(); }); equal(controller.get('model'), 'foo-bar', 'model is set properly'); equal(controller.get('content'), 'foo-bar', 'content is set properly'); }); QUnit.test("specifying `content` (without `model` specified) results in deprecation", function() { expect(1); var controller; expectDeprecation(function() { controller = Controller['default'].extend({ content: 'foo-bar' }).create(); }, 'Do not specify `content` on a Controller, use `model` instead.'); }); QUnit.test("specifying `content` (with `model` specified) does not result in deprecation", function() { expect(3); expectNoDeprecation(); var controller = Controller['default'].extend({ content: 'foo-bar', model: 'blammo' }).create(); equal(property_get.get(controller, 'content'), 'foo-bar'); equal(property_get.get(controller, 'model'), 'blammo'); }); QUnit.module('Controller injected properties'); if (!EmberDev.runningProdBuild) { QUnit.test("defining a controller on a non-controller should fail assertion", function() { expectAssertion(function() { var registry = new system__container.Registry(); var container = registry.container(); var AnObject = Object['default'].extend({ container: container, foo: inject['default'].controller('bar') }); registry.register('foo:main', AnObject); container.lookupFactory('foo:main'); }, /Defining an injected controller property on a non-controller is not allowed./); }); } QUnit.test("controllers can be injected into controllers", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:post', Controller['default'].extend({ postsController: inject['default'].controller('posts') })); registry.register('controller:posts', Controller['default'].extend()); var postController = container.lookup('controller:post'); var postsController = container.lookup('controller:posts'); equal(postsController, postController.get('postsController'), "controller.posts is injected"); }); QUnit.test("services can be injected into controllers", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('controller:application', Controller['default'].extend({ authService: inject['default'].service('auth') })); registry.register('service:auth', Service['default'].extend()); var appController = container.lookup('controller:application'); var authService = container.lookup('service:auth'); equal(authService, appController.get('authService'), "service.auth is injected"); }); }); enifed('ember-runtime/tests/controllers/controller_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/controller_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/controllers/controller_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/controllers/controller_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/controller_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/controllers/controller_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/controllers/item_controller_class_test', ['ember-metal/core', 'ember-metal/utils', 'ember-metal/run_loop', 'ember-metal/property_get', 'ember-metal/computed', 'ember-runtime/compare', 'ember-runtime/system/object', 'ember-runtime/controllers/array_controller', 'ember-runtime/controllers/controller', 'ember-runtime/computed/reduce_computed_macros', 'container/registry'], function (Ember, utils, run, property_get, computed, compare, EmberObject, ArrayController, Controller, reduce_computed_macros, Registry) { 'use strict'; var lannisters, arrayController, controllerClass, otherControllerClass, registry, container, itemControllerCount, tywin, jaime, cersei, tyrion; QUnit.module("Ember.ArrayController - itemController", { setup: function() { registry = new Registry['default'](); container = registry.container(); tywin = EmberObject['default'].create({ name: 'Tywin' }); jaime = EmberObject['default'].create({ name: 'Jaime' }); cersei = EmberObject['default'].create({ name: 'Cersei' }); tyrion = EmberObject['default'].create({ name: 'Tyrion' }); lannisters = Ember['default'].A([tywin, jaime, cersei]); itemControllerCount = 0; controllerClass = Controller['default'].extend({ init: function() { ++itemControllerCount; this._super.apply(this, arguments); }, toString: function() { return "itemController for " + this.get('name'); } }); otherControllerClass = Controller['default'].extend({ toString: function() { return "otherItemController for " + this.get('name'); } }); registry.register("controller:Item", controllerClass); registry.register("controller:OtherItem", otherControllerClass); }, teardown: function() { run['default'](function() { container.destroy(); }); registry = container = null; } }); function createUnwrappedArrayController() { arrayController = ArrayController['default'].create({ container: container, model: lannisters }); } function createArrayController() { arrayController = ArrayController['default'].create({ container: container, itemController: 'Item', model: lannisters }); } function createDynamicArrayController() { arrayController = ArrayController['default'].create({ container: container, lookupItemController: function(object) { if ("Tywin" === object.get("name")) { return "Item"; } else { return "OtherItem"; } }, model: lannisters }); } QUnit.test("when no `itemController` is set, `objectAtContent` returns objects directly", function() { createUnwrappedArrayController(); strictEqual(arrayController.objectAtContent(1), jaime, "No controller is returned when itemController is not set"); }); QUnit.test("when `itemController` is set, `objectAtContent` returns an instance of the controller", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1); ok(controllerClass.detectInstance(jaimeController), "A controller is returned when itemController is set"); }); QUnit.test("when `idx` is out of range, `objectAtContent` does not create a controller", function() { controllerClass.reopen({ init: function() { ok(false, "Controllers should not be created when `idx` is out of range"); } }); createArrayController(); strictEqual(arrayController.objectAtContent(50), undefined, "no controllers are created for out of range indexes"); }); QUnit.test("when the underlying object is null, a controller is still returned", function() { createArrayController(); arrayController.unshiftObject(null); var firstController = arrayController.objectAtContent(0); ok(controllerClass.detectInstance(firstController), "A controller is still created for null objects"); }); QUnit.test("the target of item controllers is the parent controller", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1); equal(jaimeController.get('target'), arrayController, "Item controllers' targets are their parent controller"); }); QUnit.test("the parentController property of item controllers is set to the parent controller", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1); equal(jaimeController.get('parentController'), arrayController, "Item controllers' targets are their parent controller"); }); QUnit.test("when the underlying object has not changed, `objectAtContent` always returns the same instance", function() { createArrayController(); strictEqual(arrayController.objectAtContent(1), arrayController.objectAtContent(1), "Controller instances are reused"); }); QUnit.test("when the index changes, `objectAtContent` still returns the same instance", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1); arrayController.unshiftObject(tyrion); strictEqual(arrayController.objectAtContent(2), jaimeController, "Controller instances are reused"); }); QUnit.test("when the underlying array changes, old subcontainers are destroyed", function() { createArrayController(); // cause some controllers to be instantiated arrayController.objectAtContent(1); arrayController.objectAtContent(2); // Not a public API; just checking for cleanup var subControllers = property_get.get(arrayController, '_subControllers'); var jaimeController = subControllers[1]; var cerseiController = subControllers[2]; equal(!!jaimeController.isDestroying, false, "precond - nobody is destroyed yet"); equal(!!cerseiController.isDestroying, false, "precond - nobody is destroyed yet"); run['default'](function() { arrayController.set('model', Ember['default'].A()); }); equal(!!jaimeController.isDestroying, true, "old subcontainers are destroyed"); equal(!!cerseiController.isDestroying, true, "old subcontainers are destroyed"); }); QUnit.test("item controllers are created lazily", function() { createArrayController(); equal(itemControllerCount, 0, "precond - no item controllers yet"); arrayController.objectAtContent(1); equal(itemControllerCount, 1, "item controllers are created lazily"); }); QUnit.test("when items are removed from the arrayController, their respective subcontainers are destroyed", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1); var cerseiController = arrayController.objectAtContent(2); property_get.get(arrayController, '_subControllers'); equal(!!jaimeController.isDestroyed, false, "precond - nobody is destroyed yet"); equal(!!cerseiController.isDestroyed, false, "precond - nobody is destroyed yet"); run['default'](function() { arrayController.removeObject(cerseiController); }); equal(!!cerseiController.isDestroying, true, "Removed objects' containers are cleaned up"); equal(!!jaimeController.isDestroying, false, "Retained objects' containers are not cleaned up"); }); QUnit.test("one cannot remove wrapped model directly when specifying `itemController`", function() { createArrayController(); var cerseiController = arrayController.objectAtContent(2); equal(arrayController.get('length'), 3, "precondition - array is in initial state"); arrayController.removeObject(cersei); equal(arrayController.get('length'), 3, "cannot remove wrapped objects directly"); run['default'](function() { arrayController.removeObject(cerseiController); }); equal(arrayController.get('length'), 2, "can remove wrapper objects"); }); QUnit.test("when items are removed from the underlying array, their respective subcontainers are destroyed", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1); var cerseiController = arrayController.objectAtContent(2); property_get.get(arrayController, 'subContainers'); equal(!!jaimeController.isDestroying, false, "precond - nobody is destroyed yet"); equal(!!cerseiController.isDestroying, false, "precond - nobody is destroyed yet"); run['default'](function() { lannisters.removeObject(cersei); // if only it were that easy }); equal(!!jaimeController.isDestroyed, false, "Retained objects' containers are not cleaned up"); equal(!!cerseiController.isDestroyed, true, "Removed objects' containers are cleaned up"); }); QUnit.test("`itemController` can be dynamic by overwriting `lookupItemController`", function() { createDynamicArrayController(); var tywinController = arrayController.objectAtContent(0); var jaimeController = arrayController.objectAtContent(1); ok(controllerClass.detectInstance(tywinController), "lookupItemController can return different classes for different objects"); ok(otherControllerClass.detectInstance(jaimeController), "lookupItemController can return different classes for different objects"); }); QUnit.test("when `idx` is out of range, `lookupItemController` is not called", function() { arrayController = ArrayController['default'].create({ container: container, lookupItemController: function(object) { ok(false, "`lookupItemController` should not be called when `idx` is out of range"); }, model: lannisters }); strictEqual(arrayController.objectAtContent(50), undefined, "no controllers are created for indexes that are superior to the length"); strictEqual(arrayController.objectAtContent(-1), undefined, "no controllers are created for indexes less than zero"); }); QUnit.test("if `lookupItemController` returns a string, it must be resolvable by the container", function() { arrayController = ArrayController['default'].create({ container: container, lookupItemController: function(object) { return "NonExistent"; }, model: lannisters }); throws(function() { arrayController.objectAtContent(1); }, /NonExistent/, "`lookupItemController` must return either null or a valid controller name"); }); QUnit.test("target and parentController are set to the concrete parentController", function() { var parent = ArrayController['default'].create({ }); // typically controller created for {{each itemController="foo"}} var virtual = ArrayController['default'].create({ itemController: 'Item', container: container, target: parent, parentController: parent, _isVirtual: true, model: Ember['default'].A([ { name: 'kris seldenator' } ]) }); var itemController = virtual.objectAtContent(0); equal(itemController.get('parentController'), parent); equal(itemController.get('target'), parent); run['default'](function() { parent.destroy(); virtual.destroy(); }); }); QUnit.test("array observers can invoke `objectAt` without overwriting existing item controllers", function() { createArrayController(); var tywinController = arrayController.objectAtContent(0); var arrayObserverCalled = false; arrayController.reopen({ lannistersWillChange: function() { return this; }, lannistersDidChange: function(_, idx, removedAmt, addedAmt) { arrayObserverCalled = true; equal(this.objectAt(idx).get('model.name'), "Tyrion", "Array observers get the right object via `objectAt`"); } }); arrayController.addArrayObserver(arrayController, { willChange: 'lannistersWillChange', didChange: 'lannistersDidChange' }); run['default'](function() { lannisters.unshiftObject(tyrion); }); equal(arrayObserverCalled, true, "Array observers are called normally"); equal(tywinController.get('model.name'), "Tywin", "Array observers calling `objectAt` does not overwrite existing controllers' model"); }); QUnit.test("`itemController`'s life cycle should be entangled with its parent controller", function() { createDynamicArrayController(); var tywinController = arrayController.objectAtContent(0); var jaimeController = arrayController.objectAtContent(1); run['default'](arrayController, 'destroy'); equal(tywinController.get('isDestroyed'), true); equal(jaimeController.get('isDestroyed'), true); }); QUnit.module('Ember.ArrayController - itemController with arrayComputed', { setup: function() { registry = new Registry['default'](); container = registry.container(); cersei = EmberObject['default'].create({ name: 'Cersei' }); jaime = EmberObject['default'].create({ name: 'Jaime' }); lannisters = Ember['default'].A([jaime, cersei]); controllerClass = Controller['default'].extend({ title: computed.computed(function () { switch (property_get.get(this, 'name')) { case 'Jaime': return 'Kingsguard'; case 'Cersei': return 'Queen'; } }).property('name'), toString: function() { return "itemController for " + this.get('name'); } }); registry.register("controller:Item", controllerClass); }, teardown: function() { run['default'](function() { container.destroy(); }); } }); QUnit.test("item controllers can be used to provide properties for array computed macros", function() { createArrayController(); ok(compare['default'](utils.guidFor(cersei), utils.guidFor(jaime)) < 0, "precond - guid tiebreaker would fail test"); arrayController.reopen({ sortProperties: Ember['default'].A(['title']), sorted: reduce_computed_macros.sort('@this', 'sortProperties') }); deepEqual(arrayController.get('sorted').mapProperty('model.name'), ['Jaime', 'Cersei'], "ArrayController items can be sorted on itemController properties"); }); }); enifed('ember-runtime/tests/controllers/item_controller_class_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/item_controller_class_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/controllers/item_controller_class_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/controllers/item_controller_class_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/item_controller_class_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/controllers/item_controller_class_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/controllers/object_controller_test', ['ember-runtime/controllers/object_controller', 'ember-metal/mixin'], function (object_controller, mixin) { 'use strict'; QUnit.module("Ember.ObjectController"); QUnit.test("should be able to set the target property of an ObjectController", function() { expectDeprecation(object_controller.objectControllerDeprecation); var controller = object_controller["default"].create(); var target = {}; controller.set('target', target); equal(controller.get('target'), target, "able to set the target property"); }); // See https://github.com/emberjs/ember.js/issues/5112 QUnit.test("can observe a path on an ObjectController", function() { expectDeprecation(object_controller.objectControllerDeprecation); var controller = object_controller["default"].extend({ baz: mixin.observer('foo.bar', function() {}) }).create(); controller.set('model', {}); ok(true, "should not fail"); }); QUnit.test('accessing model properties via proxy behavior results in a deprecation [DEPRECATED]', function() { var controller; expectDeprecation(function() { controller = object_controller["default"].extend({ model: { foo: 'bar', baz: 'qux' } }).create(); }, object_controller.objectControllerDeprecation); expectDeprecation(function() { controller.get('bar'); }, /object proxying is deprecated\. Please use `model\.bar` instead\./); }); QUnit.test('setting model properties via proxy behavior results in a deprecation [DEPRECATED]', function() { var controller; expectDeprecation(function() { controller = object_controller["default"].extend({ model: { foo: 'bar', baz: 'qux' } }).create(); }, object_controller.objectControllerDeprecation); expectDeprecation(function() { controller.set('bar', 'derp'); }, /object proxying is deprecated\. Please use `model\.bar` instead\./); }); QUnit.test('auto-generated controllers are not deprecated', function() { expectNoDeprecation(function() { object_controller["default"].extend({ isGenerated: true }).create(); }); }); }); enifed('ember-runtime/tests/controllers/object_controller_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/object_controller_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/controllers/object_controller_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/controllers/object_controller_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/controllers'); test('ember-runtime/tests/controllers/object_controller_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/controllers/object_controller_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/core/compare_test', ['ember-metal/utils', 'ember-runtime/system/object', 'ember-runtime/compare', 'ember-runtime/mixins/comparable'], function (utils, EmberObject, compare, Comparable) { 'use strict'; var data = []; var Comp = EmberObject['default'].extend(Comparable['default']); Comp.reopenClass({ compare: function (obj) { return obj.get('val'); } }); QUnit.module('Ember.compare()', { setup: function() { data[0] = null; data[1] = false; data[2] = true; data[3] = -12; data[4] = 3.5; data[5] = 'a string'; data[6] = 'another string'; data[7] = 'last string'; data[8] = [1, 2]; data[9] = [1, 2, 3]; data[10] = [1, 3]; data[11] = { a: 'hash' }; data[12] = EmberObject['default'].create(); data[13] = function (a) {return a;}; data[14] = new Date('2012/01/01'); data[15] = new Date('2012/06/06'); } }); QUnit.test('ordering should work', function() { var suspect, comparable, failureMessage, suspectIndex, comparableIndex; for (suspectIndex = 0; suspectIndex < data.length; suspectIndex++) { suspect = data[suspectIndex]; equal(compare['default'](suspect, suspect), 0, suspectIndex + ' should equal itself'); for (comparableIndex = suspectIndex + 1; comparableIndex < data.length; comparableIndex++) { comparable = data[comparableIndex]; failureMessage = 'data[' + suspectIndex + '] (' + utils.typeOf(suspect) + ') should be smaller than data[' + comparableIndex + '] (' + utils.typeOf(comparable) + ')'; equal(compare['default'](suspect, comparable), -1, failureMessage); } } }); QUnit.test('comparables should return values in the range of -1, 0, 1', function() { var negOne = Comp.create({ val: -1 }); var zero = Comp.create({ val: 0 }); var one = Comp.create({ val: 1 }); equal(compare['default'](negOne, 'a'), -1, 'First item comparable - returns -1 (not negated)'); equal(compare['default'](zero, 'b'), 0, 'First item comparable - returns 0 (not negated)'); equal(compare['default'](one, 'c'), 1, 'First item comparable - returns 1 (not negated)'); equal(compare['default']('a', negOne), 1, 'Second item comparable - returns -1 (negated)'); equal(compare['default']('b', zero), 0, 'Second item comparable - returns 0 (negated)'); equal(compare['default']('c', one), -1, 'Second item comparable - returns 1 (negated)'); }); }); enifed('ember-runtime/tests/core/compare_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/core'); test('ember-runtime/tests/core/compare_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/core/compare_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/core/compare_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/compare_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/compare_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/core/copy_test', ['ember-metal/platform/create', 'ember-runtime/copy'], function (create, copy) { 'use strict'; QUnit.module("Ember Copy Method"); QUnit.test("Ember.copy null", function() { var obj = { field: null }; equal(copy['default'](obj, true).field, null, "null should still be null"); }); QUnit.test("Ember.copy date", function() { var date = new Date(2014, 7, 22); var dateCopy = copy['default'](date); equal(date.getTime(), dateCopy.getTime(), "dates should be equivalent"); }); QUnit.test("Ember.copy null prototype object", function() { var obj = create['default'](null); obj.foo = 'bar'; equal(copy['default'](obj).foo, 'bar', 'bar should still be bar'); }); }); enifed('ember-runtime/tests/core/copy_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/core'); test('ember-runtime/tests/core/copy_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/core/copy_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/core/copy_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/copy_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/copy_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/core/isEqual_test', ['ember-runtime/core'], function (core) { 'use strict'; QUnit.module("isEqual"); QUnit.test("undefined and null", function() { ok(core.isEqual(undefined, undefined), "undefined is equal to undefined"); ok(!core.isEqual(undefined, null), "undefined is not equal to null"); ok(core.isEqual(null, null), "null is equal to null"); ok(!core.isEqual(null, undefined), "null is not equal to undefined"); }); QUnit.test("strings should be equal", function() { ok(!core.isEqual("Hello", "Hi"), "different Strings are unequal"); ok(core.isEqual("Hello", "Hello"), "same Strings are equal"); }); QUnit.test("numericals should be equal", function() { ok(core.isEqual(24, 24), "same numbers are equal"); ok(!core.isEqual(24, 21), "different numbers are inequal"); }); QUnit.test("dates should be equal", function() { ok(core.isEqual(new Date(1985, 7, 22), new Date(1985, 7, 22)), "same dates are equal"); ok(!core.isEqual(new Date(2014, 7, 22), new Date(1985, 7, 22)), "different dates are not equal"); }); QUnit.test("array should be equal", function() { // NOTE: We don't test for array contents -- that would be too expensive. ok(!core.isEqual([1,2], [1,2]), 'two array instances with the same values should not be equal'); ok(!core.isEqual([1,2], [1]), 'two array instances with different values should not be equal'); }); QUnit.test("first object implements isEqual should use it", function() { ok(core.isEqual({ isEqual: function() { return true; } }, null), 'should return true always'); var obj = { isEqual: function() { return false; } }; equal(core.isEqual(obj, obj), false, 'should return false because isEqual returns false'); }); }); enifed('ember-runtime/tests/core/isEqual_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/core'); test('ember-runtime/tests/core/isEqual_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/core/isEqual_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/core/isEqual_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/isEqual_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/isEqual_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/core/is_array_test', ['ember-metal/core', 'ember-metal/utils', 'ember-runtime/system/array_proxy'], function (Ember, utils, ArrayProxy) { 'use strict'; QUnit.module("Ember Type Checking"); QUnit.test("Ember.isArray", function() { var arrayProxy = ArrayProxy['default'].create({ content: Ember['default'].A() }); equal(utils.isArray(arrayProxy), true, "[]"); }); }); enifed('ember-runtime/tests/core/is_array_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/core'); test('ember-runtime/tests/core/is_array_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/core/is_array_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/core/is_array_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/is_array_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/is_array_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/core/is_empty_test', ['ember-metal/core', 'ember-metal/is_empty', 'ember-runtime/system/array_proxy'], function (Ember, isEmpty, ArrayProxy) { 'use strict'; QUnit.module("Ember.isEmpty"); QUnit.test("Ember.isEmpty", function() { var arrayProxy = ArrayProxy['default'].create({ content: Ember['default'].A() }); equal(true, isEmpty['default'](arrayProxy), "for an ArrayProxy that has empty content"); }); }); enifed('ember-runtime/tests/core/is_empty_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/core'); test('ember-runtime/tests/core/is_empty_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/core/is_empty_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/core/is_empty_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/is_empty_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/is_empty_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/core/type_test', ['ember-metal/utils', 'ember-runtime/system/object'], function (utils, EmberObject) { 'use strict'; QUnit.module("Ember Type Checking"); QUnit.test("Ember.typeOf", function() { var a = null; var arr = [1,2,3]; var obj = {}; var object = EmberObject['default'].create({ method: function() {} }); equal(utils.typeOf(undefined), 'undefined', "item of type undefined"); equal(utils.typeOf(a), 'null', "item of type null"); equal(utils.typeOf(arr), 'array', "item of type array"); equal(utils.typeOf(obj), 'object', "item of type object"); equal(utils.typeOf(object), 'instance', "item of type instance"); equal(utils.typeOf(object.method), 'function', "item of type function"); equal(utils.typeOf(EmberObject['default']), 'class', "item of type class"); equal(utils.typeOf(new Error()), 'error', "item of type error"); }); }); enifed('ember-runtime/tests/core/type_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/core'); test('ember-runtime/tests/core/type_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/core/type_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/core/type_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/type_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/type_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/ext/function_test', ['ember-metal/tests/props_helper'], function (props_helper) { 'use strict'; QUnit.module('Function.prototype.observes() helper'); props_helper.testBoth('global observer helper takes multiple params', function(get, set) { if (Ember.EXTEND_PROTOTYPES === false) { ok("undefined" === typeof Function.prototype.observes, 'Function.prototype helper disabled'); return; } var MyMixin = Ember.Mixin.create({ count: 0, foo: function() { set(this, 'count', get(this, 'count')+1); }.observes('bar', 'baz') }); var obj = Ember.mixin({}, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); set(obj, 'baz', "BAZ"); equal(get(obj, 'count'), 2, 'should invoke observer after change'); }); QUnit.module('Function.prototype.on() helper'); props_helper.testBoth('sets up an event listener, and can trigger the function on multiple events', function(get, set) { if (Ember.EXTEND_PROTOTYPES === false) { ok("undefined" === typeof Function.prototype.on, 'Function.prototype helper disabled'); return; } var MyMixin = Ember.Mixin.create({ count: 0, foo: function() { set(this, 'count', get(this, 'count')+1); }.on('bar', 'baz') }); var obj = Ember.mixin({}, Ember.Evented, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke listener immediately'); obj.trigger('bar'); obj.trigger('baz'); equal(get(obj, 'count'), 2, 'should invoke listeners when events trigger'); }); props_helper.testBoth('can be chained with observes', function(get, set) { if (Ember.EXTEND_PROTOTYPES === false) { ok('Function.prototype helper disabled'); return; } var MyMixin = Ember.Mixin.create({ count: 0, bay: 'bay', foo: function() { set(this, 'count', get(this, 'count')+1); }.observes('bay').on('bar') }); var obj = Ember.mixin({}, Ember.Evented, MyMixin); equal(get(obj, 'count'), 0, 'should not invoke listener immediately'); set(obj, 'bay', 'BAY'); obj.trigger('bar'); equal(get(obj, 'count'), 2, 'should invoke observer and listener'); }); QUnit.module('Function.prototype.property() helper'); props_helper.testBoth('sets up a ComputedProperty', function(get, set) { if (Ember.EXTEND_PROTOTYPES === false) { ok("undefined" === typeof Function.prototype.property, 'Function.prototype helper disabled'); return; } var MyClass = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return get(this, 'firstName') + ' ' + get(this, 'lastName'); }.property('firstName', 'lastName') }); var obj = MyClass.create({ firstName: 'Fred', lastName: 'Flinstone' }); equal(get(obj, 'fullName'), 'Fred Flinstone', 'should return the computed value'); set(obj, 'firstName', "Wilma"); equal(get(obj, 'fullName'), 'Wilma Flinstone', 'should return the new computed value'); set(obj, 'lastName', ""); equal(get(obj, 'fullName'), 'Wilma ', 'should return the new computed value'); }); }); enifed('ember-runtime/tests/ext/function_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/ext'); test('ember-runtime/tests/ext/function_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/ext/function_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/ext/function_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/ext'); test('ember-runtime/tests/ext/function_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/ext/function_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/ext/mixin_test', ['ember-metal/property_set', 'ember-metal/property_get', 'ember-metal/mixin', 'ember-metal/platform/create', 'ember-metal/binding', 'ember-metal/run_loop'], function (property_set, property_get, mixin, create, binding, run) { 'use strict'; QUnit.module('system/mixin/binding_test'); QUnit.test('Defining a property ending in Binding should setup binding when applied', function() { var MyMixin = mixin.Mixin.create({ fooBinding: 'bar.baz' }); var obj = { bar: { baz: 'BIFF' } }; run['default'](function() { MyMixin.apply(obj); }); ok(property_get.get(obj, 'fooBinding') instanceof binding.Binding, 'should be a binding object'); equal(property_get.get(obj, 'foo'), 'BIFF', 'binding should be created and synced'); }); QUnit.test('Defining a property ending in Binding should apply to prototype children', function() { var MyMixin, obj, obj2; run['default'](function() { MyMixin = mixin.Mixin.create({ fooBinding: 'bar.baz' }); }); obj = { bar: { baz: 'BIFF' } }; run['default'](function() { MyMixin.apply(obj); }); obj2 = create['default'](obj); run['default'](function() { property_set.set(property_get.get(obj2, 'bar'), 'baz', 'BARG'); }); ok(property_get.get(obj2, 'fooBinding') instanceof binding.Binding, 'should be a binding object'); equal(property_get.get(obj2, 'foo'), 'BARG', 'binding should be created and synced'); }); }); enifed('ember-runtime/tests/ext/mixin_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/ext'); test('ember-runtime/tests/ext/mixin_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/ext/mixin_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/ext/mixin_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/ext'); test('ember-runtime/tests/ext/mixin_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/ext/mixin_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/ext/rsvp_test', ['ember-metal/run_loop', 'ember-runtime/ext/rsvp'], function (run, RSVP) { 'use strict'; /* global Promise:true */ QUnit.module('Ember.RSVP'); QUnit.test('Ensure that errors thrown from within a promise are sent to the console', function() { var error = new Error('Error thrown in a promise for testing purposes.'); try { run['default'](function() { new RSVP['default'].Promise(function(resolve, reject) { throw error; }); }); ok(false, 'expected assertion to be thrown'); } catch(e) { equal(e, error, "error was re-thrown"); } }); var asyncStarted = 0; var asyncEnded = 0; var Promise = RSVP['default'].Promise; var EmberTest; var EmberTesting; QUnit.module("Deferred RSVP's async + Testing", { setup: function() { EmberTest = Ember.Test; EmberTesting = Ember.testing; Ember.Test = { adapter: { asyncStart: function() { asyncStarted++; QUnit.stop(); }, asyncEnd: function() { asyncEnded++; QUnit.start(); } } }; }, teardown: function() { asyncStarted = 0; asyncEnded = 0; Ember.testing = EmberTesting; Ember.Test = EmberTest; } }); QUnit.test("given `Ember.testing = true`, correctly informs the test suite about async steps", function() { expect(19); ok(!run['default'].currentRunLoop, 'expect no run-loop'); Ember.testing = true; equal(asyncStarted, 0); equal(asyncEnded, 0); var user = Promise.resolve({ name: 'tomster' }); equal(asyncStarted, 0); equal(asyncEnded, 0); user.then(function(user) { equal(asyncStarted, 1); equal(asyncEnded, 1); equal(user.name, 'tomster'); return Promise.resolve(1).then(function() { equal(asyncStarted, 1); equal(asyncEnded, 1); }); }).then(function() { equal(asyncStarted, 1); equal(asyncEnded, 1); return new Promise(function(resolve) { QUnit.stop(); // raw async, we must inform the test framework manually setTimeout(function() { QUnit.start(); // raw async, we must inform the test framework manually equal(asyncStarted, 1); equal(asyncEnded, 1); resolve({ name: 'async tomster' }); equal(asyncStarted, 2); equal(asyncEnded, 1); }, 0); }); }).then(function(user) { equal(user.name, 'async tomster'); equal(asyncStarted, 2); equal(asyncEnded, 2); }); }); QUnit.test('TransitionAborted errors are not re-thrown', function() { expect(1); var fakeTransitionAbort = { name: 'TransitionAborted' }; run['default'](RSVP['default'], 'reject', fakeTransitionAbort); ok(true, 'did not throw an error when dealing with TransitionAborted'); }); QUnit.test('rejections like jqXHR which have errorThrown property work', function() { expect(2); var wasEmberTesting = Ember.testing; var wasOnError = Ember.onerror; try { Ember.testing = false; Ember.onerror = function(error) { equal(error, actualError, 'expected the real error on the jqXHR'); equal(error.__reason_with_error_thrown__, jqXHR, 'also retains a helpful reference to the rejection reason'); }; var actualError = new Error("OMG what really happened"); var jqXHR = { errorThrown: actualError }; run['default'](RSVP['default'], 'reject', jqXHR); } finally { Ember.onerror = wasOnError; Ember.testing = wasEmberTesting; } }); QUnit.test('rejections where the errorThrown is a string should wrap the sting in an error object', function() { expect(2); var wasEmberTesting = Ember.testing; var wasOnError = Ember.onerror; try { Ember.testing = false; Ember.onerror = function(error) { equal(error.message, actualError, 'expected the real error on the jqXHR'); equal(error.__reason_with_error_thrown__, jqXHR, 'also retains a helpful reference to the rejection reason'); }; var actualError = "OMG what really happened"; var jqXHR = { errorThrown: actualError }; run['default'](RSVP['default'], 'reject', jqXHR); } finally { Ember.onerror = wasOnError; Ember.testing = wasEmberTesting; } }); }); enifed('ember-runtime/tests/ext/rsvp_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/ext'); test('ember-runtime/tests/ext/rsvp_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/ext/rsvp_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/ext/rsvp_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/ext'); test('ember-runtime/tests/ext/rsvp_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/ext/rsvp_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/inject_test', ['ember-metal/injected_property', 'ember-runtime/inject', 'ember-runtime/system/container', 'ember-runtime/system/object'], function (InjectedProperty, inject, system__container, Object) { 'use strict'; /* global EmberDev */ QUnit.module('inject'); QUnit.test("calling `inject` directly should error", function() { expectAssertion(function() { inject["default"]('foo'); }, /Injected properties must be created through helpers/); }); if (!EmberDev.runningProdBuild) { // this check is done via an assertion which is stripped from // production builds QUnit.test("injection type validation is run when first looked up", function() { expect(1); inject.createInjectionHelper('foo', function() { ok(true, 'should call validation method'); }); var registry = new system__container.Registry(); var container = registry.container(); var AnObject = Object['default'].extend({ container: container, bar: inject["default"].foo(), baz: inject["default"].foo() }); registry.register('foo:main', AnObject); container.lookupFactory('foo:main'); }); } QUnit.test("attempting to inject a nonexistent container key should error", function() { var registry = new system__container.Registry(); var container = registry.container(); var AnObject = Object['default'].extend({ container: container, foo: new InjectedProperty['default']('bar', 'baz') }); registry.register('foo:main', AnObject); throws(function() { container.lookup('foo:main'); }, /Attempting to inject an unknown injection: `bar:baz`/); }); QUnit.test("factories should return a list of lazy injection full names", function() { var AnObject = Object['default'].extend({ foo: new InjectedProperty['default']('foo', 'bar'), bar: new InjectedProperty['default']('quux') }); deepEqual(AnObject._lazyInjections(), { 'foo': 'foo:bar', 'bar': 'quux:bar' }, "should return injected container keys"); }); }); enifed('ember-runtime/tests/inject_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests'); test('ember-runtime/tests/inject_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/inject_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/inject_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests'); test('ember-runtime/tests/inject_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/inject_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/legacy_1x/mixins/observable/chained_test', ['ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-metal/observer'], function (Ember, property_get, property_set, run, EmberObject, observer) { 'use strict'; QUnit.module("Ember.Observable - Observing with @each"); QUnit.test("chained observers on enumerable properties are triggered when the observed property of any item changes", function() { var family = EmberObject['default'].create({ momma: null }); var momma = EmberObject['default'].create({ children: [] }); var child1 = EmberObject['default'].create({ name: "Bartholomew" }); var child2 = EmberObject['default'].create({ name: "Agnes" }); var child3 = EmberObject['default'].create({ name: "Dan" }); var child4 = EmberObject['default'].create({ name: "Nancy" }); property_set.set(family, 'momma', momma); property_set.set(momma, 'children', Ember['default'].A([child1, child2, child3])); var observerFiredCount = 0; observer.addObserver(family, 'momma.children.@each.name', this, function() { observerFiredCount++; }); observerFiredCount = 0; run['default'](function() { property_get.get(momma, 'children').setEach('name', 'Juan'); }); equal(observerFiredCount, 3, "observer fired after changing child names"); observerFiredCount = 0; run['default'](function() { property_get.get(momma, 'children').pushObject(child4); }); equal(observerFiredCount, 1, "observer fired after adding a new item"); observerFiredCount = 0; run['default'](function() { property_set.set(child4, 'name', "Herbert"); }); equal(observerFiredCount, 1, "observer fired after changing property on new object"); property_set.set(momma, 'children', []); observerFiredCount = 0; run['default'](function() { property_set.set(child1, 'name', "Hanna"); }); equal(observerFiredCount, 0, "observer did not fire after removing changing property on a removed object"); }); }); enifed('ember-runtime/tests/legacy_1x/mixins/observable/chained_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/legacy_1x/mixins/observable'); test('ember-runtime/tests/legacy_1x/mixins/observable/chained_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/legacy_1x/mixins/observable/chained_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/legacy_1x/mixins/observable/chained_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/legacy_1x/mixins/observable'); test('ember-runtime/tests/legacy_1x/mixins/observable/chained_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/legacy_1x/mixins/observable/chained_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/legacy_1x/mixins/observable/observable_test', ['ember-metal/property_get', 'ember-metal/enumerable_utils', 'ember-metal/computed', 'ember-metal/run_loop', 'ember-metal/utils', 'ember-metal/mixin', 'ember-runtime/system/string', 'ember-runtime/system/object', 'ember-runtime/mixins/observable'], function (property_get, enumerable_utils, computed, run, utils, mixin, string, EmberObject, Observable) { 'use strict'; var object, ObjectC, ObjectD, objectA, objectB, lookup; var ObservableObject = EmberObject['default'].extend(Observable['default']); var originalLookup = Ember.lookup; // .......................................................... // GET() // QUnit.module("object.get()", { setup: function() { object = ObservableObject.createWithMixins(Observable['default'], { normal: 'value', numberVal: 24, toggleVal: true, computed: computed.computed(function() { return 'value'; })["volatile"](), method: function() { return "value"; }, nullProperty: null, unknownProperty: function(key, value) { this.lastUnknownProperty = key; return "unknown"; } }); } }); QUnit.test("should get normal properties", function() { equal(object.get('normal'), 'value'); }); QUnit.test("should call computed properties and return their result", function() { equal(object.get("computed"), "value"); }); QUnit.test("should return the function for a non-computed property", function() { var value = object.get("method"); equal(utils.typeOf(value), 'function'); }); QUnit.test("should return null when property value is null", function() { equal(object.get("nullProperty"), null); }); QUnit.test("should call unknownProperty when value is undefined", function() { equal(object.get("unknown"), "unknown"); equal(object.lastUnknownProperty, "unknown"); }); // .......................................................... // Ember.GET() // QUnit.module("Ember.get()", { setup: function() { objectA = ObservableObject.createWithMixins({ normal: 'value', numberVal: 24, toggleVal: true, computed: computed.computed(function() { return 'value'; })["volatile"](), method: function() { return "value"; }, nullProperty: null, unknownProperty: function(key, value) { this.lastUnknownProperty = key; return "unknown"; } }); objectB = { normal: 'value', nullProperty: null }; } }); QUnit.test("should get normal properties on Ember.Observable", function() { equal(property_get.get(objectA, 'normal'), 'value'); }); QUnit.test("should call computed properties on Ember.Observable and return their result", function() { equal(property_get.get(objectA, "computed"), "value"); }); QUnit.test("should return the function for a non-computed property on Ember.Observable", function() { var value = property_get.get(objectA, "method"); equal(utils.typeOf(value), 'function'); }); QUnit.test("should return null when property value is null on Ember.Observable", function() { equal(property_get.get(objectA, "nullProperty"), null); }); QUnit.test("should call unknownProperty when value is undefined on Ember.Observable", function() { equal(property_get.get(object, "unknown"), "unknown"); equal(object.lastUnknownProperty, "unknown"); }); QUnit.test("should get normal properties on standard objects", function() { equal(property_get.get(objectB, 'normal'), 'value'); }); QUnit.test("should return null when property is null on standard objects", function() { equal(property_get.get(objectB, 'nullProperty'), null); }); /* QUnit.test("raise if the provided object is null", function() { throws(function() { get(null, 'key'); }); }); */ QUnit.test("raise if the provided object is undefined", function() { expectAssertion(function() { property_get.get(undefined, 'key'); }, /Cannot call get with 'key' on an undefined object/i); }); QUnit.test("should work when object is Ember (used in Ember.get)", function() { equal(property_get.get('Ember.RunLoop'), Ember.RunLoop, 'Ember.get'); equal(property_get.get(Ember, 'RunLoop'), Ember.RunLoop, 'Ember.get(Ember, RunLoop)'); }); QUnit.module("Ember.get() with paths", { setup: function() { lookup = Ember.lookup = {}; }, teardown: function() { Ember.lookup = originalLookup; } }); QUnit.test("should return a property at a given path relative to the lookup", function() { lookup.Foo = ObservableObject.create({ Bar: ObservableObject.createWithMixins({ Baz: computed.computed(function() { return "blargh"; })["volatile"]() }) }); equal(property_get.get('Foo.Bar.Baz'), "blargh"); }); QUnit.test("should return a property at a given path relative to the passed object", function() { var foo = ObservableObject.create({ bar: ObservableObject.createWithMixins({ baz: computed.computed(function() { return "blargh"; })["volatile"]() }) }); equal(property_get.get(foo, 'bar.baz'), "blargh"); }); QUnit.test("should return a property at a given path relative to the lookup - JavaScript hash", function() { lookup.Foo = { Bar: { Baz: "blargh" } }; equal(property_get.get('Foo.Bar.Baz'), "blargh"); }); QUnit.test("should return a property at a given path relative to the passed object - JavaScript hash", function() { var foo = { bar: { baz: "blargh" } }; equal(property_get.get(foo, 'bar.baz'), "blargh"); }); // .......................................................... // SET() // QUnit.module("object.set()", { setup: function() { object = ObservableObject.createWithMixins({ // normal property normal: 'value', // computed property _computed: "computed", computed: computed.computed(function(key, value) { if (value !== undefined) { this._computed = value; } return this._computed; })["volatile"](), // method, but not a property _method: "method", method: function(key, value) { if (value !== undefined) { this._method = value; } return this._method; }, // null property nullProperty: null, // unknown property _unknown: 'unknown', unknownProperty: function(key) { return this._unknown; }, setUnknownProperty: function(key, value) { this._unknown = value; return this._unknown; } }); } }); QUnit.test("should change normal properties and return this", function() { var ret = object.set("normal", "changed"); equal(object.normal, "changed"); equal(ret, object); }); QUnit.test("should call computed properties passing value and return this", function() { var ret = object.set("computed", "changed"); equal(object._computed, "changed"); equal(ret, object); }); QUnit.test("should change normal properties when passing undefined", function() { var ret = object.set('normal', undefined); equal(object.normal, undefined); equal(ret, object); }); QUnit.test("should replace the function for a non-computed property and return this", function() { var ret = object.set("method", "changed"); equal(object._method, "method"); // make sure this was NOT run ok(utils.typeOf(object.method) !== 'function'); equal(ret, object); }); QUnit.test("should replace prover when property value is null", function() { var ret = object.set("nullProperty", "changed"); equal(object.nullProperty, "changed"); equal(ret, object); }); QUnit.test("should call unknownProperty with value when property is undefined", function() { var ret = object.set("unknown", "changed"); equal(object._unknown, "changed"); equal(ret, object); }); // .......................................................... // COMPUTED PROPERTIES // QUnit.module("Computed properties", { setup: function() { lookup = Ember.lookup = {}; object = ObservableObject.createWithMixins({ // REGULAR computedCalls: [], computed: computed.computed(function(key, value) { this.computedCalls.push(value); return 'computed'; })["volatile"](), computedCachedCalls: [], computedCached: computed.computed(function(key, value) { this.computedCachedCalls.push(value); return 'computedCached'; }), // DEPENDENT KEYS changer: 'foo', dependentCalls: [], dependent: computed.computed(function(key, value) { this.dependentCalls.push(value); return 'dependent'; }).property('changer')["volatile"](), dependentFrontCalls: [], dependentFront: computed.computed('changer', function(key, value) { this.dependentFrontCalls.push(value); return 'dependentFront'; })["volatile"](), dependentCachedCalls: [], dependentCached: computed.computed(function(key, value) { this.dependentCachedCalls.push(value); return 'dependentCached'; }).property('changer'), // every time it is recomputed, increments call incCallCount: 0, inc: computed.computed(function() { return this.incCallCount++; }).property('changer'), // depends on cached property which depends on another property... nestedIncCallCount: 0, nestedInc: computed.computed(function(key, value) { property_get.get(this, 'inc'); return this.nestedIncCallCount++; }).property('inc'), // two computed properties that depend on a third property state: 'on', isOn: computed.computed(function(key, value) { if (value !== undefined) { this.set('state', 'on'); } return this.get('state') === 'on'; }).property('state')["volatile"](), isOff: computed.computed(function(key, value) { if (value !== undefined) { this.set('state', 'off'); } return this.get('state') === 'off'; }).property('state')["volatile"]() }); }, teardown: function() { Ember.lookup = originalLookup; } }); QUnit.test("getting values should call function return value", function() { // get each property twice. Verify return. var keys = string.w('computed computedCached dependent dependentFront dependentCached'); enumerable_utils.forEach(keys, function(key) { equal(object.get(key), key, string.fmt('Try #1: object.get(%@) should run function', [key])); equal(object.get(key), key, string.fmt('Try #2: object.get(%@) should run function', [key])); }); // verify each call count. cached should only be called once enumerable_utils.forEach(string.w('computedCalls dependentFrontCalls dependentCalls'), function(key) { equal(object[key].length, 2, string.fmt('non-cached property %@ should be called 2x', [key])); }); enumerable_utils.forEach(string.w('computedCachedCalls dependentCachedCalls'), function(key) { equal(object[key].length, 1, string.fmt('non-cached property %@ should be called 1x', [key])); }); }); QUnit.test("setting values should call function return value", function() { // get each property twice. Verify return. var keys = string.w('computed dependent dependentFront computedCached dependentCached'); var values = string.w('value1 value2'); enumerable_utils.forEach(keys, function(key) { equal(object.set(key, values[0]), object, string.fmt('Try #1: object.set(%@, %@) should run function', [key, values[0]])); equal(object.set(key, values[1]), object, string.fmt('Try #2: object.set(%@, %@) should run function', [key, values[1]])); equal(object.set(key, values[1]), object, string.fmt('Try #3: object.set(%@, %@) should not run function since it is setting same value as before', [key, values[1]])); }); // verify each call count. cached should only be called once enumerable_utils.forEach(keys, function(key) { var calls = object[key + 'Calls']; var idx, expectedLength; // Cached properties first check their cached value before setting the // property. Other properties blindly call set. expectedLength = 3; equal(calls.length, expectedLength, string.fmt('set(%@) should be called the right amount of times', [key])); for (idx=0;idx<2;idx++) { equal(calls[idx], values[idx], string.fmt('call #%@ to set(%@) should have passed value %@', [idx+1, key, values[idx]])); } }); }); QUnit.test("notify change should clear cache", function() { // call get several times to collect call count object.get('computedCached'); // should run func object.get('computedCached'); // should not run func object.propertyWillChange('computedCached') .propertyDidChange('computedCached'); object.get('computedCached'); // should run again equal(object.computedCachedCalls.length, 2, 'should have invoked method 2x'); }); QUnit.test("change dependent should clear cache", function() { // call get several times to collect call count var ret1 = object.get('inc'); // should run func equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); object.set('changer', 'bar'); equal(object.get('inc'), ret1+1, 'should increment after dependent key changes'); // should run again }); QUnit.test("just notifying change of dependent should clear cache", function() { // call get several times to collect call count var ret1 = object.get('inc'); // should run func equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); object.notifyPropertyChange('changer'); equal(object.get('inc'), ret1+1, 'should increment after dependent key changes'); // should run again }); QUnit.test("changing dependent should clear nested cache", function() { // call get several times to collect call count var ret1 = object.get('nestedInc'); // should run func equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop'); object.set('changer', 'bar'); equal(object.get('nestedInc'), ret1+1, 'should increment after dependent key changes'); // should run again }); QUnit.test("just notifying change of dependent should clear nested cache", function() { // call get several times to collect call count var ret1 = object.get('nestedInc'); // should run func equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop'); object.notifyPropertyChange('changer'); equal(object.get('nestedInc'), ret1+1, 'should increment after dependent key changes'); // should run again }); // This verifies a specific bug encountered where observers for computed // properties would fire before their prop caches were cleared. QUnit.test("change dependent should clear cache when observers of dependent are called", function() { // call get several times to collect call count var ret1 = object.get('inc'); // should run func equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); // add observer to verify change... object.addObserver('inc', this, function() { equal(object.get('inc'), ret1+1, 'should increment after dependent key changes'); // should run again }); // now run object.set('changer', 'bar'); }); QUnit.test('setting one of two computed properties that depend on a third property should clear the kvo cache', function() { // we have to call set twice to fill up the cache object.set('isOff', true); object.set('isOn', true); // setting isOff to true should clear the kvo cache object.set('isOff', true); equal(object.get('isOff'), true, 'object.isOff should be true'); equal(object.get('isOn'), false, 'object.isOn should be false'); }); QUnit.test("dependent keys should be able to be specified as property paths", function() { var depObj = ObservableObject.createWithMixins({ menu: ObservableObject.create({ price: 5 }), menuPrice: computed.computed(function() { return this.get('menu.price'); }).property('menu.price') }); equal(depObj.get('menuPrice'), 5, "precond - initial value returns 5"); depObj.set('menu.price', 6); equal(depObj.get('menuPrice'), 6, "cache is properly invalidated after nested property changes"); }); QUnit.test("nested dependent keys should propagate after they update", function() { var bindObj; run['default'](function () { lookup.DepObj = ObservableObject.createWithMixins({ restaurant: ObservableObject.create({ menu: ObservableObject.create({ price: 5 }) }), price: computed.computed(function() { return this.get('restaurant.menu.price'); }).property('restaurant.menu.price') }); bindObj = ObservableObject.createWithMixins({ priceBinding: "DepObj.price" }); }); equal(bindObj.get('price'), 5, "precond - binding propagates"); run['default'](function () { lookup.DepObj.set('restaurant.menu.price', 10); }); equal(bindObj.get('price'), 10, "binding propagates after a nested dependent keys updates"); run['default'](function () { lookup.DepObj.set('restaurant.menu', ObservableObject.create({ price: 15 })); }); equal(bindObj.get('price'), 15, "binding propagates after a middle dependent keys updates"); }); QUnit.test("cacheable nested dependent keys should clear after their dependencies update", function() { ok(true); var DepObj; run['default'](function() { lookup.DepObj = DepObj = ObservableObject.createWithMixins({ restaurant: ObservableObject.create({ menu: ObservableObject.create({ price: 5 }) }), price: computed.computed(function() { return this.get('restaurant.menu.price'); }).property('restaurant.menu.price') }); }); equal(DepObj.get('price'), 5, "precond - computed property is correct"); run['default'](function() { DepObj.set('restaurant.menu.price', 10); }); equal(DepObj.get('price'), 10, "cacheable computed properties are invalidated even if no run loop occurred"); run['default'](function() { DepObj.set('restaurant.menu.price', 20); }); equal(DepObj.get('price'), 20, "cacheable computed properties are invalidated after a second get before a run loop"); equal(DepObj.get('price'), 20, "precond - computed properties remain correct after a run loop"); run['default'](function() { DepObj.set('restaurant.menu', ObservableObject.create({ price: 15 })); }); equal(DepObj.get('price'), 15, "cacheable computed properties are invalidated after a middle property changes"); run['default'](function() { DepObj.set('restaurant.menu', ObservableObject.create({ price: 25 })); }); equal(DepObj.get('price'), 25, "cacheable computed properties are invalidated after a middle property changes again, before a run loop"); }); // .......................................................... // OBSERVABLE OBJECTS // QUnit.module("Observable objects & object properties ", { setup: function() { object = ObservableObject.createWithMixins({ normal: 'value', abnormal: 'zeroValue', numberVal: 24, toggleVal: true, observedProperty: 'beingWatched', testRemove: 'observerToBeRemoved', normalArray: Ember.A([1,2,3,4,5]), getEach: function() { var keys = ['normal','abnormal']; var ret = []; for (var idx=0; idx=0) { return this; } this._content.push(obj); this.enumerableContentDidChange(); }, nextObject: function(idx) { return idx >= property_get.get(this, 'length') ? undefined : this._content[idx]; }, length: computed.computed(function() { return this._content.length; }), slice: function() { return this._content.slice(); } }); EnumerableTests['default'].extend({ name: 'Basic Enumerable', newObject: function(ary) { ary = ary ? ary.slice() : this.newFixture(3); return new TestEnumerable(ary); }, // allows for testing of the basic enumerable after an internal mutation mutate: function(obj) { obj.addObject(obj._content.length+1); }, toArray: function(obj) { return obj.slice(); } }).run(); QUnit.module('Ember.Enumerable'); QUnit.test("should apply Ember.Array to return value of map", function() { var x = EmberObject['default'].createWithMixins(Enumerable['default']); var y = x.map(K); equal(EmberArray['default'].detect(y), true, "should have mixin applied"); }); QUnit.test("should apply Ember.Array to return value of filter", function() { var x = EmberObject['default'].createWithMixins(Enumerable['default']); var y = x.filter(K); equal(EmberArray['default'].detect(y), true, "should have mixin applied"); }); QUnit.test("should apply Ember.Array to return value of invoke", function() { var x = EmberObject['default'].createWithMixins(Enumerable['default']); var y = x.invoke(K); equal(EmberArray['default'].detect(y), true, "should have mixin applied"); }); QUnit.test("should apply Ember.Array to return value of toArray", function() { var x = EmberObject['default'].createWithMixins(Enumerable['default']); var y = x.toArray(K); equal(EmberArray['default'].detect(y), true, "should have mixin applied"); }); QUnit.test("should apply Ember.Array to return value of without", function() { var x = EmberObject['default'].createWithMixins(Enumerable['default'], { contains: function() { return true; } }); var y = x.without(K); equal(EmberArray['default'].detect(y), true, "should have mixin applied"); }); QUnit.test("should apply Ember.Array to return value of uniq", function() { var x = EmberObject['default'].createWithMixins(Enumerable['default']); var y = x.uniq(K); equal(EmberArray['default'].detect(y), true, "should have mixin applied"); }); QUnit.test('any', function() { var kittens = Ember['default'].A([{ color: 'white' }, { color: 'black' }, { color: 'white' }]); var foundWhite = kittens.any(function(kitten) { return kitten.color === 'white'; }); var foundWhite2 = kittens.isAny('color', 'white'); equal(foundWhite, true); equal(foundWhite2, true); }); QUnit.test('any with NaN', function() { var numbers = Ember['default'].A([1,2,NaN,4]); var hasNaN = numbers.any(function(n) { return isNaN(n); }); equal(hasNaN, true, "works when matching NaN"); }); QUnit.test('every', function() { var allColorsKittens = Ember['default'].A([{ color: 'white' }, { color: 'black' }, { color: 'white' }]); var allWhiteKittens = Ember['default'].A([{ color: 'white' }, { color: 'white' }, { color: 'white' }]); var allWhite = false; var whiteKittenPredicate = function(kitten) { return kitten.color === 'white'; }; allWhite = allColorsKittens.every(whiteKittenPredicate); equal(allWhite, false); allWhite = allWhiteKittens.every(whiteKittenPredicate); equal(allWhite, true); allWhite = allColorsKittens.isEvery('color', 'white'); equal(allWhite, false); allWhite = allWhiteKittens.isEvery('color', 'white'); equal(allWhite, true); }); // .......................................................... // CONTENT DID CHANGE // var DummyEnum = EmberObject['default'].extend(Enumerable['default'], { nextObject: function() {}, length: 0 }); var obj, observer; // .......................................................... // NOTIFY ENUMERABLE PROPERTY // QUnit.module('mixins/enumerable/enumerableContentDidChange'); QUnit.test('should notify observers of []', function() { var obj = EmberObject['default'].createWithMixins(Enumerable['default'], { nextObject: function() {}, // avoid exceptions _count: 0, enumerablePropertyDidChange: mixin.observer('[]', function() { this._count++; }) }); equal(obj._count, 0, 'should not have invoked yet'); obj.enumerableContentWillChange(); obj.enumerableContentDidChange(); equal(obj._count, 1, 'should have invoked'); }); // .......................................................... // NOTIFY CHANGES TO LENGTH // QUnit.module('notify observers of length', { setup: function() { obj = DummyEnum.createWithMixins({ _after: 0, lengthDidChange: mixin.observer('length', function() { this._after++; }) }); equal(obj._after, 0, 'should not have fired yet'); }, teardown: function() { obj = null; } }); QUnit.test('should notify observers when call with no params', function() { obj.enumerableContentWillChange(); equal(obj._after, 0); obj.enumerableContentDidChange(); equal(obj._after, 1); }); // API variation that included items only QUnit.test('should not notify when passed arrays of same length', function() { var added = ['foo']; var removed = ['bar']; obj.enumerableContentWillChange(removed, added); equal(obj._after, 0); obj.enumerableContentDidChange(removed, added); equal(obj._after, 0); }); QUnit.test('should notify when passed arrays of different length', function() { var added = ['foo']; var removed = ['bar', 'baz']; obj.enumerableContentWillChange(removed, added); equal(obj._after, 0); obj.enumerableContentDidChange(removed, added); equal(obj._after, 1); }); // API variation passes indexes only QUnit.test('should not notify when passed with indexes', function() { obj.enumerableContentWillChange(1, 1); equal(obj._after, 0); obj.enumerableContentDidChange(1, 1); equal(obj._after, 0); }); QUnit.test('should notify when passed old index API with delta', function() { obj.enumerableContentWillChange(1, 2); equal(obj._after, 0); obj.enumerableContentDidChange(1, 2); equal(obj._after, 1); }); // .......................................................... // NOTIFY ENUMERABLE OBSERVER // QUnit.module('notify enumerable observers', { setup: function() { obj = DummyEnum.create(); observer = EmberObject['default'].createWithMixins({ _before: null, _after: null, enumerableWillChange: function() { equal(this._before, null); // should only call once this._before = Array.prototype.slice.call(arguments); }, enumerableDidChange: function() { equal(this._after, null); // should only call once this._after = Array.prototype.slice.call(arguments); } }); obj.addEnumerableObserver(observer); }, teardown: function() { obj = observer = null; } }); QUnit.test('should notify enumerable observers when called with no params', function() { obj.enumerableContentWillChange(); deepEqual(observer._before, [obj, null, null]); obj.enumerableContentDidChange(); deepEqual(observer._after, [obj, null, null]); }); // API variation that included items only QUnit.test('should notify when called with same length items', function() { var added = ['foo']; var removed = ['bar']; obj.enumerableContentWillChange(removed, added); deepEqual(observer._before, [obj, removed, added]); obj.enumerableContentDidChange(removed, added); deepEqual(observer._after, [obj, removed, added]); }); QUnit.test('should notify when called with diff length items', function() { var added = ['foo', 'baz']; var removed = ['bar']; obj.enumerableContentWillChange(removed, added); deepEqual(observer._before, [obj, removed, added]); obj.enumerableContentDidChange(removed, added); deepEqual(observer._after, [obj, removed, added]); }); QUnit.test('should not notify when passed with indexes only', function() { obj.enumerableContentWillChange(1, 2); deepEqual(observer._before, [obj, 1, 2]); obj.enumerableContentDidChange(1, 2); deepEqual(observer._after, [obj, 1, 2]); }); QUnit.test('removing enumerable observer should disable', function() { obj.removeEnumerableObserver(observer); obj.enumerableContentWillChange(); deepEqual(observer._before, null); obj.enumerableContentDidChange(); deepEqual(observer._after, null); }); }); enifed('ember-runtime/tests/mixins/enumerable_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/enumerable_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/enumerable_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/enumerable_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/enumerable_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/enumerable_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/mixins/mutable_array_test', ['ember-runtime/tests/suites/mutable_array', 'ember-runtime/mixins/mutable_array', 'ember-runtime/system/object', 'ember-metal/computed'], function (MutableArrayTests, MutableArray, EmberObject, computed) { 'use strict'; var TestMutableArray = EmberObject['default'].extend(MutableArray['default'], { _content: null, init: function(ary) { this._content = Ember.A(ary || []); }, replace: function(idx, amt, objects) { var args = objects ? objects.slice() : []; var removeAmt = amt; var addAmt = args.length; this.arrayContentWillChange(idx, removeAmt, addAmt); args.unshift(amt); args.unshift(idx); this._content.splice.apply(this._content, args); this.arrayContentDidChange(idx, removeAmt, addAmt); return this; }, objectAt: function(idx) { return this._content[idx]; }, length: computed.computed(function() { return this._content.length; }), slice: function() { return this._content.slice(); } }); MutableArrayTests['default'].extend({ name: 'Basic Mutable Array', newObject: function(ary) { ary = ary ? ary.slice() : this.newFixture(3); return new TestMutableArray(ary); }, // allows for testing of the basic enumerable after an internal mutation mutate: function(obj) { obj.addObject(this.getFixture(1)[0]); }, toArray: function(obj) { return obj.slice(); } }).run(); }); enifed('ember-runtime/tests/mixins/mutable_array_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/mutable_array_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/mutable_array_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/mutable_array_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/mutable_array_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/mutable_array_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/mixins/mutable_enumerable_test', ['ember-runtime/tests/suites/mutable_enumerable', 'ember-runtime/mixins/mutable_enumerable', 'ember-metal/enumerable_utils', 'ember-runtime/system/object', 'ember-metal/computed', 'ember-metal/property_get'], function (MutableEnumerableTests, MutableEnumerable, enumerable_utils, EmberObject, computed, property_get) { 'use strict'; var TestMutableEnumerable = EmberObject['default'].extend(MutableEnumerable['default'], { _content: null, addObject: function(obj) { if (enumerable_utils.indexOf(this._content, obj)>=0) { return this; } this.enumerableContentWillChange(null, [obj]); this._content.push(obj); this.enumerableContentDidChange(null, [obj]); }, removeObject: function(obj) { var idx = enumerable_utils.indexOf(this._content, obj); if (idx<0) { return this; } this.enumerableContentWillChange([obj], null); this._content.splice(idx, 1); this.enumerableContentDidChange([obj], null); return this; }, init: function(ary) { this._content = ary || []; }, nextObject: function(idx) { return idx>=property_get.get(this, 'length') ? undefined : this._content[idx]; }, length: computed.computed(function() { return this._content.length; }), slice: function() { return this._content.slice(); } }); MutableEnumerableTests['default'].extend({ name: 'Basic Mutable Array', newObject: function(ary) { ary = ary ? ary.slice() : this.newFixture(3); return new TestMutableEnumerable(ary); }, // allows for testing of the basic enumerable after an internal mutation mutate: function(obj) { obj.addObject(this.getFixture(1)[0]); }, toArray: function(obj) { return obj.slice(); } }).run(); }); enifed('ember-runtime/tests/mixins/mutable_enumerable_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/mutable_enumerable_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/mutable_enumerable_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/mutable_enumerable_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/mutable_enumerable_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/mutable_enumerable_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/mixins/observable_test', ['ember-metal/computed', 'ember-metal/observer', 'ember-runtime/system/object', 'ember-metal/tests/props_helper'], function (computed, observer, EmberObject, props_helper) { 'use strict'; QUnit.module('mixins/observable'); QUnit.test('should be able to use getProperties to get a POJO of provided keys', function() { var obj = EmberObject['default'].create({ firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }); var pojo = obj.getProperties("firstName", "lastName"); equal("Steve", pojo.firstName); equal("Jobs", pojo.lastName); }); QUnit.test('should be able to use getProperties with array parameter to get a POJO of provided keys', function() { var obj = EmberObject['default'].create({ firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }); var pojo = obj.getProperties(["firstName", "lastName"]); equal("Steve", pojo.firstName); equal("Jobs", pojo.lastName); }); QUnit.test('should be able to use setProperties to set multiple properties at once', function() { var obj = EmberObject['default'].create({ firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }); obj.setProperties({ firstName: "Tim", lastName: "Cook" }); equal("Tim", obj.get("firstName")); equal("Cook", obj.get("lastName")); }); props_helper.testBoth('calling setProperties completes safely despite exceptions', function(get, set) { var exc = new Error("Something unexpected happened!"); var obj = EmberObject['default'].createWithMixins({ firstName: "Steve", lastName: "Jobs", companyName: computed.computed(function(key, value) { if (value !== undefined) { throw exc; } return "Apple, Inc."; }) }); var firstNameChangedCount = 0; observer.addObserver(obj, 'firstName', function() { firstNameChangedCount++; }); try { obj.setProperties({ firstName: 'Tim', lastName: 'Cook', companyName: 'Fruit Co., Inc.' }); } catch(err) { if (err !== exc) { throw err; } } equal(firstNameChangedCount, 1, 'firstName should have fired once'); }); props_helper.testBoth("should be able to retrieve cached values of computed properties without invoking the computed property", function(get) { var obj = EmberObject['default'].createWithMixins({ foo: computed.computed(function() { return "foo"; }), bar: "bar" }); equal(obj.cacheFor('foo'), undefined, "should return undefined if no value has been cached"); get(obj, 'foo'); equal(get(obj, 'foo'), "foo", "precond - should cache the value"); equal(obj.cacheFor('foo'), "foo", "should return the cached value after it is invoked"); equal(obj.cacheFor('bar'), undefined, "returns undefined if the value is not a computed property"); }); QUnit.test('incrementProperty should work even if value is number in string', function() { var obj = EmberObject['default'].create({ age: "24" }); obj.incrementProperty('age'); equal(25, obj.get('age')); }); }); enifed('ember-runtime/tests/mixins/observable_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/observable_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/observable_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/observable_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/observable_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/observable_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/mixins/promise_proxy_test', ['ember-metal/platform/create', 'ember-metal/property_get', 'ember-metal/run_loop', 'ember-runtime/system/object_proxy', 'ember-runtime/mixins/promise_proxy', 'ember-runtime/ext/rsvp', 'rsvp'], function (create, property_get, run, ObjectProxy, PromiseProxyMixin, rsvp, RSVP) { 'use strict'; var ObjectPromiseProxy; QUnit.test("present on ember namespace", function() { ok(PromiseProxyMixin['default'], "expected PromiseProxyMixin to exist"); }); QUnit.module("Ember.PromiseProxy - ObjectProxy", { setup: function() { ObjectPromiseProxy = ObjectProxy['default'].extend(PromiseProxyMixin['default']); } }); QUnit.test("no promise, invoking then should raise", function() { var proxy = ObjectPromiseProxy.create(); throws(function() { proxy.then(function() { return this; }, function() { return this; }); }, new RegExp("PromiseProxy's promise must be set")); }); QUnit.test("fulfillment", function() { var value = { firstName: 'stef', lastName: 'penner' }; var deferred = RSVP.defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); var didFulfillCount = 0; var didRejectCount = 0; proxy.then(function() { didFulfillCount++; }, function() { didRejectCount++; }); equal(property_get.get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(property_get.get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); equal(property_get.get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(property_get.get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); equal(didFulfillCount, 0, 'should not yet have been fulfilled'); equal(didRejectCount, 0, 'should not yet have been rejected'); run['default'](deferred, 'resolve', value); equal(didFulfillCount, 1, 'should have been fulfilled'); equal(didRejectCount, 0, 'should not have been rejected'); equal(property_get.get(proxy, 'content'), value, 'expects the proxy to have content'); equal(property_get.get(proxy, 'reason'), undefined, 'expects the proxy to still have no reason'); equal(property_get.get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading'); equal(property_get.get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); run['default'](deferred, 'resolve', value); equal(didFulfillCount, 1, 'should still have been only fulfilled once'); equal(didRejectCount, 0, 'should still not have been rejected'); run['default'](deferred, 'reject', value); equal(didFulfillCount, 1, 'should still have been only fulfilled once'); equal(didRejectCount, 0, 'should still not have been rejected'); equal(property_get.get(proxy, 'content'), value, 'expects the proxy to have still have same content'); equal(property_get.get(proxy, 'reason'), undefined, 'expects the proxy still to have no reason'); equal(property_get.get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading'); equal(property_get.get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); // rest of the promise semantics are tested in directly in RSVP }); QUnit.test("rejection", function() { var reason = new Error("failure"); var deferred = RSVP.defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); var didFulfillCount = 0; var didRejectCount = 0; proxy.then(function() { didFulfillCount++; }, function() { didRejectCount++; }); equal(property_get.get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(property_get.get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); equal(property_get.get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(property_get.get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); equal(didFulfillCount, 0, 'should not yet have been fulfilled'); equal(didRejectCount, 0, 'should not yet have been rejected'); run['default'](deferred, 'reject', reason); equal(didFulfillCount, 0, 'should not yet have been fulfilled'); equal(didRejectCount, 1, 'should have been rejected'); equal(property_get.get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(property_get.get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); equal(property_get.get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading'); equal(property_get.get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(property_get.get(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); run['default'](deferred, 'reject', reason); equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); equal(didRejectCount, 1, 'should still remain rejected'); run['default'](deferred, 'resolve', 1); equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); equal(didRejectCount, 1, 'should still remain rejected'); equal(property_get.get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(property_get.get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); equal(property_get.get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading'); equal(property_get.get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(property_get.get(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); }); QUnit.test("unhandled rejects still propagate to RSVP.on('error', ...) ", function() { expect(1); RSVP.on('error', onerror); RSVP.off('error', rsvp.onerrorDefault); var expectedReason = new Error("failure"); var deferred = RSVP.defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); proxy.get('promise'); function onerror(reason) { equal(reason, expectedReason, 'expected reason'); } RSVP.on('error', onerror); RSVP.off('error', rsvp.onerrorDefault); run['default'](deferred, 'reject', expectedReason); RSVP.on('error', rsvp.onerrorDefault); RSVP.off('error', onerror); run['default'](deferred, 'reject', expectedReason); RSVP.on('error', rsvp.onerrorDefault); RSVP.off('error', onerror); }); QUnit.test("should work with promise inheritance", function() { function PromiseSubclass() { RSVP.Promise.apply(this, arguments); } PromiseSubclass.prototype = create['default'](RSVP.Promise.prototype); PromiseSubclass.prototype.constructor = PromiseSubclass; PromiseSubclass.cast = RSVP.Promise.cast; var proxy = ObjectPromiseProxy.create({ promise: new PromiseSubclass(function() { }) }); ok(proxy.then() instanceof PromiseSubclass, 'promise proxy respected inheritance'); }); QUnit.test("should reset isFulfilled and isRejected when promise is reset", function() { var deferred = rsvp["default"].defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); equal(property_get.get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(property_get.get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); run['default'](deferred, 'resolve'); equal(property_get.get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading'); equal(property_get.get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); var anotherDeferred = rsvp["default"].defer(); proxy.set('promise', anotherDeferred.promise); equal(property_get.get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(property_get.get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(property_get.get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); run['default'](anotherDeferred, 'reject'); equal(property_get.get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading'); equal(property_get.get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(property_get.get(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected'); equal(property_get.get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); }); QUnit.test("should have content when isFulfilled is set", function() { var deferred = rsvp["default"].defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); proxy.addObserver('isFulfilled', function() { equal(property_get.get(proxy, 'content'), true); }); run['default'](deferred, 'resolve', true); }); QUnit.test("should have reason when isRejected is set", function() { var error = new Error('Y U REJECT?!?'); var deferred = rsvp["default"].defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); proxy.addObserver('isRejected', function() { equal(property_get.get(proxy, 'reason'), error); }); try { run['default'](deferred, 'reject', error); } catch(e) { equal(e, error); } }); }); enifed('ember-runtime/tests/mixins/promise_proxy_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/promise_proxy_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/promise_proxy_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/promise_proxy_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/promise_proxy_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/promise_proxy_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/mixins/sortable_test', ['ember-metal/core', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-metal/mixin', 'ember-metal/events', 'ember-runtime/system/array_proxy', 'ember-runtime/mixins/sortable', 'ember-runtime/system/object', 'ember-runtime/controllers/array_controller'], function (Ember, property_set, run, mixin, events, ArrayProxy, SortableMixin, EmberObject, ArrayController) { 'use strict'; var unsortedArray, sortedArrayController; QUnit.module("Ember.Sortable"); QUnit.module("Ember.Sortable with content", { setup: function() { run['default'](function() { var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember['default'].A(Ember['default'].A(array).copy()); sortedArrayController = ArrayProxy['default'].createWithMixins(SortableMixin['default'], { content: unsortedArray }); }); }, teardown: function() { run['default'](function() { sortedArrayController.set('content', null); sortedArrayController.destroy(); }); } }); QUnit.test("if you do not specify `sortProperties` sortable have no effect", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(0).name, 'Scumbag Dale', 'array is in it natural order'); unsortedArray.pushObject({ id: 4, name: 'Scumbag Chavard' }); equal(sortedArrayController.get('length'), 4, 'array has 4 items'); equal(sortedArrayController.objectAt(3).name, 'Scumbag Chavard', 'a new object was inserted in the natural order'); sortedArrayController.set('sortProperties', []); unsortedArray.pushObject({ id: 5, name: 'Scumbag Jackson' }); equal(sortedArrayController.get('length'), 5, 'array has 5 items'); equal(sortedArrayController.objectAt(4).name, 'Scumbag Jackson', 'a new object was inserted in the natural order with empty array as sortProperties'); }); QUnit.test("you can change sorted properties", function() { sortedArrayController.set('sortProperties', ['id']); equal(sortedArrayController.objectAt(0).name, 'Scumbag Dale', 'array is sorted by id'); equal(sortedArrayController.get('length'), 3, 'array has 3 items'); sortedArrayController.set('sortAscending', false); equal(sortedArrayController.objectAt(0).name, 'Scumbag Bryn', 'array is sorted by id in DESC order'); equal(sortedArrayController.objectAt(2).name, 'Scumbag Dale', 'array is sorted by id in DESC order'); equal(sortedArrayController.get('length'), 3, 'array has 3 items'); sortedArrayController.set('sortProperties', ['name']); equal(sortedArrayController.objectAt(0).name, 'Scumbag Katz', 'array is sorted by name in DESC order'); equal(sortedArrayController.get('length'), 3, 'array has 3 items'); }); QUnit.test("changing sort order triggers observers", function() { var observer; var changeCount = 0; observer = EmberObject['default'].createWithMixins({ array: sortedArrayController, arrangedDidChange: mixin.observer('array.[]', function() { changeCount++; }) }); equal(changeCount, 0, 'precond - changeCount starts at 0'); sortedArrayController.set('sortProperties', ['id']); equal(changeCount, 1, 'setting sortProperties increments changeCount'); sortedArrayController.set('sortAscending', false); equal(changeCount, 2, 'changing sortAscending increments changeCount'); sortedArrayController.set('sortAscending', true); equal(changeCount, 3, 'changing sortAscending again increments changeCount'); run['default'](function() { observer.destroy(); }); }); QUnit.test("changing sortProperties and sortAscending with setProperties, sortProperties appearing first", function() { sortedArrayController.set('sortProperties', ['name']); sortedArrayController.set('sortAscending', false); equal(sortedArrayController.objectAt(0).name, 'Scumbag Katz', 'array is sorted by name in DESC order'); equal(sortedArrayController.objectAt(2).name, 'Scumbag Bryn', 'array is sorted by name in DESC order'); sortedArrayController.setProperties({ sortProperties: ['id'], sortAscending: true }); equal(sortedArrayController.objectAt(0).id, 1, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).id, 3, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); sortedArrayController.setProperties({ sortProperties: ['name'], sortAscending: false }); equal(sortedArrayController.objectAt(0).name, 'Scumbag Katz', 'array is sorted by name in DESC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).name, 'Scumbag Bryn', 'array is sorted by name in DESC order after setting sortAscending and sortProperties'); sortedArrayController.setProperties({ sortProperties: ['id'], sortAscending: false }); equal(sortedArrayController.objectAt(0).id, 3, 'array is sorted by id in DESC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).id, 1, 'array is sorted by id in DESC order after setting sortAscending and sortProperties'); sortedArrayController.setProperties({ sortProperties: ['id'], sortAscending: true }); equal(sortedArrayController.objectAt(0).id, 1, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).id, 3, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); }); QUnit.test("changing sortProperties and sortAscending with setProperties, sortAscending appearing first", function() { sortedArrayController.set('sortProperties', ['name']); sortedArrayController.set('sortAscending', false); equal(sortedArrayController.objectAt(0).name, 'Scumbag Katz', 'array is sorted by name in DESC order'); equal(sortedArrayController.objectAt(2).name, 'Scumbag Bryn', 'array is sorted by name in DESC order'); sortedArrayController.setProperties({ sortAscending: true, sortProperties: ['id'] }); equal(sortedArrayController.objectAt(0).id, 1, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).id, 3, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); sortedArrayController.setProperties({ sortAscending: false, sortProperties: ['name'] }); equal(sortedArrayController.objectAt(0).name, 'Scumbag Katz', 'array is sorted by name in DESC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).name, 'Scumbag Bryn', 'array is sorted by name in DESC order after setting sortAscending and sortProperties'); sortedArrayController.setProperties({ sortAscending: false, sortProperties: ['id'] }); equal(sortedArrayController.objectAt(0).id, 3, 'array is sorted by id in DESC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).id, 1, 'array is sorted by id in DESC order after setting sortAscending and sortProperties'); sortedArrayController.setProperties({ sortAscending: true, sortProperties: ['id'] }); equal(sortedArrayController.objectAt(0).id, 1, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); equal(sortedArrayController.objectAt(2).id, 3, 'array is sorted by id in ASC order after setting sortAscending and sortProperties'); }); QUnit.module("Ember.Sortable with content and sortProperties", { setup: function() { run['default'](function() { var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember['default'].A(Ember['default'].A(array).copy()); sortedArrayController = ArrayController['default'].create({ content: unsortedArray, sortProperties: ['name'] }); }); }, teardown: function() { run['default'](function() { sortedArrayController.destroy(); }); } }); QUnit.test("sortable object will expose associated content in the right order", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(0).name, 'Scumbag Bryn', 'array is sorted by name'); }); QUnit.test("you can add objects in sorted order", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); unsortedArray.pushObject({ id: 4, name: 'Scumbag Chavard' }); equal(sortedArrayController.get('length'), 4, 'array has 4 items'); equal(sortedArrayController.objectAt(1).name, 'Scumbag Chavard', 'a new object added to content was inserted according to given constraint'); sortedArrayController.addObject({ id: 5, name: 'Scumbag Fucs' }); equal(sortedArrayController.get('length'), 5, 'array has 5 items'); equal(sortedArrayController.objectAt(3).name, 'Scumbag Fucs', 'a new object added to controller was inserted according to given constraint'); }); QUnit.test("you can push objects in sorted order", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); unsortedArray.pushObject({ id: 4, name: 'Scumbag Chavard' }); equal(sortedArrayController.get('length'), 4, 'array has 4 items'); equal(sortedArrayController.objectAt(1).name, 'Scumbag Chavard', 'a new object added to content was inserted according to given constraint'); sortedArrayController.pushObject({ id: 5, name: 'Scumbag Fucs' }); equal(sortedArrayController.get('length'), 5, 'array has 5 items'); equal(sortedArrayController.objectAt(3).name, 'Scumbag Fucs', 'a new object added to controller was inserted according to given constraint'); }); QUnit.test("you can unshift objects in sorted order", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); unsortedArray.unshiftObject({ id: 4, name: 'Scumbag Chavard' }); equal(sortedArrayController.get('length'), 4, 'array has 4 items'); equal(sortedArrayController.objectAt(1).name, 'Scumbag Chavard', 'a new object added to content was inserted according to given constraint'); sortedArrayController.addObject({ id: 5, name: 'Scumbag Fucs' }); equal(sortedArrayController.get('length'), 5, 'array has 5 items'); equal(sortedArrayController.objectAt(3).name, 'Scumbag Fucs', 'a new object added to controller was inserted according to given constraint'); }); QUnit.test("addObject does not insert duplicates", function() { var sortedArrayProxy; var obj = {}; sortedArrayProxy = ArrayProxy['default'].createWithMixins(SortableMixin['default'], { content: Ember['default'].A([obj]) }); equal(sortedArrayProxy.get('length'), 1, 'array has 1 item'); sortedArrayProxy.addObject(obj); equal(sortedArrayProxy.get('length'), 1, 'array still has 1 item'); }); QUnit.test("you can change a sort property and the content will rearrange", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(0).name, 'Scumbag Bryn', 'bryn is first'); property_set.set(sortedArrayController.objectAt(0), 'name', 'Scumbag Fucs'); equal(sortedArrayController.objectAt(0).name, 'Scumbag Dale', 'dale is first now'); equal(sortedArrayController.objectAt(1).name, 'Scumbag Fucs', 'foucs is second'); }); QUnit.test("you can change the position of the middle item", function() { equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(1).name, 'Scumbag Dale', 'Dale is second'); property_set.set(sortedArrayController.objectAt(1), 'name', 'Alice'); // Change Dale to Alice equal(sortedArrayController.objectAt(0).name, 'Alice', 'Alice (previously Dale) is first now'); }); QUnit.test("don't remove and insert if position didn't change", function() { var insertItemSortedCalled = false; sortedArrayController.reopen({ insertItemSorted: function(item) { insertItemSortedCalled = true; this._super(item); } }); sortedArrayController.set('sortProperties', ['name']); property_set.set(sortedArrayController.objectAt(0), 'name', 'Scumbag Brynjolfsson'); ok(!insertItemSortedCalled, "insertItemSorted should not have been called"); }); QUnit.test("sortProperties observers removed on content removal", function() { var removedObject = unsortedArray.objectAt(2); equal(events.listenersFor(removedObject, 'name:change').length, 1, "Before removal, there should be one listener for sortProperty change."); unsortedArray.replace(2, 1, []); equal(events.listenersFor(removedObject, 'name:change').length, 0, "After removal, there should be no listeners for sortProperty change."); }); QUnit.module("Ember.Sortable with sortProperties", { setup: function() { run['default'](function() { sortedArrayController = ArrayController['default'].create({ sortProperties: ['name'] }); var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember['default'].A(Ember['default'].A(array).copy()); }); }, teardown: function() { run['default'](function() { sortedArrayController.destroy(); }); } }); QUnit.test("you can set content later and it will be sorted", function() { equal(sortedArrayController.get('length'), 0, 'array has 0 items'); run['default'](function() { sortedArrayController.set('content', unsortedArray); }); equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(0).name, 'Scumbag Bryn', 'array is sorted by name'); }); QUnit.module("Ember.Sortable with sortFunction and sortProperties", { setup: function() { run['default'](function() { sortedArrayController = ArrayController['default'].create({ sortProperties: ['name'], sortFunction: function(v, w) { var lowerV = v.toLowerCase(); var lowerW = w.toLowerCase(); if (lowerV < lowerW) { return -1; } if (lowerV > lowerW) { return 1; } return 0; } }); var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag bryn" }]; unsortedArray = Ember['default'].A(Ember['default'].A(array).copy()); }); }, teardown: function() { run['default'](function() { sortedArrayController.destroy(); }); } }); QUnit.test("you can sort with custom sorting function", function() { equal(sortedArrayController.get('length'), 0, 'array has 0 items'); run['default'](function() { sortedArrayController.set('content', unsortedArray); }); equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(0).name, 'Scumbag bryn', 'array is sorted by custom sort'); }); QUnit.test("Ember.Sortable with sortFunction on ArrayProxy should work like ArrayController", function() { run['default'](function() { sortedArrayController = ArrayProxy['default'].createWithMixins(SortableMixin['default'], { sortProperties: ['name'], sortFunction: function(v, w) { var lowerV = v.toLowerCase(); var lowerW = w.toLowerCase(); if (lowerV < lowerW) { return -1; } if (lowerV > lowerW) { return 1; } return 0; } }); var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember['default'].A(Ember['default'].A(array).copy()); }); equal(sortedArrayController.get('length'), 0, 'array has 0 items'); run['default'](function() { sortedArrayController.set('content', unsortedArray); }); equal(sortedArrayController.get('length'), 3, 'array has 3 items'); equal(sortedArrayController.objectAt(0).name, 'Scumbag Bryn', 'array is sorted by name'); }); }); enifed('ember-runtime/tests/mixins/sortable_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/sortable_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/sortable_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/sortable_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/sortable_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/sortable_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/mixins/target_action_support_test', ['ember-metal/core', 'ember-runtime/system/object', 'ember-runtime/mixins/target_action_support'], function (Ember, EmberObject, TargetActionSupport) { 'use strict'; var originalLookup; QUnit.module("TargetActionSupport", { setup: function() { originalLookup = Ember['default'].lookup; }, teardown: function() { Ember['default'].lookup = originalLookup; } }); QUnit.test("it should return false if no target or action are specified", function() { expect(1); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default']); ok(false === obj.triggerAction(), "no target or action was specified"); }); QUnit.test("it should support actions specified as strings", function() { expect(2); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: EmberObject['default'].create({ anEvent: function() { ok(true, "anEvent method was called"); } }), action: 'anEvent' }); ok(true === obj.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should invoke the send() method on objects that implement it", function() { expect(3); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: EmberObject['default'].create({ send: function(evt, context) { equal(evt, 'anEvent', "send() method was invoked with correct event name"); equal(context, obj, "send() method was invoked with correct context"); } }), action: 'anEvent' }); ok(true === obj.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should find targets specified using a property path", function() { expect(2); var Test = {}; Ember['default'].lookup = { Test: Test }; Test.targetObj = EmberObject['default'].create({ anEvent: function() { ok(true, "anEvent method was called on global object"); } }); var myObj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: 'Test.targetObj', action: 'anEvent' }); ok(true === myObj.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should use an actionContext object specified as a property on the object", function() { expect(2); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { action: 'anEvent', actionContext: {}, target: EmberObject['default'].create({ anEvent: function(ctx) { ok(obj.actionContext === ctx, "anEvent method was called with the expected context"); } }) }); ok(true === obj.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should find an actionContext specified as a property path", function() { expect(2); var Test = {}; Ember['default'].lookup = { Test: Test }; Test.aContext = {}; var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { action: 'anEvent', actionContext: 'Test.aContext', target: EmberObject['default'].create({ anEvent: function(ctx) { ok(Test.aContext === ctx, "anEvent method was called with the expected context"); } }) }); ok(true === obj.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should use the target specified in the argument", function() { expect(2); var targetObj = EmberObject['default'].create({ anEvent: function() { ok(true, "anEvent method was called"); } }); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { action: 'anEvent' }); ok(true === obj.triggerAction({ target: targetObj }), "a valid target and action were specified"); }); QUnit.test("it should use the action specified in the argument", function() { expect(2); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: EmberObject['default'].create({ anEvent: function() { ok(true, "anEvent method was called"); } }) }); ok(true === obj.triggerAction({ action: 'anEvent' }), "a valid target and action were specified"); }); QUnit.test("it should use the actionContext specified in the argument", function() { expect(2); var context = {}; var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: EmberObject['default'].create({ anEvent: function(ctx) { ok(context === ctx, "anEvent method was called with the expected context"); } }), action: 'anEvent' }); ok(true === obj.triggerAction({ actionContext: context }), "a valid target and action were specified"); }); QUnit.test("it should allow multiple arguments from actionContext", function() { expect(3); var param1 = 'someParam'; var param2 = 'someOtherParam'; var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: EmberObject['default'].create({ anEvent: function(first, second) { ok(first === param1, "anEvent method was called with the expected first argument"); ok(second === param2, "anEvent method was called with the expected second argument"); } }), action: 'anEvent' }); ok(true === obj.triggerAction({ actionContext: [param1, param2] }), "a valid target and action were specified"); }); QUnit.test("it should use a null value specified in the actionContext argument", function() { expect(2); var obj = EmberObject['default'].createWithMixins(TargetActionSupport['default'], { target: EmberObject['default'].create({ anEvent: function(ctx) { ok(null === ctx, "anEvent method was called with the expected context (null)"); } }), action: 'anEvent' }); ok(true === obj.triggerAction({ actionContext: null }), "a valid target and action were specified"); }); }); enifed('ember-runtime/tests/mixins/target_action_support_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/target_action_support_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/mixins/target_action_support_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/mixins/target_action_support_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/target_action_support_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/target_action_support_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/array', ['exports', 'ember-runtime/tests/suites/enumerable', 'ember-runtime/tests/suites/array/indexOf', 'ember-runtime/tests/suites/array/lastIndexOf', 'ember-runtime/tests/suites/array/objectAt'], function (exports, enumerable, indexOfTests, lastIndexOfTests, objectAtTests) { 'use strict'; var ObserverClass = enumerable.ObserverClass.extend({ observeArray: function(obj) { obj.addArrayObserver(this); return this; }, stopObserveArray: function(obj) { obj.removeArrayObserver(this); return this; }, arrayWillChange: function() { equal(this._before, null, 'should only call once'); this._before = Array.prototype.slice.call(arguments); }, arrayDidChange: function() { equal(this._after, null, 'should only call once'); this._after = Array.prototype.slice.call(arguments); } }); var ArrayTests = enumerable.EnumerableTests.extend({ observerClass: ObserverClass }); ArrayTests.ObserverClass = ObserverClass; ArrayTests.importModuleTests(indexOfTests['default']); ArrayTests.importModuleTests(lastIndexOfTests['default']); ArrayTests.importModuleTests(objectAtTests['default']); exports.ArrayTests = ArrayTests; exports.ObserverClass = ObserverClass; }); enifed('ember-runtime/tests/suites/array.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/array.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/array.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/array.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/array.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/array.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/array/indexOf', ['exports', 'ember-runtime/tests/suites/suite', 'ember-runtime/system/string'], function (exports, suites__suite, string) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('indexOf'); suite.test("should return index of object", function() { var expected = this.newFixture(3); var obj = this.newObject(expected); var len = 3; var idx; for (idx=0;idx=0) { observer.addBeforeObserver(obj, keys[loc], this, 'propertyWillChange'); } return this; }, /** Begins observing the passed key names on the passed object. Any changes on the named properties will be recorded. @param {Ember.Enumerable} obj The enumerable to observe. @returns {Object} receiver */ observe: function(obj) { if (obj.addObserver) { var keys = Array.prototype.slice.call(arguments, 1); var loc = keys.length; while (--loc >= 0) { obj.addObserver(keys[loc], this, 'propertyDidChange'); } } else { this.isEnabled = false; } return this; }, /** Returns true if the passed key was invoked. If you pass a value as well then validates that the values match. @param {String} key Key to validate @param {Object} value (Optional) value @returns {Boolean} */ validate: function(key, value) { if (!this.isEnabled) { return true; } if (!this._keys[key]) { return false; } if (arguments.length>1) { return this._values[key] === value; } else { return true; } }, /** Returns times the before observer as invoked. @param {String} key Key to check */ timesCalledBefore: function(key) { return this._keysBefore[key] || 0; }, /** Returns times the observer as invoked. @param {String} key Key to check */ timesCalled: function(key) { return this._keys[key] || 0; }, /** begins acting as an enumerable observer. */ observeEnumerable: function(obj) { obj.addEnumerableObserver(this); return this; }, stopObserveEnumerable: function(obj) { obj.removeEnumerableObserver(this); return this; }, enumerableWillChange: function() { equal(this._before, null, 'should only call once'); this._before = Array.prototype.slice.call(arguments); }, enumerableDidChange: function() { equal(this._after, null, 'should only call once'); this._after = Array.prototype.slice.call(arguments); } }); var EnumerableTests = suite.Suite.extend({ /** Implement to return a new enumerable object for testing. Should accept either no parameters, a single number (indicating the desired length of the collection) or an array of objects. @param {Array} content An array of items to include in the enumerable optionally. @returns {Ember.Enumerable} a new enumerable */ newObject: mixin.required(Function), /** Implement to return a set of new fixture strings that can be applied to the enumerable. This may be passed into the newObject method. @param {Number} count The number of items required. @returns {Array} array of strings */ newFixture: function(cnt) { var ret = []; while (--cnt >= 0) { ret.push(utils.generateGuid()); } return ret; }, /** Implement to return a set of new fixture objects that can be applied to the enumerable. This may be passed into the newObject method. @param {Number} cnt The number of items required. @returns {Array} array of objects */ newObjectsFixture: function(cnt) { var ret = []; var item; while (--cnt >= 0) { item = {}; utils.guidFor(item); ret.push(item); } return ret; }, /** Implement accept an instance of the enumerable and return an array containing the objects in the enumerable. This is used only for testing so performance is not important. @param {Ember.Enumerable} enumerable The enumerable to convert. @returns {Array} array of items */ toArray: mixin.required(Function), /** Implement this method if your object can mutate internally (even if it does not support the MutableEnumerable API). The method should accept an object of your desired type and modify it somehow. Suite tests will use this to ensure that all appropriate caches, etc. clear when the mutation occurs. If you do not define this optional method, then mutation-related tests will be skipped. @param {Ember.Enumerable} enumerable The enumerable to mutate @returns {void} */ mutate: function() {}, /** Becomes true when you define a new mutate() method, indicating that mutation tests should run. This is calculated automatically. @type Boolean */ canTestMutation: computed.computed(function() { return this.mutate !== EnumerableTests.prototype.mutate; }), /** Invoked to actually run the test - overridden by mixins */ run: function() {}, /** Creates a new observer object for testing. You can add this object as an observer on an array and it will record results anytime it is invoked. After running the test, call the validate() method on the observer to validate the results. */ newObserver: function(obj) { var ret = property_get.get(this, 'observerClass').create(); if (arguments.length>0) { ret.observeBefore.apply(ret, arguments); } if (arguments.length>0) { ret.observe.apply(ret, arguments); } return ret; }, observerClass: ObserverClass }); EnumerableTests.importModuleTests(anyTests['default']); EnumerableTests.importModuleTests(isAnyTests['default']); EnumerableTests.importModuleTests(compactTests['default']); EnumerableTests.importModuleTests(containsTests['default']); EnumerableTests.importModuleTests(everyTests['default']); EnumerableTests.importModuleTests(filterTests['default']); EnumerableTests.importModuleTests(findTests['default']); EnumerableTests.importModuleTests(firstObjectTests['default']); EnumerableTests.importModuleTests(forEachTests['default']); EnumerableTests.importModuleTests(mapByTests['default']); EnumerableTests.importModuleTests(invokeTests['default']); EnumerableTests.importModuleTests(lastObjectTests['default']); EnumerableTests.importModuleTests(mapTests['default']); EnumerableTests.importModuleTests(reduceTests['default']); EnumerableTests.importModuleTests(rejectTests['default']); EnumerableTests.importModuleTests(sortByTests['default']); EnumerableTests.importModuleTests(toArrayTests['default']); EnumerableTests.importModuleTests(uniqTests['default']); EnumerableTests.importModuleTests(withoutTests['default']); exports['default'] = EnumerableTests; exports.EnumerableTests = EnumerableTests; exports.ObserverClass = ObserverClass; }); enifed('ember-runtime/tests/suites/enumerable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/enumerable.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/enumerable.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/any', ['exports', 'ember-metal/core', 'ember-runtime/tests/suites/suite'], function (exports, Ember, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); // .......................................................... // any() // suite.module('any'); suite.test('any should should invoke callback on each item as long as you return false', function() { var obj = this.newObject(); var ary = this.toArray(obj); var found = []; var result; result = obj.any(function(i) { found.push(i); return false; }); equal(result, false, 'return value of obj.any'); deepEqual(found, ary, 'items passed during any() should match'); }); suite.test('any should stop invoking when you return true', function() { var obj = this.newObject(); var ary = this.toArray(obj); var cnt = ary.length - 2; var exp = cnt; var found = []; var result; result = obj.any(function(i) { found.push(i); return --cnt <= 0; }); equal(result, true, 'return value of obj.any'); equal(found.length, exp, 'should invoke proper number of times'); deepEqual(found, ary.slice(0, -2), 'items passed during any() should match'); }); suite.test('any should return true if any object matches the callback', function() { var obj = Ember['default'].A([0, 1, 2]); var result; result = obj.any(function(i) { return !!i; }); equal(result, true, 'return value of obj.any'); }); suite.test('any should return false if no object matches the callback', function() { var obj = Ember['default'].A([0, null, false]); var result; result = obj.any(function(i) { return !!i; }); equal(result, false, 'return value of obj.any'); }); suite.test('any should produce correct results even if the matching element is undefined', function() { var obj = Ember['default'].A([undefined]); var result; result = obj.any(function(i) { return true; }); equal(result, true, 'return value of obj.any'); }); suite.test('any should be aliased to some', function() { var obj = this.newObject(); var ary = this.toArray(obj); var anyFound = []; var someFound = []; var cnt = ary.length - 2; var anyResult, someResult; anyResult = obj.any(function(i) { anyFound.push(i); return false; }); someResult = obj.some(function(i) { someFound.push(i); return false; }); equal(someResult, anyResult); anyFound = []; someFound = []; cnt = ary.length - 2; anyResult = obj.any(function(i) { anyFound.push(i); return --cnt <= 0; }); cnt = ary.length - 2; someResult = obj.some(function(i) { someFound.push(i); return --cnt <= 0; }); equal(someResult, anyResult); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/any.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/any.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/any.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/any.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/any.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/any.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/compact', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('compact'); suite.test('removes null and undefined values from enumerable', function() { var obj = this.newObject([null, 1, false, '', undefined, 0, null]); var ary = obj.compact(); deepEqual(ary, [1, false, '', 0]); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/compact.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/compact.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/compact.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/compact.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/compact.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/compact.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/contains', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('contains'); suite.test('contains returns true if items is in enumerable', function() { var data = this.newFixture(3); var obj = this.newObject(data); equal(obj.contains(data[1]), true, 'should return true if contained'); }); suite.test('contains returns false if item is not in enumerable', function() { var data = this.newFixture(1); var obj = this.newObject(this.newFixture(3)); equal(obj.contains(data[0]), false, 'should return false if not contained'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/contains.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/contains.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/contains.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/contains.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/contains.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/contains.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/every', ['exports', 'ember-runtime/system/object', 'ember-runtime/tests/suites/suite'], function (exports, EmberObject, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); // .......................................................... // every() // suite.module('every'); suite.test('every should should invoke callback on each item as long as you return true', function() { var obj = this.newObject(); var ary = this.toArray(obj); var found = []; var result; result = obj.every(function(i) { found.push(i); return true; }); equal(result, true, 'return value of obj.every'); deepEqual(found, ary, 'items passed during every() should match'); }); suite.test('every should stop invoking when you return false', function() { var obj = this.newObject(); var ary = this.toArray(obj); var cnt = ary.length - 2; var exp = cnt; var found = []; var result; result = obj.every(function(i) { found.push(i); return --cnt>0; }); equal(result, false, 'return value of obj.every'); equal(found.length, exp, 'should invoke proper number of times'); deepEqual(found, ary.slice(0, -2), 'items passed during every() should match'); }); // .......................................................... // isEvery() // suite.module('isEvery'); suite.test('should return true of every property matches', function() { var obj = this.newObject([ { foo: 'foo', bar: 'BAZ' }, EmberObject['default'].create({ foo: 'foo', bar: 'bar' }) ]); equal(obj.isEvery('foo', 'foo'), true, 'isEvery(foo)'); equal(obj.isEvery('bar', 'bar'), false, 'isEvery(bar)'); }); suite.test('should return true of every property is true', function() { var obj = this.newObject([ { foo: 'foo', bar: true }, EmberObject['default'].create({ foo: 'bar', bar: false }) ]); // different values - all eval to true equal(obj.isEvery('foo'), true, 'isEvery(foo)'); equal(obj.isEvery('bar'), false, 'isEvery(bar)'); }); suite.test('should return true if every property matches null', function() { var obj = this.newObject([ { foo: null, bar: 'BAZ' }, EmberObject['default'].create({ foo: null, bar: null }) ]); equal(obj.isEvery('foo', null), true, "isEvery('foo', null)"); equal(obj.isEvery('bar', null), false, "isEvery('bar', null)"); }); suite.test('everyBy should be aliased to isEvery', function() { var obj = this.newObject(); equal(obj.isEvery, obj.everyBy); }); suite.test('everyProperty should be aliased to isEvery', function() { var obj = this.newObject(); equal(obj.isEvery, obj.everyProperty); }); suite.test('should return true if every property is undefined', function() { var obj = this.newObject([ { foo: undefined, bar: 'BAZ' }, EmberObject['default'].create({ bar: undefined }) ]); equal(obj.isEvery('foo', undefined), true, "isEvery('foo', undefined)"); equal(obj.isEvery('bar', undefined), false, "isEvery('bar', undefined)"); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/every.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/every.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/every.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/every.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/every.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/every.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/filter', ['exports', 'ember-runtime/system/object', 'ember-runtime/tests/suites/suite'], function (exports, EmberObject, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); // .......................................................... // filter() // suite.module('filter'); suite.test('filter should invoke on each item', function() { var obj = this.newObject(); var ary = this.toArray(obj); var cnt = ary.length - 2; var found = []; var result; // return true on all but the last two result = obj.filter(function(i) { found.push(i); return --cnt>=0; }); deepEqual(found, ary, 'should have invoked on each item'); deepEqual(result, ary.slice(0, -2), 'filtered array should exclude items'); }); // .......................................................... // filterBy() // suite.module('filterBy'); suite.test('should filter based on object', function() { var obj, ary; ary = [ { foo: 'foo', bar: 'BAZ' }, EmberObject['default'].create({ foo: 'foo', bar: 'bar' }) ]; obj = this.newObject(ary); deepEqual(obj.filterBy('foo', 'foo'), ary, 'filterBy(foo)'); deepEqual(obj.filterBy('bar', 'bar'), [ary[1]], 'filterBy(bar)'); }); suite.test('should include in result if property is true', function() { var obj, ary; ary = [ { foo: 'foo', bar: true }, EmberObject['default'].create({ foo: 'bar', bar: false }) ]; obj = this.newObject(ary); // different values - all eval to true deepEqual(obj.filterBy('foo'), ary, 'filterBy(foo)'); deepEqual(obj.filterBy('bar'), [ary[0]], 'filterBy(bar)'); }); suite.test('should filter on second argument if provided', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 2 }), { name: 'obj3', foo: 2 }, EmberObject['default'].create({ name: 'obj4', foo: 3 }) ]; obj = this.newObject(ary); deepEqual(obj.filterBy('foo', 3), [ary[0], ary[3]], "filterBy('foo', 3)')"); }); suite.test('should correctly filter null second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: null }), { name: 'obj3', foo: null }, EmberObject['default'].create({ name: 'obj4', foo: 3 }) ]; obj = this.newObject(ary); deepEqual(obj.filterBy('foo', null), [ary[1], ary[2]], "filterBy('foo', 3)')"); }); suite.test('should not return all objects on undefined second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 2 }) ]; obj = this.newObject(ary); deepEqual(obj.filterBy('foo', undefined), [], "filterBy('foo', 3)')"); }); suite.test('should correctly filter explicit undefined second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 3 }), { name: 'obj3', foo: undefined }, EmberObject['default'].create({ name: 'obj4', foo: undefined }), { name: 'obj5' }, EmberObject['default'].create({ name: 'obj6' }) ]; obj = this.newObject(ary); deepEqual(obj.filterBy('foo', undefined), ary.slice(2), "filterBy('foo', 3)')"); }); suite.test('should not match undefined properties without second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 3 }), { name: 'obj3', foo: undefined }, EmberObject['default'].create({ name: 'obj4', foo: undefined }), { name: 'obj5' }, EmberObject['default'].create({ name: 'obj6' }) ]; obj = this.newObject(ary); deepEqual(obj.filterBy('foo'), ary.slice(0, 2), "filterBy('foo', 3)')"); }); suite.test('should be aliased to filterProperty', function() { var ary = []; equal(ary.filterProperty, ary.filterBy); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/filter.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/filter.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/filter.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/filter.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/filter.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/filter.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/find', ['exports', 'ember-runtime/system/object', 'ember-runtime/tests/suites/suite'], function (exports, EmberObject, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); // .......................................................... // find() // suite.module('find'); suite.test('find should invoke callback on each item as long as you return false', function() { var obj = this.newObject(); var ary = this.toArray(obj); var found = []; var result; result = obj.find(function(i) { found.push(i); return false; }); equal(result, undefined, 'return value of obj.find'); deepEqual(found, ary, 'items passed during find() should match'); }); suite.test('every should stop invoking when you return true', function() { var obj = this.newObject(); var ary = this.toArray(obj); var cnt = ary.length - 2; var exp = cnt; var found = []; var result; result = obj.find(function(i) { found.push(i); return --cnt >= 0; }); equal(result, ary[exp-1], 'return value of obj.find'); equal(found.length, exp, 'should invoke proper number of times'); deepEqual(found, ary.slice(0, -2), 'items passed during find() should match'); }); // .......................................................... // findBy() // suite.module('findBy'); suite.test('should return first object of property matches', function() { var ary, obj; ary = [ { foo: 'foo', bar: 'BAZ' }, EmberObject['default'].create({ foo: 'foo', bar: 'bar' }) ]; obj = this.newObject(ary); equal(obj.findBy('foo', 'foo'), ary[0], 'findBy(foo)'); equal(obj.findBy('bar', 'bar'), ary[1], 'findBy(bar)'); }); suite.test('should return first object with truthy prop', function() { var ary, obj; ary = [ { foo: 'foo', bar: false }, EmberObject['default'].create({ foo: 'bar', bar: true }) ]; obj = this.newObject(ary); // different values - all eval to true equal(obj.findBy('foo'), ary[0], 'findBy(foo)'); equal(obj.findBy('bar'), ary[1], 'findBy(bar)'); }); suite.test('should return first null property match', function() { var ary, obj; ary = [ { foo: null, bar: 'BAZ' }, EmberObject['default'].create({ foo: null, bar: null }) ]; obj = this.newObject(ary); equal(obj.findBy('foo', null), ary[0], "findBy('foo', null)"); equal(obj.findBy('bar', null), ary[1], "findBy('bar', null)"); }); suite.test('should return first undefined property match', function() { var ary, obj; ary = [ { foo: undefined, bar: 'BAZ' }, EmberObject['default'].create({ }) ]; obj = this.newObject(ary); equal(obj.findBy('foo', undefined), ary[0], "findBy('foo', undefined)"); equal(obj.findBy('bar', undefined), ary[1], "findBy('bar', undefined)"); }); suite.test('should be aliased to findProperty', function() { var obj; obj = this.newObject([]); equal(obj.findProperty, obj.findBy); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/find.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/find.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/find.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/find.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/find.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/find.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/firstObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('firstObject'); suite.test('returns first item in enumerable', function() { var obj = this.newObject(); equal(property_get.get(obj, 'firstObject'), this.toArray(obj)[0]); }); suite.test('returns undefined if enumerable is empty', function() { var obj = this.newObject([]); equal(property_get.get(obj, 'firstObject'), undefined); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/firstObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/firstObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/firstObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/firstObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/firstObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/firstObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/forEach', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get', 'ember-metal/utils'], function (exports, suites__suite, property_get, utils) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('forEach'); suite.test('forEach should iterate over list', function() { var obj = this.newObject(); var ary = this.toArray(obj); var found = []; obj.forEach(function(i) { found.push(i); }); deepEqual(found, ary, 'items passed during forEach should match'); }); suite.test('forEach should iterate over list after mutation', function() { if (property_get.get(this, 'canTestMutation')) { expect(0); return; } var obj = this.newObject(); var ary = this.toArray(obj); var found = []; obj.forEach(function(i) { found.push(i); }); deepEqual(found, ary, 'items passed during forEach should match'); this.mutate(obj); ary = this.toArray(obj); found = []; obj.forEach(function(i) { found.push(i); }); deepEqual(found, ary, 'items passed during forEach should match'); }); suite.test('2nd target parameter', function() { var obj = this.newObject(); var target = this; obj.forEach(function() { // ES6TODO: When transpiled we will end up with "use strict" which disables automatically binding to the global context. // Therefore, the following test can never pass in strict mode unless we modify the `map` function implementation to // use `Ember.lookup` if target is not specified. // // equal(guidFor(this), guidFor(global), 'should pass the global object as this if no context'); }); obj.forEach(function() { equal(utils.guidFor(this), utils.guidFor(target), 'should pass target as this if context'); }, target); }); suite.test('callback params', function() { var obj = this.newObject(); var ary = this.toArray(obj); var loc = 0; obj.forEach(function(item, idx, enumerable) { equal(item, ary[loc], 'item param'); equal(idx, loc, 'idx param'); equal(utils.guidFor(enumerable), utils.guidFor(obj), 'enumerable param'); loc++; }); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/forEach.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/forEach.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/forEach.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/forEach.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/forEach.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/forEach.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/invoke', ['exports', 'ember-runtime/system/object', 'ember-runtime/tests/suites/suite'], function (exports, EmberObject, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('invoke'); suite.test('invoke should call on each object that implements', function() { var cnt, ary, obj; function F(amt) { cnt += amt===undefined ? 1 : amt; } cnt = 0; ary = [ { foo: F }, EmberObject['default'].create({ foo: F }), // NOTE: does not impl foo - invoke should just skip EmberObject['default'].create({ bar: F }), { foo: F } ]; obj = this.newObject(ary); obj.invoke('foo'); equal(cnt, 3, 'should have invoked 3 times'); cnt = 0; obj.invoke('foo', 2); equal(cnt, 6, 'should have invoked 3 times, passing param'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/invoke.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/invoke.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/invoke.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/invoke.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/invoke.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/invoke.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/is_any', ['exports', 'ember-runtime/system/object', 'ember-runtime/tests/suites/suite'], function (exports, EmberObject, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); // .......................................................... // isAny() // suite.module('isAny'); suite.test('should return true of any property matches', function() { var obj = this.newObject([ { foo: 'foo', bar: 'BAZ' }, EmberObject['default'].create({ foo: 'foo', bar: 'bar' }) ]); equal(obj.isAny('foo', 'foo'), true, 'isAny(foo)'); equal(obj.isAny('bar', 'bar'), true, 'isAny(bar)'); equal(obj.isAny('bar', 'BIFF'), false, 'isAny(BIFF)'); }); suite.test('should return true of any property is true', function() { var obj = this.newObject([ { foo: 'foo', bar: true }, EmberObject['default'].create({ foo: 'bar', bar: false }) ]); // different values - all eval to true equal(obj.isAny('foo'), true, 'isAny(foo)'); equal(obj.isAny('bar'), true, 'isAny(bar)'); equal(obj.isAny('BIFF'), false, 'isAny(biff)'); }); suite.test('should return true if any property matches null', function() { var obj = this.newObject([ { foo: null, bar: 'bar' }, EmberObject['default'].create({ foo: 'foo', bar: null }) ]); equal(obj.isAny('foo', null), true, "isAny('foo', null)"); equal(obj.isAny('bar', null), true, "isAny('bar', null)"); }); suite.test('should return true if any property is undefined', function() { var obj = this.newObject([ { foo: undefined, bar: 'bar' }, EmberObject['default'].create({ foo: 'foo' }) ]); equal(obj.isAny('foo', undefined), true, "isAny('foo', undefined)"); equal(obj.isAny('bar', undefined), true, "isAny('bar', undefined)"); }); suite.test('should not match undefined properties without second argument', function() { var obj = this.newObject([ { foo: undefined }, EmberObject['default'].create({ }) ]); equal(obj.isAny('foo'), false, "isAny('foo', undefined)"); }); suite.test('anyBy should be aliased to isAny', function() { var obj = this.newObject(); equal(obj.isAny, obj.anyBy); }); suite.test('isAny should be aliased to someProperty', function() { var obj = this.newObject(); equal(obj.someProperty, obj.isAny); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/is_any.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/is_any.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/is_any.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/is_any.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/is_any.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/is_any.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/lastObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('lastObject'); suite.test('returns last item in enumerable', function() { var obj = this.newObject(); var ary = this.toArray(obj); equal(property_get.get(obj, 'lastObject'), ary[ary.length-1]); }); suite.test('returns undefined if enumerable is empty', function() { var obj = this.newObject([]); equal(property_get.get(obj, 'lastObject'), undefined); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/lastObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/lastObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/lastObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/lastObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/lastObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/lastObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/map', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/enumerable_utils', 'ember-metal/property_get', 'ember-metal/utils'], function (exports, suites__suite, EnumerableUtils, property_get, utils) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('map'); function mapFunc(item) { return item ? item.toString() : null; } suite.test('map should iterate over list', function() { var obj = this.newObject(); var ary = EnumerableUtils['default'].map(this.toArray(obj), mapFunc); var found = []; found = obj.map(mapFunc); deepEqual(found, ary, 'mapped arrays should match'); }); suite.test('map should iterate over list after mutation', function() { if (property_get.get(this, 'canTestMutation')) { expect(0); return; } var obj = this.newObject(); var ary = this.toArray(obj).map(mapFunc); var found; found = obj.map(mapFunc); deepEqual(found, ary, 'items passed during forEach should match'); this.mutate(obj); ary = this.toArray(obj).map(mapFunc); found = obj.map(mapFunc); deepEqual(found, ary, 'items passed during forEach should match'); }); suite.test('2nd target parameter', function() { var obj = this.newObject(); var target = this; obj.map(function() { // ES6TODO: When transpiled we will end up with "use strict" which disables automatically binding to the global context. // Therefore, the following test can never pass in strict mode unless we modify the `map` function implementation to // use `Ember.lookup` if target is not specified. // // equal(guidFor(this), guidFor(global), 'should pass the global object as this if no context'); }); obj.map(function() { equal(utils.guidFor(this), utils.guidFor(target), 'should pass target as this if context'); }, target); }); suite.test('callback params', function() { var obj = this.newObject(); var ary = this.toArray(obj); var loc = 0; obj.map(function(item, idx, enumerable) { equal(item, ary[loc], 'item param'); equal(idx, loc, 'idx param'); equal(utils.guidFor(enumerable), utils.guidFor(obj), 'enumerable param'); loc++; }); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/map.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/map.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/map.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/map.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/map.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/map.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/mapBy', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('mapBy'); suite.test('get value of each property', function() { var obj = this.newObject([{ a: 1 },{ a: 2 }]); equal(obj.mapBy('a').join(''), '12'); }); suite.test('should work also through getEach alias', function() { var obj = this.newObject([{ a: 1 },{ a: 2 }]); equal(obj.getEach('a').join(''), '12'); }); suite.test('should be aliased to mapProperty', function() { var obj = this.newObject([]); equal(obj.mapProperty, obj.mapBy); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/mapBy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/mapBy.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/mapBy.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/mapBy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/mapBy.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/mapBy.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/reduce', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('reduce'); suite.test('collects a summary value from an enumeration', function() { var obj = this.newObject([1, 2, 3]); var res = obj.reduce(function(previousValue, item, index, enumerable) { return previousValue + item; }, 0); equal(res, 6); }); suite.test('passes index of item to callback', function() { var obj = this.newObject([1, 2, 3]); var res = obj.reduce(function(previousValue, item, index, enumerable) { return previousValue + index; }, 0); equal(res, 3); }); suite.test('passes enumerable object to callback', function() { var obj = this.newObject([1, 2, 3]); var res = obj.reduce(function(previousValue, item, index, enumerable) { return enumerable; }, 0); equal(res, obj); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/reduce.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/reduce.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/reduce.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/reduce.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/reduce.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/reduce.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/reject', ['exports', 'ember-runtime/system/object', 'ember-runtime/tests/suites/suite'], function (exports, EmberObject, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); // .......................................................... // reject() // suite.module('reject'); suite.test('should reject any item that does not meet the condition', function() { var obj = this.newObject([1,2,3,4]); var result; result = obj.reject(function(i) { return i < 3; }); deepEqual(result, [3,4], 'reject the correct items'); }); suite.test('should be the inverse of filter', function() { var obj = this.newObject([1,2,3,4]); var isEven = function(i) { return i % 2 === 0; }; var filtered, rejected; filtered = obj.filter(isEven); rejected = obj.reject(isEven); deepEqual(filtered, [2,4], 'filtered evens'); deepEqual(rejected, [1,3], 'rejected evens'); }); // .......................................................... // rejectBy() // suite.module('rejectBy'); suite.test('should reject based on object', function() { var obj, ary; ary = [ { foo: 'foo', bar: 'BAZ' }, EmberObject['default'].create({ foo: 'foo', bar: 'bar' }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('foo', 'foo'), [], 'rejectBy(foo)'); deepEqual(obj.rejectBy('bar', 'bar'), [ary[0]], 'rejectBy(bar)'); }); suite.test('should include in result if property is false', function() { var obj, ary; ary = [ { foo: false, bar: true }, EmberObject['default'].create({ foo: false, bar: false }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('foo'), ary, 'rejectBy(foo)'); deepEqual(obj.rejectBy('bar'), [ary[1]], 'rejectBy(bar)'); }); suite.test('should reject on second argument if provided', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 2 }), { name: 'obj3', foo: 2 }, EmberObject['default'].create({ name: 'obj4', foo: 3 }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('foo', 3), [ary[1], ary[2]], "rejectBy('foo', 3)')"); }); suite.test('should correctly reject null second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: null }), { name: 'obj3', foo: null }, EmberObject['default'].create({ name: 'obj4', foo: 3 }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('foo', null), [ary[0], ary[3]], "rejectBy('foo', null)')"); }); suite.test('should correctly reject undefined second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 2 }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('bar', undefined), [], "rejectBy('bar', undefined)')"); }); suite.test('should correctly reject explicit undefined second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 3 }), { name: 'obj3', foo: undefined }, EmberObject['default'].create({ name: 'obj4', foo: undefined }), { name: 'obj5' }, EmberObject['default'].create({ name: 'obj6' }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('foo', undefined), ary.slice(0, 2), "rejectBy('foo', undefined)')"); }); suite.test('should match undefined, null, or false properties without second argument', function() { var obj, ary; ary = [ { name: 'obj1', foo: 3 }, EmberObject['default'].create({ name: 'obj2', foo: 3 }), { name: 'obj3', foo: undefined }, EmberObject['default'].create({ name: 'obj4', foo: undefined }), { name: 'obj5' }, EmberObject['default'].create({ name: 'obj6' }), { name: 'obj7', foo: null }, EmberObject['default'].create({ name: 'obj8', foo: null }), { name: 'obj9', foo: false }, EmberObject['default'].create({ name: 'obj10', foo: false }) ]; obj = this.newObject(ary); deepEqual(obj.rejectBy('foo'), ary.slice(2), "rejectBy('foo')')"); }); suite.test('should be aliased to rejectProperty', function() { var ary =[]; equal(ary.rejectProperty, ary.rejectBy); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/reject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/reject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/reject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/reject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/reject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/reject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/sortBy', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('sortBy'); suite.test('sort by value of property', function() { var obj = this.newObject([{ a: 2 },{ a: 1 }]); var sorted = obj.sortBy('a'); equal(property_get.get(sorted[0], 'a'), 1); equal(property_get.get(sorted[1], 'a'), 2); }); suite.test('supports multiple propertyNames', function() { var obj = this.newObject([{ a: 1, b: 2 }, { a: 1, b: 1 }]); var sorted = obj.sortBy('a', 'b'); equal(property_get.get(sorted[0], 'b'), 1); equal(property_get.get(sorted[1], 'b'), 2); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/sortBy.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/sortBy.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/sortBy.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/sortBy.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/sortBy.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/sortBy.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/toArray', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('toArray'); suite.test('toArray should convert to an array', function() { var obj = this.newObject(); deepEqual(obj.toArray(), this.toArray(obj)); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/toArray.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/toArray.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/toArray.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/toArray.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/toArray.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/toArray.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/uniq', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('uniq'); suite.test('should return new instance with duplicates removed', function() { var before, after, obj, ret; after = this.newFixture(3); before = [after[0], after[1], after[2], after[1], after[0]]; obj = this.newObject(before); before = obj.toArray(); // in case of set before will be different... ret = obj.uniq(); deepEqual(this.toArray(ret), after, 'should have removed item'); deepEqual(this.toArray(obj), before, 'should not have changed original'); }); suite.test('should return duplicate of same content if no duplicates found', function() { var item, obj, ret; obj = this.newObject(this.newFixture(3)); ret = obj.uniq(item); ok(ret !== obj, 'should not be same object'); deepEqual(this.toArray(ret), this.toArray(obj), 'should be the same content'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/uniq.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/uniq.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/uniq.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/uniq.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/uniq.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/uniq.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/enumerable/without', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('without'); suite.test('should return new instance with item removed', function() { var before, after, obj, ret; before = this.newFixture(3); after = [before[0], before[2]]; obj = this.newObject(before); ret = obj.without(before[1]); deepEqual(this.toArray(ret), after, 'should have removed item'); deepEqual(this.toArray(obj), before, 'should not have changed original'); }); suite.test('should return same instance if object not found', function() { var item, obj, ret; item = this.newFixture(1)[0]; obj = this.newObject(this.newFixture(3)); ret = obj.without(item); equal(ret, obj, 'should be same instance'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/enumerable/without.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/without.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/enumerable/without.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/enumerable/without.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/enumerable'); test('ember-runtime/tests/suites/enumerable/without.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/enumerable/without.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array', ['exports', 'ember-runtime/tests/suites/array', 'ember-runtime/tests/suites/mutable_array/insertAt', 'ember-runtime/tests/suites/mutable_array/popObject', 'ember-runtime/tests/suites/mutable_array/pushObject', 'ember-runtime/tests/suites/mutable_array/pushObjects', 'ember-runtime/tests/suites/mutable_array/removeAt', 'ember-runtime/tests/suites/mutable_array/replace', 'ember-runtime/tests/suites/mutable_array/shiftObject', 'ember-runtime/tests/suites/mutable_array/unshiftObject', 'ember-runtime/tests/suites/mutable_array/reverseObjects'], function (exports, array, insertAtTests, popObjectTests, pushObjectTests, pushObjectsTest, removeAtTests, replaceTests, shiftObjectTests, unshiftObjectTests, reverseObjectsTests) { 'use strict'; var MutableArrayTests = array.ArrayTests.extend(); MutableArrayTests.importModuleTests(insertAtTests['default']); MutableArrayTests.importModuleTests(popObjectTests['default']); MutableArrayTests.importModuleTests(pushObjectTests['default']); MutableArrayTests.importModuleTests(pushObjectsTest['default']); MutableArrayTests.importModuleTests(removeAtTests['default']); MutableArrayTests.importModuleTests(replaceTests['default']); MutableArrayTests.importModuleTests(shiftObjectTests['default']); MutableArrayTests.importModuleTests(unshiftObjectTests['default']); MutableArrayTests.importModuleTests(reverseObjectsTests['default']); exports['default'] = MutableArrayTests; }); enifed('ember-runtime/tests/suites/mutable_array.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/mutable_array.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/mutable_array.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/addObject', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('addObject'); suite.test("should return receiver", function() { var before, obj; before = this.newFixture(3); obj = this.newObject(before); equal(obj.addObject(before[1]), obj, 'should return receiver'); }); suite.test("[A,B].addObject(C) => [A,B,C] + notify", function() { var obj, before, after, observer, item; before = this.newFixture(2); item = this.newFixture(1)[0]; after = [before[0], before[1], item]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.addObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); } }); suite.test("[A,B,C].addObject(A) => [A,B,C] + NO notify", function() { var obj, before, after, observer, item; before = this.newFixture(3); after = before; item = before[0]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.addObject(item); // note: item in set deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.validate('[]'), false, 'should NOT have notified []'); equal(observer.validate('@each'), false, 'should NOT have notified @each'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); } }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/addObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/addObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/addObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/addObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/addObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/addObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/clear', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('clear'); suite.test("[].clear() => [] + notify", function () { var obj, before, after, observer; before = []; after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.clear(), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); equal(observer.validate('[]'), false, 'should NOT have notified [] once'); equal(observer.validate('@each'), false, 'should NOT have notified @each once'); equal(observer.validate('length'), false, 'should NOT have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); suite.test("[X].clear() => [] + notify", function () { var obj, before, after, observer; before = this.newFixture(1); after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.clear(), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/clear.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/clear.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/clear.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/clear.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/clear.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/clear.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/insertAt', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('insertAt'); suite.test("[].insertAt(0, X) => [X] + notify", function() { var obj, after, observer; after = this.newFixture(1); obj = this.newObject([]); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.insertAt(0, after[0]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalledBefore('[]'), 1, 'should have notified [] will change once'); equal(observer.timesCalledBefore('@each'), 1, 'should have notified @each will change once'); equal(observer.timesCalledBefore('length'), 1, 'should have notified length will change once'); equal(observer.timesCalledBefore('firstObject'), 1, 'should have notified firstObject will change once'); equal(observer.timesCalledBefore('lastObject'), 1, 'should have notified lastObject will change once'); equal(observer.timesCalled('[]'), 1, 'should have notified [] did change once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each did change once'); equal(observer.timesCalled('length'), 1, 'should have notified length did change once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject did change once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject did change once'); }); suite.test("[].insertAt(200,X) => OUT_OF_RANGE_EXCEPTION exception", function() { var obj = this.newObject([]); var that = this; throws(function() { obj.insertAt(200, that.newFixture(1)[0]); }, Error); }); suite.test("[A].insertAt(0, X) => [X,A] + notify", function() { var obj, item, after, before, observer; item = this.newFixture(1)[0]; before = this.newFixture(1); after = [item, before[0]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.insertAt(0, item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalledBefore('[]'), 1, 'should have notified [] will change once'); equal(observer.timesCalledBefore('@each'), 1, 'should have notified @each will change once'); equal(observer.timesCalledBefore('length'), 1, 'should have notified length will change once'); equal(observer.timesCalledBefore('firstObject'), 1, 'should have notified firstObject will change once'); equal(observer.timesCalledBefore('lastObject'), 0, 'should NOT have notified lastObject will change once'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[A].insertAt(1, X) => [A,X] + notify", function() { var obj, item, after, before, observer; item = this.newFixture(1)[0]; before = this.newFixture(1); after = [before[0], item]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.insertAt(1, item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalledBefore('[]'), 1, 'should have notified [] will change once'); equal(observer.timesCalledBefore('@each'), 1, 'should have notified @each will change once'); equal(observer.timesCalledBefore('length'), 1, 'should have notified length will change once'); equal(observer.timesCalledBefore('firstObject'), 0, 'should NOT have notified firstObject will change once'); equal(observer.timesCalledBefore('lastObject'), 1, 'should have notified lastObject will change once'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); }); suite.test("[A].insertAt(200,X) => OUT_OF_RANGE exception", function() { var obj = this.newObject(this.newFixture(1)); var that = this; throws(function() { obj.insertAt(200, that.newFixture(1)[0]); }, Error); }); suite.test("[A,B,C].insertAt(0,X) => [X,A,B,C] + notify", function() { var obj, item, after, before, observer; item = this.newFixture(1)[0]; before = this.newFixture(3); after = [item, before[0], before[1], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.insertAt(0, item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalledBefore('[]'), 1, 'should have notified [] will change once'); equal(observer.timesCalledBefore('@each'), 1, 'should have notified @each will change once'); equal(observer.timesCalledBefore('length'), 1, 'should have notified length will change once'); equal(observer.timesCalledBefore('firstObject'), 1, 'should have notified firstObject will change once'); equal(observer.timesCalledBefore('lastObject'), 0, 'should NOT have notified lastObject will change once'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[A,B,C].insertAt(1,X) => [A,X,B,C] + notify", function() { var obj, item, after, before, observer; item = this.newFixture(1)[0]; before = this.newFixture(3); after = [before[0], item, before[1], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.insertAt(1, item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalledBefore('[]'), 1, 'should have notified [] will change once'); equal(observer.timesCalledBefore('@each'), 1, 'should have notified @each will change once'); equal(observer.timesCalledBefore('length'), 1, 'should have notified length will change once'); equal(observer.timesCalledBefore('firstObject'), 0, 'should NOT have notified firstObject will change once'); equal(observer.timesCalledBefore('lastObject'), 0, 'should NOT have notified lastObject will change once'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[A,B,C].insertAt(3,X) => [A,B,C,X] + notify", function() { var obj, item, after, before, observer; item = this.newFixture(1)[0]; before = this.newFixture(3); after = [before[0], before[1], before[2], item]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.insertAt(3, item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalledBefore('[]'), 1, 'should have notified [] will change once'); equal(observer.timesCalledBefore('@each'), 1, 'should have notified @each will change once'); equal(observer.timesCalledBefore('length'), 1, 'should have notified length will change once'); equal(observer.timesCalledBefore('firstObject'), 0, 'should NOT have notified firstObject will change once'); equal(observer.timesCalledBefore('lastObject'), 1, 'should have notified lastObject will change once'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/insertAt.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/insertAt.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/insertAt.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/insertAt.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/insertAt.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/insertAt.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/popObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('popObject'); suite.test("[].popObject() => [] + returns undefined + NO notify", function() { var obj, observer; obj = this.newObject([]); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.popObject(), undefined, 'popObject results'); deepEqual(this.toArray(obj), [], 'post item results'); equal(observer.validate('[]'), false, 'should NOT have notified []'); equal(observer.validate('@each'), false, 'should NOT have notified @each'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[X].popObject() => [] + notify", function() { var obj, before, after, observer, ret; before = this.newFixture(1); after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ ret = obj.popObject(); equal(ret, before[0], 'return object'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C].popObject() => [A,B] + notify", function() { var obj, before, after, observer, ret; before = this.newFixture(3); after = [before[0], before[1]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ ret = obj.popObject(); equal(ret, before[2], 'return object'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/popObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/popObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/popObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/popObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/popObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/popObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/pushObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('pushObject'); suite.test("returns pushed object", function() { var exp = this.newFixture(1)[0]; var obj = this.newObject([]); equal(obj.pushObject(exp), exp, 'should return pushed object'); }); suite.test("[].pushObject(X) => [X] + notify", function() { var obj, before, after, observer; before = []; after = this.newFixture(1); obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.pushObject(after[0]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C].pushObject(X) => [A,B,C,X] + notify", function() { var obj, before, after, item, observer; before = this.newFixture(3); item = this.newFixture(1)[0]; after = [before[0], before[1], before[2], item]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.pushObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/pushObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/pushObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/pushObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/pushObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/pushObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/pushObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/pushObjects', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('pushObjects'); suite.test("should raise exception if not Ember.Enumerable is passed to pushObjects", function() { var obj = this.newObject([]); throws(function() { obj.pushObjects("string"); }); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/pushObjects.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/pushObjects.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/pushObjects.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/pushObjects.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/pushObjects.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/pushObjects.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/removeAt', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('removeAt'); suite.test("[X].removeAt(0) => [] + notify", function() { var obj, before, after, observer; before = this.newFixture(1); after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.removeAt(0), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[].removeAt(200) => OUT_OF_RANGE_EXCEPTION exception", function() { var obj = this.newObject([]); throws(function() { obj.removeAt(200); }, Error); }); suite.test("[A,B].removeAt(0) => [B] + notify", function() { var obj, before, after, observer; before = this.newFixture(2); after = [before[1]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.removeAt(0), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[A,B].removeAt(1) => [A] + notify", function() { var obj, before, after, observer; before = this.newFixture(2); after = [before[0]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.removeAt(1), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); }); suite.test("[A,B,C].removeAt(1) => [A,C] + notify", function() { var obj, before, after, observer; before = this.newFixture(3); after = [before[0], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.removeAt(1), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); suite.test("[A,B,C,D].removeAt(1,2) => [A,D] + notify", function() { var obj, before, after, observer; before = this.newFixture(4); after = [before[0], before[3]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.removeAt(1, 2), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/removeAt.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/removeAt.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/removeAt.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/removeAt.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/removeAt.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/removeAt.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/removeObject', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('removeObject'); suite.test("should return receiver", function() { var before, obj; before = this.newFixture(3); obj = this.newObject(before); equal(obj.removeObject(before[1]), obj, 'should return receiver'); }); suite.test("[A,B,C].removeObject(B) => [A,C] + notify", function() { var obj, before, after, observer; before = this.newFixture(3); after = [before[0], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.removeObject(before[1]); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); } }); suite.test("[A,B,C].removeObject(D) => [A,B,C]", function() { var obj, before, after, observer, item; before = this.newFixture(3); after = before; item = this.newFixture(1)[0]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.removeObject(item); // note: item not in set deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.validate('[]'), false, 'should NOT have notified []'); equal(observer.validate('@each'), false, 'should NOT have notified @each'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); } }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/removeObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/removeObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/removeObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/removeObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/removeObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/removeObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/replace', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('replace'); suite.test("[].replace(0,0,'X') => ['X'] + notify", function() { var obj, exp, observer; exp = this.newFixture(1); obj = this.newObject([]); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.replace(0, 0, exp); deepEqual(this.toArray(obj), exp, 'post item results'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C,D].replace(1,2,X) => [A,X,D] + notify", function() { var obj, observer, before, replace, after; before = this.newFixture(4); replace = this.newFixture(1); after = [before[0], replace[0], before[3]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.replace(1, 2, replace); deepEqual(this.toArray(obj), after, 'post item results'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); suite.test("[A,B,C,D].replace(1,2,[X,Y]) => [A,X,Y,D] + notify", function() { var obj, observer, before, replace, after; before = this.newFixture(4); replace = this.newFixture(2); after = [before[0], replace[0], replace[1], before[3]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.replace(1, 2, replace); deepEqual(this.toArray(obj), after, 'post item results'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); suite.test("[A,B].replace(1,0,[X,Y]) => [A,X,Y,B] + notify", function() { var obj, observer, before, replace, after; before = this.newFixture(2); replace = this.newFixture(2); after = [before[0], replace[0], replace[1], before[1]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.replace(1, 0, replace); deepEqual(this.toArray(obj), after, 'post item results'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); suite.test("[A,B,C,D].replace(2,2) => [A,B] + notify", function() { var obj, observer, before, after; before = this.newFixture(4); after = [before[0], before[1]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.replace(2, 2); deepEqual(this.toArray(obj), after, 'post item results'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); }); suite.test('Adding object should notify enumerable observer', function() { var fixtures = this.newFixture(4); var obj = this.newObject(fixtures); var observer = this.newObserver(obj).observeEnumerable(obj); var item = this.newFixture(1)[0]; obj.replace(2, 2, [item]); deepEqual(observer._before, [obj, [fixtures[2], fixtures[3]], 1], 'before'); deepEqual(observer._after, [obj, 2, [item]], 'after'); }); suite.test('Adding object should notify array observer', function() { var fixtures = this.newFixture(4); var obj = this.newObject(fixtures); var observer = this.newObserver(obj).observeArray(obj); var item = this.newFixture(1)[0]; obj.replace(2, 2, [item]); deepEqual(observer._before, [obj, 2, 2, 1], 'before'); deepEqual(observer._after, [obj, 2, 2, 1], 'after'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/replace.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/replace.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/replace.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/replace.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/replace.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/replace.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/reverseObjects', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('reverseObjects'); suite.test("[A,B,C].reverseObjects() => [] + notify", function () { var obj, before, after, observer; before = this.newFixture(3); after = [before[2], before[1], before[0]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.reverseObjects(), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 0, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/reverseObjects.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/reverseObjects.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/reverseObjects.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/reverseObjects.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/reverseObjects.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/reverseObjects.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/setObjects', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('setObjects'); suite.test("[A,B,C].setObjects([]) = > [] + notify", function() { var obj, before, after, observer; before = this.newFixture(3); after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.setObjects(after), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C].setObjects([D, E, F, G]) = > [D, E, F, G] + notify", function() { var obj, before, after, observer; before = this.newFixture(3); after = this.newFixture(4); obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.setObjects(after), obj, 'return self'); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/setObjects.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/setObjects.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/setObjects.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/setObjects.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/setObjects.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/setObjects.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/shiftObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('shiftObject'); suite.test("[].shiftObject() => [] + returns undefined + NO notify", function() { var obj, before, after, observer; before = []; after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.shiftObject(), undefined); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.validate('[]', undefined, 1), false, 'should NOT have notified [] once'); equal(observer.validate('@each', undefined, 1), false, 'should NOT have notified @each once'); equal(observer.validate('length', undefined, 1), false, 'should NOT have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); suite.test("[X].shiftObject() => [] + notify", function() { var obj, before, after, observer; before = this.newFixture(1); after = []; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.shiftObject(), before[0], 'should return object'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C].shiftObject() => [B,C] + notify", function() { var obj, before, after, observer; before = this.newFixture(3); after = [before[1], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ equal(obj.shiftObject(), before[0], 'should return object'); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/shiftObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/shiftObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/shiftObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/shiftObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/shiftObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/shiftObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/unshiftObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('unshiftObject'); suite.test("returns unshifted object", function() { var obj = this.newObject([]); var item = this.newFixture(1)[0]; equal(obj.unshiftObject(item), item, 'should return unshifted object'); }); suite.test("[].unshiftObject(X) => [X] + notify", function() { var obj, before, after, item, observer; before = []; item = this.newFixture(1)[0]; after = [item]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.unshiftObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C].unshiftObject(X) => [X,A,B,C] + notify", function() { var obj, before, after, item, observer; before = this.newFixture(3); item = this.newFixture(1)[0]; after = [item, before[0], before[1], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.unshiftObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[A,B,C].unshiftObject(A) => [A,A,B,C] + notify", function() { var obj, before, after, item, observer; before = this.newFixture(3); item = before[0]; // note same object as current head. should end up twice after = [item, before[0], before[1], before[2]]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.unshiftObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/unshiftObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/unshiftObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/unshiftObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/unshiftObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/unshiftObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/unshiftObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/unshiftObjects', ['exports', 'ember-runtime/tests/suites/suite'], function (exports, suites__suite) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('unshiftObjects'); suite.test("returns receiver", function() { var obj = this.newObject([]); var items = this.newFixture(3); equal(obj.unshiftObjects(items), obj, 'should return receiver'); }); suite.test("[].unshiftObjects([A,B,C]) => [A,B,C] + notify", function() { var obj, before, items, observer; before = []; items = this.newFixture(3); obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.unshiftObjects(items); deepEqual(this.toArray(obj), items, 'post item results'); equal(Ember.get(obj, 'length'), items.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); suite.test("[A,B,C].unshiftObjects([X,Y]) => [X,Y,A,B,C] + notify", function() { var obj, before, items, after, observer; before = this.newFixture(3); items = this.newFixture(2); after = items.concat(before); obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.unshiftObjects(items); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); suite.test("[A,B,C].unshiftObjects([A,B]) => [A,B,A,B,C] + notify", function() { var obj, before, after, items, observer; before = this.newFixture(3); items = [before[0], before[1]]; // note same object as current head. should end up twice after = items.concat(before); obj = this.newObject(before); observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); /* Prime the cache */ obj.unshiftObjects(items); deepEqual(this.toArray(obj), after, 'post item results'); equal(Ember.get(obj, 'length'), after.length, 'length'); equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('@each'), 1, 'should have notified @each once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_array/unshiftObjects.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/unshiftObjects.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/unshiftObjects.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_array/unshiftObjects.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_array'); test('ember-runtime/tests/suites/mutable_array/unshiftObjects.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_array/unshiftObjects.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable', ['exports', 'ember-runtime/tests/suites/enumerable', 'ember-runtime/tests/suites/mutable_enumerable/addObject', 'ember-runtime/tests/suites/mutable_enumerable/removeObject', 'ember-runtime/tests/suites/mutable_enumerable/removeObjects'], function (exports, enumerable, addObjectTests, removeObjectTests, removeObjectsTests) { 'use strict'; var MutableEnumerableTests = enumerable.EnumerableTests.extend(); MutableEnumerableTests.importModuleTests(addObjectTests['default']); MutableEnumerableTests.importModuleTests(removeObjectTests['default']); MutableEnumerableTests.importModuleTests(removeObjectsTests['default']); exports['default'] = MutableEnumerableTests; }); enifed('ember-runtime/tests/suites/mutable_enumerable.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/mutable_enumerable.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/mutable_enumerable.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable/addObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('addObject'); suite.test("should return receiver", function() { var before, obj; before = this.newFixture(3); obj = this.newObject(before); equal(obj.addObject(before[1]), obj, 'should return receiver'); }); suite.test("[A,B].addObject(C) => [A,B,C] + notify", function() { var obj, before, after, observer, item; before = this.newFixture(2); item = this.newFixture(1)[0]; after = [before[0], before[1], item]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); property_get.get(obj, 'firstObject'); property_get.get(obj, 'lastObject'); obj.addObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); // This gets called since MutableEnumerable is naive about changes equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once'); } }); suite.test("[A,B,C].addObject(A) => [A,B,C] + NO notify", function() { var obj, before, after, observer, item; before = this.newFixture(3); after = before; item = before[0]; obj = this.newObject(before); observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.addObject(item); // note: item in set deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.validate('[]'), false, 'should NOT have notified []'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test('Adding object should notify enumerable observer', function() { var obj = this.newObject(this.newFixture(3)); var observer = this.newObserver(obj).observeEnumerable(obj); var item = this.newFixture(1)[0]; obj.addObject(item); deepEqual(observer._before, [obj, null, [item]]); deepEqual(observer._after, [obj, null, [item]]); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_enumerable/addObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_enumerable'); test('ember-runtime/tests/suites/mutable_enumerable/addObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable/addObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable/addObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_enumerable'); test('ember-runtime/tests/suites/mutable_enumerable/addObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable/addObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable/removeObject', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get'], function (exports, suites__suite, property_get) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('removeObject'); suite.test("should return receiver", function() { var before, obj; before = this.newFixture(3); obj = this.newObject(before); equal(obj.removeObject(before[1]), obj, 'should return receiver'); }); suite.test("[A,B,C].removeObject(B) => [A,C] + notify", function() { var obj, before, after, observer; before = Ember.A(this.newFixture(3)); after = [before[0], before[2]]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObject(before[1]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test("[A,B,C].removeObject(D) => [A,B,C]", function() { var obj, before, after, observer, item; before = Ember.A(this.newFixture(3)); after = before; item = this.newFixture(1)[0]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObject(item); // Note: item not in set deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.validate('[]'), false, 'should NOT have notified []'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test('Removing object should notify enumerable observer', function() { var fixtures = this.newFixture(3); var obj = this.newObject(fixtures); var observer = this.newObserver(obj).observeEnumerable(obj); var item = fixtures[1]; obj.removeObject(item); deepEqual(observer._before, [obj, [item], null]); deepEqual(observer._after, [obj, [item], null]); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_enumerable/removeObject.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_enumerable'); test('ember-runtime/tests/suites/mutable_enumerable/removeObject.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable/removeObject.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable/removeObject.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_enumerable'); test('ember-runtime/tests/suites/mutable_enumerable/removeObject.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable/removeObject.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable/removeObjects', ['exports', 'ember-runtime/tests/suites/suite', 'ember-metal/property_get', 'ember-metal/core'], function (exports, suites__suite, property_get, Ember) { 'use strict'; var suite = suites__suite.SuiteModuleBuilder.create(); suite.module('removeObjects'); suite.test("should return receiver", function() { var before, obj; before = Ember['default'].A(this.newFixture(3)); obj = before; equal(obj.removeObjects(before[1]), obj, 'should return receiver'); }); suite.test("[A,B,C].removeObjects([B]) => [A,C] + notify", function() { var obj, before, after, observer; before = Ember['default'].A(this.newFixture(3)); after = [before[0], before[2]]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects([before[1]]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test("[{A},{B},{C}].removeObjects([{B}]) => [{A},{C}] + notify", function() { var obj, before, after, observer; before = Ember['default'].A(this.newObjectsFixture(3)); after = [before[0], before[2]]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects([before[1]]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test("[A,B,C].removeObjects([A,B]) => [C] + notify", function() { var obj, before, after, observer; before = Ember['default'].A(this.newFixture(3)); after = [before[2]]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects([before[0], before[1]]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test("[{A},{B},{C}].removeObjects([{A},{B}]) => [{C}] + notify", function() { var obj, before, after, observer; before = Ember['default'].A(this.newObjectsFixture(3)); after = [before[2]]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects([before[0], before[1]]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test("[A,B,C].removeObjects([A,B,C]) => [] + notify", function() { var obj, before, after, observer; before = Ember['default'].A(this.newFixture(3)); after = []; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects([before[0], before[1], before[2]]); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject'); equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject'); } }); suite.test("[{A},{B},{C}].removeObjects([{A},{B},{C}]) => [] + notify", function() { var obj, before, after, observer; before = Ember['default'].A(this.newObjectsFixture(3)); after = []; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects(before); deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.timesCalled('[]'), 1, 'should have notified [] once'); equal(observer.timesCalled('length'), 1, 'should have notified length once'); equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject'); equal(observer.validate('lastObject'), 1, 'should have notified lastObject'); } }); suite.test("[A,B,C].removeObjects([D]) => [A,B,C]", function() { var obj, before, after, observer, item; before = Ember['default'].A(this.newFixture(3)); after = before; item = this.newFixture(1)[0]; obj = before; observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject'); obj.getProperties('firstObject', 'lastObject'); // Prime the cache obj.removeObjects([item]); // Note: item not in set deepEqual(this.toArray(obj), after, 'post item results'); equal(property_get.get(obj, 'length'), after.length, 'length'); if (observer.isEnabled) { equal(observer.validate('[]'), false, 'should NOT have notified []'); equal(observer.validate('length'), false, 'should NOT have notified length'); equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject'); equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject'); } }); suite.test('Removing objects should notify enumerable observer', function() { var fixtures = this.newFixture(3); var obj = this.newObject(fixtures); var observer = this.newObserver(obj).observeEnumerable(obj); var item = fixtures[1]; obj.removeObjects([item]); deepEqual(observer._before, [obj, [item], null]); deepEqual(observer._after, [obj, [item], null]); }); exports['default'] = suite; }); enifed('ember-runtime/tests/suites/mutable_enumerable/removeObjects.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites/mutable_enumerable'); test('ember-runtime/tests/suites/mutable_enumerable/removeObjects.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable/removeObjects.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/mutable_enumerable/removeObjects.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites/mutable_enumerable'); test('ember-runtime/tests/suites/mutable_enumerable/removeObjects.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/mutable_enumerable/removeObjects.js should pass jshint.'); }); }); enifed('ember-runtime/tests/suites/suite', ['exports', 'ember-runtime/system/object', 'ember-metal/mixin', 'ember-metal/utils', 'ember-metal/property_get', 'ember-metal/enumerable_utils'], function (exports, EmberObject, mixin, utils, property_get, enumerable_utils) { 'use strict'; var Suite = EmberObject['default'].extend({ /** Define a name for these tests - all modules are prefixed w/ it. @type String */ name: mixin.required(String), /** Invoked to actually run the test - overridden by mixins */ run: function() {} }); Suite.reopenClass({ plan: null, run: function() { var C = this; return new C().run(); }, module: function(desc, opts) { if (!opts) { opts = {}; } var setup = opts.setup; var teardown = opts.teardown; this.reopen({ run: function() { this._super.apply(this, arguments); var title = property_get.get(this, 'name')+': '+desc; var ctx = this; QUnit.module(title, { setup: function() { if (setup) { setup.call(ctx); } }, teardown: function() { if (teardown) { teardown.call(ctx); } } }); } }); }, test: function(name, func) { this.reopen({ run: function() { this._super.apply(this, arguments); var ctx = this; if (!func) { QUnit.test(name); // output warning } else { QUnit.test(name, function() { func.call(ctx); }); } } }); }, // convert to guids to minimize logging. same: function(actual, exp, message) { actual = (actual && actual.map) ? actual.map(function(x) { return utils.guidFor(x); }) : actual; exp = (exp && exp.map) ? exp.map(function(x) { return utils.guidFor(x); }) : exp; return deepEqual(actual, exp, message); }, // easy way to disable tests notest: function() {}, importModuleTests: function(builder) { var self = this; this.module(builder._module); enumerable_utils.forEach(builder._tests, function(descAndFunc) { self.test.apply(self, descAndFunc); }); } }); var SuiteModuleBuilder = EmberObject['default'].extend({ _module: null, _tests: null, init: function() { this._tests = []; }, module: function(name) { this._module = name; }, test: function(name, func) { this._tests.push([name, func]); } }); exports['default'] = Suite; exports.SuiteModuleBuilder = SuiteModuleBuilder; exports.Suite = Suite; }); enifed('ember-runtime/tests/suites/suite.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/suite.js should pass jscs', function() { ok(true, 'ember-runtime/tests/suites/suite.js should pass jscs.'); }); }); enifed('ember-runtime/tests/suites/suite.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/suites'); test('ember-runtime/tests/suites/suite.js should pass jshint', function() { ok(true, 'ember-runtime/tests/suites/suite.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/application/base_test', ['ember-runtime/system/namespace', 'ember-runtime/system/application'], function (Namespace, Application) { 'use strict'; QUnit.module('Ember.Application'); QUnit.test('Ember.Application should be a subclass of Ember.Namespace', function() { ok(Namespace['default'].detect(Application['default']), 'Ember.Application subclass of Ember.Namespace'); }); }); enifed('ember-runtime/tests/system/application/base_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/application'); test('ember-runtime/tests/system/application/base_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/application/base_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/application/base_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/application'); test('ember-runtime/tests/system/application/base_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/application/base_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/array_proxy/arranged_content_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-metal/computed', 'ember-runtime/system/array_proxy'], function (Ember, run, computed, ArrayProxy) { 'use strict'; var array; QUnit.module("ArrayProxy - arrangedContent", { setup: function() { run['default'](function() { array = ArrayProxy['default'].createWithMixins({ content: Ember['default'].A([1,2,4,5]), arrangedContent: computed.computed(function() { var content = this.get('content'); return content && Ember['default'].A(content.slice().sort(function(a, b) { if (a == null) { a = -1; } if (b == null) { b = -1; } return b - a; })); }).property('content.[]') }); }); }, teardown: function() { run['default'](function() { array.destroy(); }); } }); QUnit.test("addObject - adds to end of 'content' if not present", function() { run['default'](function() { array.addObject(3); }); deepEqual(array.get('content'), [1,2,4,5,3], 'adds to end of content'); deepEqual(array.get('arrangedContent'), [5,4,3,2,1], 'arrangedContent stays sorted'); run['default'](function() { array.addObject(1); }); deepEqual(array.get('content'), [1,2,4,5,3], 'does not add existing number to content'); }); QUnit.test("addObjects - adds to end of 'content' if not present", function() { run['default'](function() { array.addObjects([1,3,6]); }); deepEqual(array.get('content'), [1,2,4,5,3,6], 'adds to end of content'); deepEqual(array.get('arrangedContent'), [6,5,4,3,2,1], 'arrangedContent stays sorted'); }); QUnit.test("compact - returns arrangedContent without nulls and undefined", function() { run['default'](function() { array.set('content', Ember['default'].A([1,3,null,2,undefined])); }); deepEqual(array.compact(), [3,2,1]); }); QUnit.test("indexOf - returns index of object in arrangedContent", function() { equal(array.indexOf(4), 1, 'returns arranged index'); }); QUnit.test("insertAt - raises, indeterminate behavior", function() { throws(function() { run['default'](function() { array.insertAt(2, 3); }); }); }); QUnit.test("lastIndexOf - returns last index of object in arrangedContent", function() { run['default'](function() { array.pushObject(4); }); equal(array.lastIndexOf(4), 2, 'returns last arranged index'); }); QUnit.test("nextObject - returns object at index in arrangedContent", function() { equal(array.nextObject(1), 4, 'returns object at index'); }); QUnit.test("objectAt - returns object at index in arrangedContent", function() { equal(array.objectAt(1), 4, 'returns object at index'); }); // Not sure if we need a specific test for it, since it's internal QUnit.test("objectAtContent - returns object at index in arrangedContent", function() { equal(array.objectAtContent(1), 4, 'returns object at index'); }); QUnit.test("objectsAt - returns objects at indices in arrangedContent", function() { deepEqual(array.objectsAt([0,2,4]), [5,2,undefined], 'returns objects at indices'); }); QUnit.test("popObject - removes last object in arrangedContent", function() { var popped; run['default'](function() { popped = array.popObject(); }); equal(popped, 1, 'returns last object'); deepEqual(array.get('content'), [2,4,5], 'removes from content'); }); QUnit.test("pushObject - adds to end of content even if it already exists", function() { run['default'](function() { array.pushObject(1); }); deepEqual(array.get('content'), [1,2,4,5,1], 'adds to end of content'); }); QUnit.test("pushObjects - adds multiple to end of content even if it already exists", function() { run['default'](function() { array.pushObjects([1,2,4]); }); deepEqual(array.get('content'), [1,2,4,5,1,2,4], 'adds to end of content'); }); QUnit.test("removeAt - removes from index in arrangedContent", function() { run['default'](function() { array.removeAt(1, 2); }); deepEqual(array.get('content'), [1,5]); }); QUnit.test("removeObject - removes object from content", function() { run['default'](function() { array.removeObject(2); }); deepEqual(array.get('content'), [1,4,5]); }); QUnit.test("removeObjects - removes objects from content", function() { run['default'](function() { array.removeObjects([2,4,6]); }); deepEqual(array.get('content'), [1,5]); }); QUnit.test("replace - raises, indeterminate behavior", function() { throws(function() { run['default'](function() { array.replace(1, 2, [3]); }); }); }); QUnit.test("replaceContent - does a standard array replace on content", function() { run['default'](function() { array.replaceContent(1, 2, [3]); }); deepEqual(array.get('content'), [1,3,5]); }); QUnit.test("reverseObjects - raises, use Sortable#sortAscending", function() { throws(function() { run['default'](function() { array.reverseObjects(); }); }); }); QUnit.test("setObjects - replaces entire content", function() { run['default'](function() { array.setObjects([6,7,8]); }); deepEqual(array.get('content'), [6,7,8], 'replaces content'); }); QUnit.test("shiftObject - removes from start of arrangedContent", function() { var shifted; run['default'](function() { shifted = array.shiftObject(); }); equal(shifted, 5, 'returns first object'); deepEqual(array.get('content'), [1,2,4], 'removes object from content'); }); QUnit.test("slice - returns a slice of the arrangedContent", function() { deepEqual(array.slice(1, 3), [4,2], 'returns sliced arrangedContent'); }); QUnit.test("toArray - returns copy of arrangedContent", function() { deepEqual(array.toArray(), [5,4,2,1]); }); QUnit.test("unshiftObject - adds to start of content", function() { run['default'](function() { array.unshiftObject(6); }); deepEqual(array.get('content'), [6,1,2,4,5], 'adds to start of content'); }); QUnit.test("unshiftObjects - adds to start of content", function() { run['default'](function() { array.unshiftObjects([6,7]); }); deepEqual(array.get('content'), [6,7,1,2,4,5], 'adds to start of content'); }); QUnit.test("without - returns arrangedContent without object", function() { deepEqual(array.without(2), [5,4,1], 'returns arranged without object'); }); QUnit.test("lastObject - returns last arranged object", function() { equal(array.get('lastObject'), 1, 'returns last arranged object'); }); QUnit.test("firstObject - returns first arranged object", function() { equal(array.get('firstObject'), 5, 'returns first arranged object'); }); QUnit.module("ArrayProxy - arrangedContent matching content", { setup: function() { run['default'](function() { array = ArrayProxy['default'].createWithMixins({ content: Ember['default'].A([1,2,4,5]) }); }); }, teardown: function() { run['default'](function() { array.destroy(); }); } }); QUnit.test("insertAt - inserts object at specified index", function() { run['default'](function() { array.insertAt(2, 3); }); deepEqual(array.get('content'), [1,2,3,4,5]); }); QUnit.test("replace - does a standard array replace", function() { run['default'](function() { array.replace(1, 2, [3]); }); deepEqual(array.get('content'), [1,3,5]); }); QUnit.test("reverseObjects - reverses content", function() { run['default'](function() { array.reverseObjects(); }); deepEqual(array.get('content'), [5,4,2,1]); }); QUnit.module("ArrayProxy - arrangedContent with transforms", { setup: function() { run['default'](function() { array = ArrayProxy['default'].createWithMixins({ content: Ember['default'].A([1,2,4,5]), arrangedContent: computed.computed(function() { var content = this.get('content'); return content && Ember['default'].A(content.slice().sort(function(a, b) { if (a == null) { a = -1; } if (b == null) { b = -1; } return b - a; })); }).property('content.[]'), objectAtContent: function(idx) { var obj = this.get('arrangedContent').objectAt(idx); return obj && obj.toString(); } }); }); }, teardown: function() { run['default'](function() { array.destroy(); }); } }); QUnit.test("indexOf - returns index of object in arrangedContent", function() { equal(array.indexOf('4'), 1, 'returns arranged index'); }); QUnit.test("lastIndexOf - returns last index of object in arrangedContent", function() { run['default'](function() { array.pushObject(4); }); equal(array.lastIndexOf('4'), 2, 'returns last arranged index'); }); QUnit.test("nextObject - returns object at index in arrangedContent", function() { equal(array.nextObject(1), '4', 'returns object at index'); }); QUnit.test("objectAt - returns object at index in arrangedContent", function() { equal(array.objectAt(1), '4', 'returns object at index'); }); // Not sure if we need a specific test for it, since it's internal QUnit.test("objectAtContent - returns object at index in arrangedContent", function() { equal(array.objectAtContent(1), '4', 'returns object at index'); }); QUnit.test("objectsAt - returns objects at indices in arrangedContent", function() { deepEqual(array.objectsAt([0,2,4]), ['5','2',undefined], 'returns objects at indices'); }); QUnit.test("popObject - removes last object in arrangedContent", function() { var popped; run['default'](function() { popped = array.popObject(); }); equal(popped, '1', 'returns last object'); deepEqual(array.get('content'), [2,4,5], 'removes from content'); }); QUnit.test("removeObject - removes object from content", function() { run['default'](function() { array.removeObject('2'); }); deepEqual(array.get('content'), [1,4,5]); }); QUnit.test("removeObjects - removes objects from content", function() { run['default'](function() { array.removeObjects(['2','4','6']); }); deepEqual(array.get('content'), [1,5]); }); QUnit.test("shiftObject - removes from start of arrangedContent", function() { var shifted; run['default'](function() { shifted = array.shiftObject(); }); equal(shifted, '5', 'returns first object'); deepEqual(array.get('content'), [1,2,4], 'removes object from content'); }); QUnit.test("slice - returns a slice of the arrangedContent", function() { deepEqual(array.slice(1, 3), ['4','2'], 'returns sliced arrangedContent'); }); QUnit.test("toArray - returns copy of arrangedContent", function() { deepEqual(array.toArray(), ['5','4','2','1']); }); QUnit.test("without - returns arrangedContent without object", function() { deepEqual(array.without('2'), ['5','4','1'], 'returns arranged without object'); }); QUnit.test("lastObject - returns last arranged object", function() { equal(array.get('lastObject'), '1', 'returns last arranged object'); }); QUnit.test("firstObject - returns first arranged object", function() { equal(array.get('firstObject'), '5', 'returns first arranged object'); }); }); enifed('ember-runtime/tests/system/array_proxy/arranged_content_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/arranged_content_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/array_proxy/arranged_content_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/array_proxy/arranged_content_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/arranged_content_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/array_proxy/arranged_content_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/array_proxy/content_change_test', ['ember-metal/core', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-runtime/system/array_proxy', 'ember-runtime/controllers/array_controller'], function (Ember, property_set, run, ArrayProxy, ArrayController) { 'use strict'; QUnit.module("ArrayProxy - content change"); QUnit.test("should update length for null content", function() { var proxy = ArrayProxy['default'].create({ content: Ember['default'].A([1,2,3]) }); equal(proxy.get('length'), 3, "precond - length is 3"); proxy.set('content', null); equal(proxy.get('length'), 0, "length updates"); }); QUnit.test("The `arrangedContentWillChange` method is invoked before `content` is changed.", function() { var callCount = 0; var expectedLength; var proxy = ArrayProxy['default'].extend({ content: Ember['default'].A([1, 2, 3]), arrangedContentWillChange: function() { equal(this.get('arrangedContent.length'), expectedLength, "hook should be invoked before array has changed"); callCount++; } }).create(); proxy.pushObject(4); equal(callCount, 0, "pushing content onto the array doesn't trigger it"); proxy.get('content').pushObject(5); equal(callCount, 0, "pushing content onto the content array doesn't trigger it"); expectedLength = 5; proxy.set('content', Ember['default'].A(['a', 'b'])); equal(callCount, 1, "replacing the content array triggers the hook"); }); QUnit.test("The `arrangedContentDidChange` method is invoked after `content` is changed.", function() { var callCount = 0; var expectedLength; var proxy = ArrayProxy['default'].extend({ content: Ember['default'].A([1, 2, 3]), arrangedContentDidChange: function() { equal(this.get('arrangedContent.length'), expectedLength, "hook should be invoked after array has changed"); callCount++; } }).create(); equal(callCount, 0, "hook is not called after creating the object"); proxy.pushObject(4); equal(callCount, 0, "pushing content onto the array doesn't trigger it"); proxy.get('content').pushObject(5); equal(callCount, 0, "pushing content onto the content array doesn't trigger it"); expectedLength = 2; proxy.set('content', Ember['default'].A(['a', 'b'])); equal(callCount, 1, "replacing the content array triggers the hook"); }); QUnit.test("The ArrayProxy doesn't explode when assigned a destroyed object", function() { var arrayController = ArrayController['default'].create(); var proxy = ArrayProxy['default'].create(); run['default'](function() { arrayController.destroy(); }); property_set.set(proxy, 'content', arrayController); ok(true, "No exception was raised"); }); }); enifed('ember-runtime/tests/system/array_proxy/content_change_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/content_change_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/array_proxy/content_change_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/array_proxy/content_change_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/content_change_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/array_proxy/content_change_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/array_proxy/content_update_test', ['ember-metal/core', 'ember-metal/computed', 'ember-runtime/system/array_proxy'], function (Ember, computed, ArrayProxy) { 'use strict'; QUnit.module("Ember.ArrayProxy - content update"); QUnit.test("The `contentArrayDidChange` method is invoked after `content` is updated.", function() { var proxy; var observerCalled = false; proxy = ArrayProxy['default'].createWithMixins({ content: Ember['default'].A(), arrangedContent: computed.computed('content', function(key) { return Ember['default'].A(this.get('content').slice()); }), contentArrayDidChange: function(array, idx, removedCount, addedCount) { observerCalled = true; return this._super(array, idx, removedCount, addedCount); } }); proxy.pushObject(1); ok(observerCalled, "contentArrayDidChange is invoked"); }); }); enifed('ember-runtime/tests/system/array_proxy/content_update_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/content_update_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/array_proxy/content_update_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/array_proxy/content_update_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/content_update_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/array_proxy/content_update_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/array_proxy/suite_test', ['ember-metal/core', 'ember-runtime/tests/suites/mutable_array', 'ember-runtime/system/array_proxy', 'ember-metal/property_get'], function (Ember, MutableArrayTests, ArrayProxy, property_get) { 'use strict'; MutableArrayTests['default'].extend({ name: 'Ember.ArrayProxy', newObject: function(ary) { var ret = ary ? ary.slice() : this.newFixture(3); return ArrayProxy['default'].create({ content: Ember['default'].A(ret) }); }, mutate: function(obj) { obj.pushObject(property_get.get(obj, 'length')+1); }, toArray: function(obj) { return obj.toArray ? obj.toArray() : obj.slice(); } }).run(); }); enifed('ember-runtime/tests/system/array_proxy/suite_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/suite_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/array_proxy/suite_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/array_proxy/suite_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/array_proxy'); test('ember-runtime/tests/system/array_proxy/suite_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/array_proxy/suite_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/deferred_test', ['ember-metal/run_loop', 'ember-runtime/system/deferred'], function (run, Deferred) { 'use strict'; QUnit.module("Ember.Deferred all-in-one"); asyncTest("Can resolve a promise", function() { var value = { value: true }; ignoreDeprecation(function() { var promise = Deferred['default'].promise(function(deferred) { setTimeout(function() { run['default'](function() { deferred.resolve(value); }); }); }); promise.then(function(resolveValue) { QUnit.start(); equal(resolveValue, value, "The resolved value should be correct"); }); }); }); asyncTest("Can reject a promise", function() { var rejected = { rejected: true }; ignoreDeprecation(function() { var promise = Deferred['default'].promise(function(deferred) { setTimeout(function() { run['default'](function() { deferred.reject(rejected); }); }); }); promise.then(null, function(rejectedValue) { QUnit.start(); equal(rejectedValue, rejected, "The resolved value should be correct"); }); }); }); }); enifed('ember-runtime/tests/system/deferred_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system'); test('ember-runtime/tests/system/deferred_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/deferred_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/deferred_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system'); test('ember-runtime/tests/system/deferred_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/deferred_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/lazy_load_test', ['ember-metal/run_loop', 'ember-runtime/system/lazy_load'], function (run, lazy_load) { 'use strict'; QUnit.module("Lazy Loading"); QUnit.test("if a load hook is registered, it is executed when runLoadHooks are exected", function() { var count = 0; run['default'](function() { lazy_load.onLoad("__test_hook__", function(object) { count += object; }); }); run['default'](function() { lazy_load.runLoadHooks("__test_hook__", 1); }); equal(count, 1, "the object was passed into the load hook"); }); QUnit.test("if runLoadHooks was already run, it executes newly added hooks immediately", function() { var count = 0; run['default'](function() { lazy_load.onLoad("__test_hook__", function(object) { count += object; }); }); run['default'](function() { lazy_load.runLoadHooks("__test_hook__", 1); }); count = 0; run['default'](function() { lazy_load.onLoad("__test_hook__", function(object) { count += object; }); }); equal(count, 1, "the original object was passed into the load hook"); }); QUnit.test("hooks in ENV.EMBER_LOAD_HOOKS['hookName'] get executed", function() { // Note that the necessary code to perform this test is run before // the Ember lib is loaded in tests/index.html run['default'](function() { lazy_load.runLoadHooks("__before_ember_test_hook__", 1); }); equal(window.ENV.__test_hook_count__, 1, "the object was passed into the load hook"); }); if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { QUnit.test("load hooks trigger a custom event", function() { var eventObject = "super duper awesome events"; window.addEventListener('__test_hook_for_events__', function(e) { ok(true, 'custom event was fired'); equal(e.detail, eventObject, 'event details are provided properly'); }); run['default'](function() { lazy_load.runLoadHooks("__test_hook_for_events__", eventObject); }); }); } }); enifed('ember-runtime/tests/system/lazy_load_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system'); test('ember-runtime/tests/system/lazy_load_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/lazy_load_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/lazy_load_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system'); test('ember-runtime/tests/system/lazy_load_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/lazy_load_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/namespace/base_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-metal/property_get', 'ember-runtime/system/object', 'ember-runtime/system/namespace'], function (Ember, run, property_get, EmberObject, Namespace) { 'use strict'; var originalLookup, lookup; QUnit.module('Namespace', { setup: function() { originalLookup = Ember['default'].lookup; Ember['default'].BOOTED = false; lookup = Ember['default'].lookup = {}; }, teardown: function() { Ember['default'].BOOTED = false; for (var prop in lookup) { if (lookup[prop]) { run['default'](lookup[prop], 'destroy'); } } Ember['default'].lookup = originalLookup; } }); QUnit.test('Namespace should be a subclass of EmberObject', function() { ok(EmberObject['default'].detect(Namespace['default'])); }); QUnit.test("Namespace should be duck typed", function() { ok(property_get.get(Namespace['default'].create(), 'isNamespace'), "isNamespace property is true"); }); QUnit.test('Namespace is found and named', function() { var nsA = lookup.NamespaceA = Namespace['default'].create(); equal(nsA.toString(), "NamespaceA", "namespaces should have a name if they are on lookup"); var nsB = lookup.NamespaceB = Namespace['default'].create(); equal(nsB.toString(), "NamespaceB", "namespaces work if created after the first namespace processing pass"); }); QUnit.test("Classes under an Namespace are properly named", function() { var nsA = lookup.NamespaceA = Namespace['default'].create(); nsA.Foo = EmberObject['default'].extend(); equal(nsA.Foo.toString(), "NamespaceA.Foo", "Classes pick up their parent namespace"); nsA.Bar = EmberObject['default'].extend(); equal(nsA.Bar.toString(), "NamespaceA.Bar", "New Classes get the naming treatment too"); var nsB = lookup.NamespaceB = Namespace['default'].create(); nsB.Foo = EmberObject['default'].extend(); equal(nsB.Foo.toString(), "NamespaceB.Foo", "Classes in new namespaces get the naming treatment"); }); //test("Classes under Ember are properly named", function() { // // ES6TODO: This test does not work reliably when running independent package build with Broccoli config. // Ember.TestObject = EmberObject.extend({}); // equal(Ember.TestObject.toString(), "Ember.TestObject", "class under Ember is given a string representation"); //}); QUnit.test("Lowercase namespaces are no longer supported", function() { var nsC = lookup.namespaceC = Namespace['default'].create(); equal(nsC.toString(), undefined); }); QUnit.test("A namespace can be assigned a custom name", function() { var nsA = Namespace['default'].create({ name: "NamespaceA" }); var nsB = lookup.NamespaceB = Namespace['default'].create({ name: "CustomNamespaceB" }); nsA.Foo = EmberObject['default'].extend(); nsB.Foo = EmberObject['default'].extend(); equal(nsA.Foo.toString(), "NamespaceA.Foo", "The namespace's name is used when the namespace is not in the lookup object"); equal(nsB.Foo.toString(), "CustomNamespaceB.Foo", "The namespace's name is used when the namespace is in the lookup object"); }); QUnit.test("Calling namespace.nameClasses() eagerly names all classes", function() { Ember['default'].BOOTED = true; var namespace = lookup.NS = Namespace['default'].create(); namespace.ClassA = EmberObject['default'].extend(); namespace.ClassB = EmberObject['default'].extend(); Namespace['default'].processAll(); equal(namespace.ClassA.toString(), "NS.ClassA"); equal(namespace.ClassB.toString(), "NS.ClassB"); }); QUnit.test("A namespace can be looked up by its name", function() { var NS = lookup.NS = Namespace['default'].create(); var UI = lookup.UI = Namespace['default'].create(); var CF = lookup.CF = Namespace['default'].create(); equal(Namespace['default'].byName('NS'), NS); equal(Namespace['default'].byName('UI'), UI); equal(Namespace['default'].byName('CF'), CF); }); QUnit.test("A nested namespace can be looked up by its name", function() { var UI = lookup.UI = Namespace['default'].create(); UI.Nav = Namespace['default'].create(); equal(Namespace['default'].byName('UI.Nav'), UI.Nav); }); QUnit.test("Destroying a namespace before caching lookup removes it from the list of namespaces", function() { var CF = lookup.CF = Namespace['default'].create(); run['default'](CF, 'destroy'); equal(Namespace['default'].byName('CF'), undefined, "namespace can not be found after destroyed"); }); QUnit.test("Destroying a namespace after looking up removes it from the list of namespaces", function() { var CF = lookup.CF = Namespace['default'].create(); equal(Namespace['default'].byName('CF'), CF, "precondition - namespace can be looked up by name"); run['default'](CF, 'destroy'); equal(Namespace['default'].byName('CF'), undefined, "namespace can not be found after destroyed"); }); }); enifed('ember-runtime/tests/system/namespace/base_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/namespace'); test('ember-runtime/tests/system/namespace/base_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/namespace/base_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/namespace/base_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/namespace'); test('ember-runtime/tests/system/namespace/base_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/namespace/base_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/native_array/copyable_suite_test', ['ember-runtime/tests/suites/copyable', 'ember-metal/utils'], function (CopyableTests, utils) { 'use strict'; CopyableTests['default'].extend({ name: 'NativeArray Copyable', newObject: function() { return Ember.A([utils.generateGuid()]); }, isEqual: function(a, b) { if (!(a instanceof Array)) { return false; } if (!(b instanceof Array)) { return false; } if (a.length !== b.length) { return false; } return a[0]===b[0]; }, shouldBeFreezable: false }).run(); QUnit.module("NativeArray Copyable"); QUnit.test("deep copy is respected", function() { var array = Ember.A([{ id: 1 }, { id: 2 }, { id: 3 }]); var copiedArray = array.copy(true); deepEqual(copiedArray, array, "copied array is equivalent"); ok(copiedArray[0] !== array[0], "objects inside should be unique"); }); }); enifed('ember-runtime/tests/system/native_array/copyable_suite_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/native_array'); test('ember-runtime/tests/system/native_array/copyable_suite_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/native_array/copyable_suite_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/native_array/copyable_suite_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/native_array'); test('ember-runtime/tests/system/native_array/copyable_suite_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/native_array/copyable_suite_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/native_array/suite_test', ['ember-runtime/tests/suites/mutable_array'], function (MutableArrayTests) { 'use strict'; MutableArrayTests['default'].extend({ name: 'Native Array', newObject: function(ary) { return Ember.A(ary ? ary.slice() : this.newFixture(3)); }, mutate: function(obj) { obj.pushObject(obj.length+1); }, toArray: function(obj) { return obj.slice(); // make a copy. } }).run(); }); enifed('ember-runtime/tests/system/native_array/suite_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/native_array'); test('ember-runtime/tests/system/native_array/suite_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/native_array/suite_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/native_array/suite_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/native_array'); test('ember-runtime/tests/system/native_array/suite_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/native_array/suite_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/computed_test', ['ember-metal/computed', 'ember-metal/property_get', 'ember-metal/mixin', 'ember-metal/tests/props_helper', 'ember-runtime/system/object'], function (computed, property_get, mixin, props_helper, EmberObject) { 'use strict'; function K() { return this; } QUnit.module('EmberObject computed property'); props_helper.testWithDefault('computed property on instance', function(get, set) { var MyClass = EmberObject['default'].extend({ foo: computed.computed(function() { return 'FOO'; }) }); equal(get(new MyClass(), 'foo'), 'FOO'); }); props_helper.testWithDefault('computed property on subclass', function(get, set) { var MyClass = EmberObject['default'].extend({ foo: computed.computed(function() { return 'FOO'; }) }); var Subclass = MyClass.extend({ foo: computed.computed(function() { return 'BAR'; }) }); equal(get(new Subclass(), 'foo'), 'BAR'); }); props_helper.testWithDefault('replacing computed property with regular val', function(get, set) { var MyClass = EmberObject['default'].extend({ foo: computed.computed(function() { return 'FOO'; }) }); var Subclass = MyClass.extend({ foo: 'BAR' }); equal(get(new Subclass(), 'foo'), 'BAR'); }); props_helper.testWithDefault('complex depndent keys', function(get, set) { var MyClass = EmberObject['default'].extend({ init: function() { this._super.apply(this, arguments); set(this, 'bar', { baz: 'BIFF' }); }, count: 0, foo: computed.computed(function() { set(this, 'count', get(this, 'count')+1); return property_get.get(get(this, 'bar'), 'baz') + ' ' + get(this, 'count'); }).property('bar.baz') }); var Subclass = MyClass.extend({ count: 20 }); var obj1 = new MyClass(); var obj2 = new Subclass(); equal(get(obj1, 'foo'), 'BIFF 1'); equal(get(obj2, 'foo'), 'BIFF 21'); set(get(obj1, 'bar'), 'baz', 'BLARG'); equal(get(obj1, 'foo'), 'BLARG 2'); equal(get(obj2, 'foo'), 'BIFF 21'); set(get(obj2, 'bar'), 'baz', 'BOOM'); equal(get(obj1, 'foo'), 'BLARG 2'); equal(get(obj2, 'foo'), 'BOOM 22'); }); props_helper.testWithDefault('complex dependent keys changing complex dependent keys', function(get, set) { var MyClass = EmberObject['default'].extend({ init: function() { this._super.apply(this, arguments); set(this, 'bar', { baz: 'BIFF' }); }, count: 0, foo: computed.computed(function() { set(this, 'count', get(this, 'count')+1); return property_get.get(get(this, 'bar'), 'baz') + ' ' + get(this, 'count'); }).property('bar.baz') }); var Subclass = MyClass.extend({ init: function() { this._super.apply(this, arguments); set(this, 'bar2', { baz: 'BIFF2' }); }, count: 0, foo: computed.computed(function() { set(this, 'count', get(this, 'count')+1); return property_get.get(get(this, 'bar2'), 'baz') + ' ' + get(this, 'count'); }).property('bar2.baz') }); var obj2 = new Subclass(); equal(get(obj2, 'foo'), 'BIFF2 1'); set(get(obj2, 'bar'), 'baz', 'BLARG'); equal(get(obj2, 'foo'), 'BIFF2 1', 'should not invalidate property'); set(get(obj2, 'bar2'), 'baz', 'BLARG'); equal(get(obj2, 'foo'), 'BLARG 2', 'should invalidate property'); }); QUnit.test("can retrieve metadata for a computed property", function() { var MyClass = EmberObject['default'].extend({ computedProperty: computed.computed(function() { }).meta({ key: 'keyValue' }) }); equal(property_get.get(MyClass.metaForProperty('computedProperty'), 'key'), 'keyValue', "metadata saved on the computed property can be retrieved"); var ClassWithNoMetadata = EmberObject['default'].extend({ computedProperty: computed.computed(function() { })["volatile"](), staticProperty: 12 }); equal(typeof ClassWithNoMetadata.metaForProperty('computedProperty'), "object", "returns empty hash if no metadata has been saved"); expectAssertion(function() { ClassWithNoMetadata.metaForProperty('nonexistentProperty'); }, "metaForProperty() could not find a computed property with key 'nonexistentProperty'."); expectAssertion(function() { ClassWithNoMetadata.metaForProperty('staticProperty'); }, "metaForProperty() could not find a computed property with key 'staticProperty'."); }); QUnit.test("can iterate over a list of computed properties for a class", function() { var MyClass = EmberObject['default'].extend({ foo: computed.computed(function() { }), fooDidChange: mixin.observer('foo', function() { }), bar: computed.computed(function() { }) }); var SubClass = MyClass.extend({ baz: computed.computed(function() { }) }); SubClass.reopen({ bat: computed.computed(function() { }).meta({ iAmBat: true }) }); var list = []; MyClass.eachComputedProperty(function(name) { list.push(name); }); deepEqual(list.sort(), ['bar', 'foo'], "watched and unwatched computed properties are iterated"); list = []; SubClass.eachComputedProperty(function(name, meta) { list.push(name); if (name === 'bat') { deepEqual(meta, { iAmBat: true }); } else { deepEqual(meta, {}); } }); deepEqual(list.sort(), ['bar', 'bat', 'baz', 'foo'], "all inherited properties are included"); }); QUnit.test("list of properties updates when an additional property is added (such cache busting)", function() { var MyClass = EmberObject['default'].extend({ foo: computed.computed(K), fooDidChange: mixin.observer('foo', function() { }), bar: computed.computed(K) }); var list = []; MyClass.eachComputedProperty(function(name) { list.push(name); }); deepEqual(list.sort(), ['bar', 'foo'].sort(), 'expected two computed properties'); MyClass.reopen({ baz: computed.computed(K) }); MyClass.create(); // force apply mixins list = []; MyClass.eachComputedProperty(function(name) { list.push(name); }); deepEqual(list.sort(), ['bar', 'foo', 'baz'].sort(), 'expected three computed properties'); }); }); enifed('ember-runtime/tests/system/object/computed_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/computed_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/computed_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/computed_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/computed_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/computed_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/create_test', ['ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/computed', 'ember-metal/mixin', 'ember-metal/run_loop', 'ember-metal/events', 'ember-runtime/system/object', 'ember-metal/keys'], function (Ember, property_get, property_set, utils, computed, mixin, run, events, EmberObject, keys) { 'use strict'; var moduleOptions, originalLookup; moduleOptions = { setup: function() { originalLookup = Ember['default'].lookup; Ember['default'].lookup = {}; }, teardown: function() { Ember['default'].lookup = originalLookup; } }; QUnit.module('EmberObject.create', moduleOptions); QUnit.test("simple properties are set", function() { var o = EmberObject['default'].create({ ohai: 'there' }); equal(o.get('ohai'), 'there'); }); QUnit.test("calls computed property setters", function() { var MyClass = EmberObject['default'].extend({ foo: computed.computed(function(key, val) { if (arguments.length === 2) { return val; } return "this is not the value you're looking for"; }) }); var o = MyClass.create({ foo: 'bar' }); equal(o.get('foo'), 'bar'); }); QUnit.test("allows bindings to be defined", function() { var obj = EmberObject['default'].create({ foo: 'foo', barBinding: 'foo' }); equal(obj.get('bar'), 'foo', 'The binding value is correct'); }); QUnit.test("calls setUnknownProperty if defined", function() { var setUnknownPropertyCalled = false; var MyClass = EmberObject['default'].extend({ setUnknownProperty: function(key, value) { setUnknownPropertyCalled = true; } }); MyClass.create({ foo: 'bar' }); ok(setUnknownPropertyCalled, 'setUnknownProperty was called'); }); QUnit.test("throws if you try to define a computed property", function() { expectAssertion(function() { EmberObject['default'].create({ foo: computed.computed(function() {}) }); }, 'Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().'); }); QUnit.test("throws if you try to call _super in a method", function() { expectAssertion(function() { EmberObject['default'].create({ foo: function() { this._super.apply(this, arguments); } }); }, 'Ember.Object.create no longer supports defining methods that call _super.'); }); QUnit.test("throws if you try to 'mixin' a definition", function() { var myMixin = mixin.Mixin.create({ adder: function(arg1, arg2) { return arg1 + arg2; } }); expectAssertion(function() { EmberObject['default'].create(myMixin); }, "Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead."); }); // This test is for IE8. QUnit.test("property name is the same as own prototype property", function() { var MyClass = EmberObject['default'].extend({ toString: function() { return 'MyClass'; } }); equal(MyClass.create().toString(), 'MyClass', "should inherit property from the arguments of `EmberObject.create`"); }); QUnit.test("inherits properties from passed in EmberObject", function() { var baseObj = EmberObject['default'].create({ foo: 'bar' }); var secondaryObj = EmberObject['default'].create(baseObj); equal(secondaryObj.foo, baseObj.foo, "Em.O.create inherits properties from EmberObject parameter"); }); QUnit.test("throws if you try to pass anything a string as a parameter", function() { var expected = "EmberObject.create only accepts an objects."; throws(function() { EmberObject['default'].create("some-string"); }, expected); }); QUnit.test("EmberObject.create can take undefined as a parameter", function() { var o = EmberObject['default'].create(undefined); deepEqual(EmberObject['default'].create(), o); }); QUnit.test("EmberObject.create can take null as a parameter", function() { var o = EmberObject['default'].create(null); deepEqual(EmberObject['default'].create(), o); }); QUnit.module('EmberObject.createWithMixins', moduleOptions); QUnit.test("Creates a new object that contains passed properties", function() { var called = false; var obj = EmberObject['default'].createWithMixins({ prop: 'FOO', method: function() { called=true; } }); equal(property_get.get(obj, 'prop'), 'FOO', 'obj.prop'); obj.method(); ok(called, 'method executed'); }); // .......................................................... // WORKING WITH MIXINS // QUnit.test("Creates a new object that includes mixins and properties", function() { var MixinA = mixin.Mixin.create({ mixinA: 'A' }); var obj = EmberObject['default'].createWithMixins(MixinA, { prop: 'FOO' }); equal(property_get.get(obj, 'mixinA'), 'A', 'obj.mixinA'); equal(property_get.get(obj, 'prop'), 'FOO', 'obj.prop'); }); // .......................................................... // LIFECYCLE // QUnit.test("Configures _super() on methods with override", function() { var completed = false; var MixinA = mixin.Mixin.create({ method: function() {} }); var obj = EmberObject['default'].createWithMixins(MixinA, { method: function() { this._super.apply(this, arguments); completed = true; } }); obj.method(); ok(completed, 'should have run method without error'); }); QUnit.test("Calls init if defined", function() { var completed = false; EmberObject['default'].createWithMixins({ init: function() { this._super.apply(this, arguments); completed = true; } }); ok(completed, 'should have run init without error'); }); QUnit.test("Calls all mixin inits if defined", function() { var completed = 0; var Mixin1 = mixin.Mixin.create({ init: function() { this._super.apply(this, arguments); completed++; } }); var Mixin2 = mixin.Mixin.create({ init: function() { this._super.apply(this, arguments); completed++; } }); EmberObject['default'].createWithMixins(Mixin1, Mixin2); equal(completed, 2, 'should have called init for both mixins.'); }); QUnit.test("Triggers init", function() { var completed = false; EmberObject['default'].createWithMixins({ markAsCompleted: events.on("init", function() { completed = true; }) }); ok(completed, 'should have triggered init which should have run markAsCompleted'); }); QUnit.test('creating an object with required properties', function() { var ClassA = EmberObject['default'].extend({ foo: mixin.required() }); var obj = ClassA.createWithMixins({ foo: 'FOO' }); // should not throw equal(property_get.get(obj, 'foo'), 'FOO'); }); // .......................................................... // BUGS // QUnit.test('create should not break observed values', function() { var CountObject = EmberObject['default'].extend({ value: null, _count: 0, reset: function() { this._count = 0; return this; }, valueDidChange: mixin.observer('value', function() { this._count++; }) }); var obj = CountObject.createWithMixins({ value: 'foo' }); equal(obj._count, 0, 'should not fire yet'); property_set.set(obj, 'value', 'BAR'); equal(obj._count, 1, 'should fire'); }); QUnit.test('bindings on a class should only sync on instances', function() { Ember['default'].lookup['TestObject'] = EmberObject['default'].createWithMixins({ foo: 'FOO' }); var Class, inst; run['default'](function() { Class = EmberObject['default'].extend({ fooBinding: 'TestObject.foo' }); inst = Class.createWithMixins(); }); equal(property_get.get(Class.prototype, 'foo'), undefined, 'should not sync binding'); equal(property_get.get(inst, 'foo'), 'FOO', 'should sync binding'); }); QUnit.test('inherited bindings should only sync on instances', function() { var TestObject; Ember['default'].lookup['TestObject'] = TestObject = EmberObject['default'].createWithMixins({ foo: 'FOO' }); var Class, Subclass, inst; run['default'](function() { Class = EmberObject['default'].extend({ fooBinding: 'TestObject.foo' }); }); run['default'](function() { Subclass = Class.extend(); inst = Subclass.createWithMixins(); }); equal(property_get.get(Class.prototype, 'foo'), undefined, 'should not sync binding on Class'); equal(property_get.get(Subclass.prototype, 'foo'), undefined, 'should not sync binding on Subclass'); equal(property_get.get(inst, 'foo'), 'FOO', 'should sync binding on inst'); run['default'](function() { property_set.set(TestObject, 'foo', 'BAR'); }); equal(property_get.get(Class.prototype, 'foo'), undefined, 'should not sync binding on Class'); equal(property_get.get(Subclass.prototype, 'foo'), undefined, 'should not sync binding on Subclass'); equal(property_get.get(inst, 'foo'), 'BAR', 'should sync binding on inst'); }); QUnit.test("created objects should not share a guid with their superclass", function() { ok(utils.guidFor(EmberObject['default']), "EmberObject has a guid"); var objA = EmberObject['default'].createWithMixins(); var objB = EmberObject['default'].createWithMixins(); ok(utils.guidFor(objA) !== utils.guidFor(objB), "two instances do not share a guid"); }); QUnit.test("ensure internal properties do not leak", function() { var obj = EmberObject['default'].create({ firstName: 'Joe', lastName: 'Black' }); var expectedProperties = ['firstName', 'lastName']; var actualProperties = keys['default'](obj); deepEqual(actualProperties, expectedProperties, 'internal properties do not leak'); }); }); enifed('ember-runtime/tests/system/object/create_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/create_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/create_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/create_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/create_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/create_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/destroy_test', ['ember-metal/run_loop', 'ember-metal/platform/define_property', 'ember-metal/mixin', 'ember-metal/property_set', 'ember-metal/binding', 'ember-metal/property_events', 'ember-metal/keys', 'ember-metal/tests/props_helper', 'ember-runtime/system/object'], function (run, define_property, mixin, property_set, binding, property_events, objectKeys, props_helper, EmberObject) { 'use strict'; QUnit.module('ember-runtime/system/object/destroy_test'); props_helper.testBoth("should schedule objects to be destroyed at the end of the run loop", function(get, set) { var obj = EmberObject['default'].create(); var meta; run['default'](function() { obj.destroy(); meta = obj['__ember_meta__']; ok(meta, "meta is not destroyed immediately"); ok(get(obj, 'isDestroying'), "object is marked as destroying immediately"); ok(!get(obj, 'isDestroyed'), "object is not destroyed immediately"); }); meta = obj['__ember_meta__']; ok(!meta, "meta is destroyed after run loop finishes"); ok(get(obj, 'isDestroyed'), "object is destroyed after run loop finishes"); }); QUnit.test("observers should not fire after an object has been destroyed", function() { var count = 0; var obj = EmberObject['default'].createWithMixins({ fooDidChange: mixin.observer('foo', function() { count++; }) }); obj.set('foo', 'bar'); equal(count, 1, "observer was fired once"); run['default'](function() { property_events.beginPropertyChanges(); obj.set('foo', 'quux'); obj.destroy(); property_events.endPropertyChanges(); }); equal(count, 1, "observer was not called after object was destroyed"); }); QUnit.test("destroyed objects should not see each others changes during teardown but a long lived object should", function () { var shouldChange = 0; var shouldNotChange = 0; var objs = {}; var A = EmberObject['default'].extend({ objs: objs, isAlive: true, willDestroy: function () { this.set('isAlive', false); }, bDidChange: mixin.observer('objs.b.isAlive', function () { shouldNotChange++; }), cDidChange: mixin.observer('objs.c.isAlive', function () { shouldNotChange++; }) }); var B = EmberObject['default'].extend({ objs: objs, isAlive: true, willDestroy: function () { this.set('isAlive', false); }, aDidChange: mixin.observer('objs.a.isAlive', function () { shouldNotChange++; }), cDidChange: mixin.observer('objs.c.isAlive', function () { shouldNotChange++; }) }); var C = EmberObject['default'].extend({ objs: objs, isAlive: true, willDestroy: function () { this.set('isAlive', false); }, aDidChange: mixin.observer('objs.a.isAlive', function () { shouldNotChange++; }), bDidChange: mixin.observer('objs.b.isAlive', function () { shouldNotChange++; }) }); var LongLivedObject = EmberObject['default'].extend({ objs: objs, isAliveDidChange: mixin.observer('objs.a.isAlive', function () { shouldChange++; }) }); objs.a = new A(); objs.b = new B(); objs.c = new C(); new LongLivedObject(); run['default'](function () { var keys = objectKeys['default'](objs); for (var i = 0, l = keys.length; i < l; i++) { objs[keys[i]].destroy(); } }); equal(shouldNotChange, 0, 'destroyed graph objs should not see change in willDestroy'); equal(shouldChange, 1, 'long lived should see change in willDestroy'); }); QUnit.test("bindings should be synced when are updated in the willDestroy hook", function() { var bar = EmberObject['default'].create({ value: false, willDestroy: function() { this.set('value', true); } }); var foo = EmberObject['default'].create({ value: null, bar: bar }); run['default'](function() { binding.bind(foo, 'value', 'bar.value'); }); ok(bar.get('value') === false, 'the initial value has been bound'); run['default'](function() { bar.destroy(); }); ok(foo.get('value'), 'foo is synced when the binding is updated in the willDestroy hook'); }); }); enifed('ember-runtime/tests/system/object/destroy_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/destroy_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/destroy_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/destroy_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/destroy_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/destroy_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/detectInstance_test', ['ember-runtime/system/object'], function (EmberObject) { 'use strict'; QUnit.module('system/object/detectInstance'); QUnit.test('detectInstance detects instances correctly', function() { var A = EmberObject['default'].extend(); var B = A.extend(); var C = A.extend(); var o = EmberObject['default'].create(); var a = A.create(); var b = B.create(); var c = C.create(); ok(EmberObject['default'].detectInstance(o), 'o is an instance of EmberObject'); ok(EmberObject['default'].detectInstance(a), 'a is an instance of EmberObject'); ok(EmberObject['default'].detectInstance(b), 'b is an instance of EmberObject'); ok(EmberObject['default'].detectInstance(c), 'c is an instance of EmberObject'); ok(!A.detectInstance(o), 'o is not an instance of A'); ok(A.detectInstance(a), 'a is an instance of A'); ok(A.detectInstance(b), 'b is an instance of A'); ok(A.detectInstance(c), 'c is an instance of A'); ok(!B.detectInstance(o), 'o is not an instance of B'); ok(!B.detectInstance(a), 'a is not an instance of B'); ok(B.detectInstance(b), 'b is an instance of B'); ok(!B.detectInstance(c), 'c is not an instance of B'); ok(!C.detectInstance(o), 'o is not an instance of C'); ok(!C.detectInstance(a), 'a is not an instance of C'); ok(!C.detectInstance(b), 'b is not an instance of C'); ok(C.detectInstance(c), 'c is an instance of C'); }); }); enifed('ember-runtime/tests/system/object/detectInstance_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/detectInstance_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/detectInstance_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/detectInstance_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/detectInstance_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/detectInstance_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/detect_test', ['ember-runtime/system/object'], function (EmberObject) { 'use strict'; QUnit.module('system/object/detect'); QUnit.test('detect detects classes correctly', function() { var A = EmberObject['default'].extend(); var B = A.extend(); var C = A.extend(); ok(EmberObject['default'].detect(EmberObject['default']), 'EmberObject is an EmberObject class'); ok(EmberObject['default'].detect(A), 'A is an EmberObject class'); ok(EmberObject['default'].detect(B), 'B is an EmberObject class'); ok(EmberObject['default'].detect(C), 'C is an EmberObject class'); ok(!A.detect(EmberObject['default']), 'EmberObject is not an A class'); ok(A.detect(A), 'A is an A class'); ok(A.detect(B), 'B is an A class'); ok(A.detect(C), 'C is an A class'); ok(!B.detect(EmberObject['default']), 'EmberObject is not a B class'); ok(!B.detect(A), 'A is not a B class'); ok(B.detect(B), 'B is a B class'); ok(!B.detect(C), 'C is not a B class'); ok(!C.detect(EmberObject['default']), 'EmberObject is not a C class'); ok(!C.detect(A), 'A is not a C class'); ok(!C.detect(B), 'B is not a C class'); ok(C.detect(C), 'C is a C class'); }); }); enifed('ember-runtime/tests/system/object/detect_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/detect_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/detect_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/detect_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/detect_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/detect_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/events_test', ['ember-runtime/system/object', 'ember-runtime/mixins/evented'], function (EmberObject, Evented) { 'use strict'; QUnit.module("Object events"); QUnit.test("a listener can be added to an object", function() { var count = 0; var F = function() { count++; }; var obj = EmberObject['default'].createWithMixins(Evented['default']); obj.on('event!', F); obj.trigger('event!'); equal(count, 1, "the event was triggered"); obj.trigger('event!'); equal(count, 2, "the event was triggered"); }); QUnit.test("a listener can be added and removed automatically the first time it is triggered", function() { var count = 0; var F = function() { count++; }; var obj = EmberObject['default'].createWithMixins(Evented['default']); obj.one('event!', F); obj.trigger('event!'); equal(count, 1, "the event was triggered"); obj.trigger('event!'); equal(count, 1, "the event was not triggered again"); }); QUnit.test("triggering an event can have arguments", function() { var self, args; var obj = EmberObject['default'].createWithMixins(Evented['default']); obj.on('event!', function() { args = [].slice.call(arguments); self = this; }); obj.trigger('event!', "foo", "bar"); deepEqual(args, ["foo", "bar"]); equal(self, obj); }); QUnit.test("a listener can be added and removed automatically and have arguments", function() { var self, args; var count = 0; var obj = EmberObject['default'].createWithMixins(Evented['default']); obj.one('event!', function() { args = [].slice.call(arguments); self = this; count++; }); obj.trigger('event!', "foo", "bar"); deepEqual(args, ["foo", "bar"]); equal(self, obj); equal(count, 1, "the event is triggered once"); obj.trigger('event!', "baz", "bat"); deepEqual(args, ["foo", "bar"]); equal(count, 1, "the event was not triggered again"); equal(self, obj); }); QUnit.test("binding an event can specify a different target", function() { var self, args; var obj = EmberObject['default'].createWithMixins(Evented['default']); var target = {}; obj.on('event!', target, function() { args = [].slice.call(arguments); self = this; }); obj.trigger('event!', "foo", "bar"); deepEqual(args, ["foo", "bar"]); equal(self, target); }); QUnit.test("a listener registered with one can take method as string and can be added with different target", function() { var count = 0; var target = {}; target.fn = function() { count++; }; var obj = EmberObject['default'].createWithMixins(Evented['default']); obj.one('event!', target, 'fn'); obj.trigger('event!'); equal(count, 1, "the event was triggered"); obj.trigger('event!'); equal(count, 1, "the event was not triggered again"); }); QUnit.test("a listener registered with one can be removed with off", function() { var obj = EmberObject['default'].createWithMixins(Evented['default'], { F: function() {} }); var F = function() {}; obj.one('event!', F); obj.one('event!', obj, 'F'); equal(obj.has('event!'), true, 'has events'); obj.off('event!', F); obj.off('event!', obj, 'F'); equal(obj.has('event!'), false, 'has no more events'); }); QUnit.test("adding and removing listeners should be chainable", function() { var obj = EmberObject['default'].createWithMixins(Evented['default']); var F = function() {}; var ret = obj.on('event!', F); equal(ret, obj, '#on returns self'); ret = obj.off('event!', F); equal(ret, obj, '#off returns self'); ret = obj.one('event!', F); equal(ret, obj, '#one returns self'); }); }); enifed('ember-runtime/tests/system/object/events_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/events_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/events_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/events_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/events_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/events_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/extend_test', ['ember-metal/property_get', 'ember-runtime/system/object'], function (property_get, EmberObject) { 'use strict'; QUnit.module('EmberObject.extend'); QUnit.test('Basic extend', function() { var SomeClass = EmberObject['default'].extend({ foo: 'BAR' }); ok(SomeClass.isClass, "A class has isClass of true"); var obj = new SomeClass(); equal(obj.foo, 'BAR'); }); QUnit.test('Sub-subclass', function() { var SomeClass = EmberObject['default'].extend({ foo: 'BAR' }); var AnotherClass = SomeClass.extend({ bar: 'FOO' }); var obj = new AnotherClass(); equal(obj.foo, 'BAR'); equal(obj.bar, 'FOO'); }); QUnit.test('Overriding a method several layers deep', function() { var SomeClass = EmberObject['default'].extend({ fooCnt: 0, foo: function() { this.fooCnt++; }, barCnt: 0, bar: function() { this.barCnt++; } }); var AnotherClass = SomeClass.extend({ barCnt: 0, bar: function() { this.barCnt++; this._super.apply(this, arguments); } }); var FinalClass = AnotherClass.extend({ fooCnt: 0, foo: function() { this.fooCnt++; this._super.apply(this, arguments); } }); var obj = new FinalClass(); obj.foo(); obj.bar(); equal(obj.fooCnt, 2, 'should invoke both'); equal(obj.barCnt, 2, 'should invoke both'); // Try overriding on create also obj = FinalClass.createWithMixins({ foo: function() { this.fooCnt++; this._super.apply(this, arguments); } }); obj.foo(); obj.bar(); equal(obj.fooCnt, 3, 'should invoke final as well'); equal(obj.barCnt, 2, 'should invoke both'); }); QUnit.test('With concatenatedProperties', function() { var SomeClass = EmberObject['default'].extend({ things: 'foo', concatenatedProperties: ['things'] }); var AnotherClass = SomeClass.extend({ things: 'bar' }); var YetAnotherClass = SomeClass.extend({ things: 'baz' }); var some = new SomeClass(); var another = new AnotherClass(); var yetAnother = new YetAnotherClass(); deepEqual(some.get('things'), ['foo'], 'base class should have just its value'); deepEqual(another.get('things'), ['foo', 'bar'], "subclass should have base class' and its own"); deepEqual(yetAnother.get('things'), ['foo', 'baz'], "subclass should have base class' and its own"); }); QUnit.test('With concatenatedProperties class properties', function() { var SomeClass = EmberObject['default'].extend(); SomeClass.reopenClass({ concatenatedProperties: ['things'], things: 'foo' }); var AnotherClass = SomeClass.extend(); AnotherClass.reopenClass({ things: 'bar' }); var YetAnotherClass = SomeClass.extend(); YetAnotherClass.reopenClass({ things: 'baz' }); var some = new SomeClass(); var another = new AnotherClass(); var yetAnother = new YetAnotherClass(); deepEqual(property_get.get(some.constructor, 'things'), ['foo'], 'base class should have just its value'); deepEqual(property_get.get(another.constructor, 'things'), ['foo', 'bar'], "subclass should have base class' and its own"); deepEqual(property_get.get(yetAnother.constructor, 'things'), ['foo', 'baz'], "subclass should have base class' and its own"); }); }); enifed('ember-runtime/tests/system/object/extend_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/extend_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/extend_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/extend_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/extend_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/extend_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/observer_test', ['ember-metal/core', 'ember-metal/mixin', 'ember-metal/run_loop', 'ember-metal/tests/props_helper', 'ember-runtime/system/object'], function (Ember, mixin, run, props_helper, EmberObject) { 'use strict'; QUnit.module('EmberObject observer'); props_helper.testBoth('observer on class', function(get, set) { var MyClass = EmberObject['default'].extend({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = new MyClass(); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observer on subclass', function(get, set) { var MyClass = EmberObject['default'].extend({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var Subclass = MyClass.extend({ foo: mixin.observer('baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = new Subclass(); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); equal(get(obj, 'count'), 0, 'should not invoke observer after change'); set(obj, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observer on instance', function(get, set) { var obj = EmberObject['default'].createWithMixins({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observer on instance overriding class', function(get, set) { var MyClass = EmberObject['default'].extend({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = MyClass.createWithMixins({ foo: mixin.observer('baz', function() { // <-- change property we observe set(this, 'count', get(this, 'count')+1); }) }); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); set(obj, 'bar', "BAZ"); equal(get(obj, 'count'), 0, 'should not invoke observer after change'); set(obj, 'baz', "BAZ"); equal(get(obj, 'count'), 1, 'should invoke observer after change'); }); props_helper.testBoth('observer should not fire after being destroyed', function(get, set) { var obj = EmberObject['default'].createWithMixins({ count: 0, foo: mixin.observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); equal(get(obj, 'count'), 0, 'precond - should not invoke observer immediately'); run['default'](function() { obj.destroy(); }); if (Ember['default'].assert) { expectAssertion(function() { set(obj, 'bar', "BAZ"); }, "calling set on destroyed object"); } else { set(obj, 'bar', "BAZ"); } equal(get(obj, 'count'), 0, 'should not invoke observer after change'); }); // .......................................................... // COMPLEX PROPERTIES // props_helper.testBoth('chain observer on class', function(get, set) { var MyClass = EmberObject['default'].extend({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj1 = MyClass.create({ bar: { baz: 'biff' } }); var obj2 = MyClass.create({ bar: { baz: 'biff2' } }); equal(get(obj1, 'count'), 0, 'should not invoke yet'); equal(get(obj2, 'count'), 0, 'should not invoke yet'); set(get(obj1, 'bar'), 'baz', 'BIFF1'); equal(get(obj1, 'count'), 1, 'should invoke observer on obj1'); equal(get(obj2, 'count'), 0, 'should not invoke yet'); set(get(obj2, 'bar'), 'baz', 'BIFF2'); equal(get(obj1, 'count'), 1, 'should not invoke again'); equal(get(obj2, 'count'), 1, 'should invoke observer on obj2'); }); props_helper.testBoth('chain observer on class', function(get, set) { var MyClass = EmberObject['default'].extend({ count: 0, foo: mixin.observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj1 = MyClass.createWithMixins({ bar: { baz: 'biff' } }); var obj2 = MyClass.createWithMixins({ bar: { baz: 'biff2' }, bar2: { baz: 'biff3' }, foo: mixin.observer('bar2.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); equal(get(obj1, 'count'), 0, 'should not invoke yet'); equal(get(obj2, 'count'), 0, 'should not invoke yet'); set(get(obj1, 'bar'), 'baz', 'BIFF1'); equal(get(obj1, 'count'), 1, 'should invoke observer on obj1'); equal(get(obj2, 'count'), 0, 'should not invoke yet'); set(get(obj2, 'bar'), 'baz', 'BIFF2'); equal(get(obj1, 'count'), 1, 'should not invoke again'); equal(get(obj2, 'count'), 0, 'should not invoke yet'); set(get(obj2, 'bar2'), 'baz', 'BIFF3'); equal(get(obj1, 'count'), 1, 'should not invoke again'); equal(get(obj2, 'count'), 1, 'should invoke observer on obj2'); }); props_helper.testBoth('chain observer on class that has a reference to an uninitialized object will finish chains that reference it', function(get, set) { var changed = false; var ChildClass = EmberObject['default'].extend({ parent: null, parentOneTwoDidChange: mixin.observer('parent.one.two', function() { changed = true; }) }); var ParentClass = EmberObject['default'].extend({ one: { two: "old" }, init: function () { this.child = ChildClass.create({ parent: this }); } }); var parent = new ParentClass(); equal(changed, false, 'precond'); parent.set('one.two', 'new'); equal(changed, true, 'child should have been notified of change to path'); }); }); enifed('ember-runtime/tests/system/object/observer_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/observer_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/observer_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/observer_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/observer_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/observer_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/reopenClass_test', ['ember-metal/property_get', 'ember-runtime/system/object'], function (property_get, EmberObject) { 'use strict'; QUnit.module('system/object/reopenClass'); QUnit.test('adds new properties to subclass', function() { var Subclass = EmberObject['default'].extend(); Subclass.reopenClass({ foo: function() { return 'FOO'; }, bar: 'BAR' }); equal(Subclass.foo(), 'FOO', 'Adds method'); equal(property_get.get(Subclass, 'bar'), 'BAR', 'Adds property'); }); QUnit.test('class properties inherited by subclasses', function() { var Subclass = EmberObject['default'].extend(); Subclass.reopenClass({ foo: function() { return 'FOO'; }, bar: 'BAR' }); var SubSub = Subclass.extend(); equal(SubSub.foo(), 'FOO', 'Adds method'); equal(property_get.get(SubSub, 'bar'), 'BAR', 'Adds property'); }); }); enifed('ember-runtime/tests/system/object/reopenClass_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/reopenClass_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/reopenClass_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/reopenClass_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/reopenClass_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/reopenClass_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/reopen_test', ['ember-metal/property_get', 'ember-runtime/system/object'], function (property_get, EmberObject) { 'use strict'; QUnit.module('system/core_object/reopen'); QUnit.test('adds new properties to subclass instance', function() { var Subclass = EmberObject['default'].extend(); Subclass.reopen({ foo: function() { return 'FOO'; }, bar: 'BAR' }); equal(new Subclass().foo(), 'FOO', 'Adds method'); equal(property_get.get(new Subclass(), 'bar'), 'BAR', 'Adds property'); }); QUnit.test('reopened properties inherited by subclasses', function() { var Subclass = EmberObject['default'].extend(); var SubSub = Subclass.extend(); Subclass.reopen({ foo: function() { return 'FOO'; }, bar: 'BAR' }); equal(new SubSub().foo(), 'FOO', 'Adds method'); equal(property_get.get(new SubSub(), 'bar'), 'BAR', 'Adds property'); }); QUnit.test('allows reopening already instantiated classes', function() { var Subclass = EmberObject['default'].extend(); Subclass.create(); Subclass.reopen({ trololol: true }); equal(Subclass.create().get('trololol'), true, "reopen works"); }); }); enifed('ember-runtime/tests/system/object/reopen_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/reopen_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/reopen_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/reopen_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/reopen_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/reopen_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/strict-mode-test', ['ember-runtime/system/object'], function (EmberObject) { 'use strict'; QUnit.module('strict mode tests'); QUnit.test('__superWrapper does not throw errors in strict mode', function() { var Foo = EmberObject['default'].extend({ blah: function() { return 'foo'; } }); var Bar = Foo.extend({ blah: function() { return 'bar'; }, callBlah: function() { var blah = this.blah; return blah(); } }); var bar = Bar.create(); equal(bar.callBlah(), 'bar', 'can call local function without call/apply'); }); }); enifed('ember-runtime/tests/system/object/strict-mode-test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/strict-mode-test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/strict-mode-test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/strict-mode-test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/strict-mode-test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/strict-mode-test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/subclasses_test', ['ember-metal/run_loop', 'ember-metal/computed', 'ember-runtime/system/object'], function (run, computed, EmberObject) { 'use strict'; QUnit.module('system/object/subclasses'); QUnit.test('chains should copy forward to subclasses when prototype created', function () { var ObjectWithChains, objWithChains, SubWithChains, SubSub, subSub; run['default'](function () { ObjectWithChains = EmberObject['default'].extend({ obj: { a: 'a', hi: 'hi' }, aBinding: 'obj.a' // add chain }); // realize prototype objWithChains = ObjectWithChains.create(); // should not copy chains from parent yet SubWithChains = ObjectWithChains.extend({ hiBinding: 'obj.hi', // add chain hello: computed.computed(function() { return this.get('obj.hi') + ' world'; }).property('hi'), // observe chain greetingBinding: 'hello' }); SubSub = SubWithChains.extend(); // should realize prototypes and copy forward chains subSub = SubSub.create(); }); equal(subSub.get('greeting'), 'hi world'); run['default'](function () { objWithChains.set('obj.hi', 'hello'); }); equal(subSub.get('greeting'), 'hello world'); }); }); enifed('ember-runtime/tests/system/object/subclasses_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/subclasses_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/subclasses_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/subclasses_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/subclasses_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/subclasses_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object/toString_test', ['ember-metal/core', 'ember-metal/utils', 'ember-runtime/system/object', 'ember-runtime/system/namespace'], function (Ember, utils, EmberObject, Namespace) { 'use strict'; var originalLookup, lookup; QUnit.module('system/object/toString', { setup: function() { originalLookup = Ember['default'].lookup; lookup = Ember['default'].lookup = {}; }, teardown: function() { Ember['default'].lookup = originalLookup; } }); QUnit.test("toString() returns the same value if called twice", function() { var Foo = Namespace['default'].create(); Foo.toString = function() { return "Foo"; }; Foo.Bar = EmberObject['default'].extend(); equal(Foo.Bar.toString(), "Foo.Bar"); equal(Foo.Bar.toString(), "Foo.Bar"); var obj = Foo.Bar.create(); equal(obj.toString(), ""); equal(obj.toString(), ""); equal(Foo.Bar.toString(), "Foo.Bar"); }); QUnit.test("toString on a class returns a useful value when nested in a namespace", function() { var obj; var Foo = Namespace['default'].create(); Foo.toString = function() { return "Foo"; }; Foo.Bar = EmberObject['default'].extend(); equal(Foo.Bar.toString(), "Foo.Bar"); obj = Foo.Bar.create(); equal(obj.toString(), ""); Foo.Baz = Foo.Bar.extend(); equal(Foo.Baz.toString(), "Foo.Baz"); obj = Foo.Baz.create(); equal(obj.toString(), ""); obj = Foo.Bar.create(); equal(obj.toString(), ""); }); QUnit.test("toString on a namespace finds the namespace in Ember.lookup", function() { var Foo = lookup.Foo = Namespace['default'].create(); equal(Foo.toString(), "Foo"); }); QUnit.test("toString on a namespace finds the namespace in Ember.lookup", function() { var Foo = lookup.Foo = Namespace['default'].create(); var obj; Foo.Bar = EmberObject['default'].extend(); equal(Foo.Bar.toString(), "Foo.Bar"); obj = Foo.Bar.create(); equal(obj.toString(), ""); }); QUnit.test("toString on a namespace falls back to modulePrefix, if defined", function() { var Foo = Namespace['default'].create({ modulePrefix: 'foo' }); equal(Foo.toString(), "foo"); }); QUnit.test('toString includes toStringExtension if defined', function() { var Foo = EmberObject['default'].extend({ toStringExtension: function() { return "fooey"; } }); var foo = Foo.create(); var Bar = EmberObject['default'].extend({}); var bar = Bar.create(); // simulate these classes being defined on a Namespace Foo[utils.GUID_KEY+'_name'] = 'Foo'; Bar[utils.GUID_KEY+'_name'] = 'Bar'; equal(bar.toString(), '', 'does not include toStringExtension part'); equal(foo.toString(), '', 'Includes toStringExtension result'); }); }); enifed('ember-runtime/tests/system/object/toString_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/toString_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object/toString_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object/toString_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/object'); test('ember-runtime/tests/system/object/toString_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object/toString_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/object_proxy_test', ['ember-metal/observer', 'ember-metal/computed', 'ember-metal/watching', 'ember-metal/tests/props_helper', 'ember-runtime/system/object_proxy'], function (ember_metal__observer, computed, watching, props_helper, ObjectProxy) { 'use strict'; QUnit.module("ObjectProxy"); props_helper.testBoth("should not proxy properties passed to create", function (get, set) { var Proxy = ObjectProxy['default'].extend({ cp: computed.computed(function (key, value) { if (value) { this._cp = value; } return this._cp; }) }); var proxy = Proxy.create({ prop: 'Foo', cp: 'Bar' }); equal(get(proxy, 'prop'), 'Foo', 'should not have tried to proxy set'); equal(proxy._cp, 'Bar', 'should use CP setter'); }); props_helper.testBoth("should proxy properties to content", function(get, set) { var content = { firstName: 'Tom', lastName: 'Dale', unknownProperty: function (key) { return key + ' unknown';} }; var proxy = ObjectProxy['default'].create(); equal(get(proxy, 'firstName'), undefined, 'get on proxy without content should return undefined'); expectAssertion(function () { set(proxy, 'firstName', 'Foo'); }, /Cannot delegate set\('firstName', Foo\) to the 'content'/i); set(proxy, 'content', content); equal(get(proxy, 'firstName'), 'Tom', 'get on proxy with content should forward to content'); equal(get(proxy, 'lastName'), 'Dale', 'get on proxy with content should forward to content'); equal(get(proxy, 'foo'), 'foo unknown', 'get on proxy with content should forward to content'); set(proxy, 'lastName', 'Huda'); equal(get(content, 'lastName'), 'Huda', 'content should have new value from set on proxy'); equal(get(proxy, 'lastName'), 'Huda', 'proxy should have new value from set on proxy'); set(proxy, 'content', { firstName: 'Yehuda', lastName: 'Katz' }); equal(get(proxy, 'firstName'), 'Yehuda', 'proxy should reflect updated content'); equal(get(proxy, 'lastName'), 'Katz', 'proxy should reflect updated content'); }); props_helper.testBoth("should work with watched properties", function(get, set) { var content1 = { firstName: 'Tom', lastName: 'Dale' }; var content2 = { firstName: 'Yehuda', lastName: 'Katz' }; var count = 0; var Proxy, proxy, last; Proxy = ObjectProxy['default'].extend({ fullName: computed.computed(function () { var firstName = this.get('firstName'); var lastName = this.get('lastName'); if (firstName && lastName) { return firstName + ' ' + lastName; } return firstName || lastName; }).property('firstName', 'lastName') }); proxy = Proxy.create(); ember_metal__observer.addObserver(proxy, 'fullName', function () { last = get(proxy, 'fullName'); count++; }); // proxy without content returns undefined equal(get(proxy, 'fullName'), undefined); // setting content causes all watched properties to change set(proxy, 'content', content1); // both dependent keys changed equal(count, 2); equal(last, 'Tom Dale'); // setting property in content causes proxy property to change set(content1, 'lastName', 'Huda'); equal(count, 3); equal(last, 'Tom Huda'); // replacing content causes all watched properties to change set(proxy, 'content', content2); // both dependent keys changed equal(count, 5); equal(last, 'Yehuda Katz'); // content1 is no longer watched ok(!watching.isWatching(content1, 'firstName'), 'not watching firstName'); ok(!watching.isWatching(content1, 'lastName'), 'not watching lastName'); // setting property in new content set(content2, 'firstName', 'Tomhuda'); equal(last, 'Tomhuda Katz'); equal(count, 6); // setting property in proxy syncs with new content set(proxy, 'lastName', 'Katzdale'); equal(count, 7); equal(last, 'Tomhuda Katzdale'); equal(get(content2, 'firstName'), 'Tomhuda'); equal(get(content2, 'lastName'), 'Katzdale'); }); QUnit.test("set and get should work with paths", function () { var content = { foo: { bar: 'baz' } }; var proxy = ObjectProxy['default'].create({ content: content }); var count = 0; proxy.set('foo.bar', 'hello'); equal(proxy.get('foo.bar'), 'hello'); equal(proxy.get('content.foo.bar'), 'hello'); proxy.addObserver('foo.bar', function () { count++; }); proxy.set('foo.bar', 'bye'); equal(count, 1); equal(proxy.get('foo.bar'), 'bye'); equal(proxy.get('content.foo.bar'), 'bye'); }); props_helper.testBoth("should transition between watched and unwatched strategies", function(get, set) { var content = { foo: 'foo' }; var proxy = ObjectProxy['default'].create({ content: content }); var count = 0; function observer() { count++; } equal(get(proxy, 'foo'), 'foo'); set(content, 'foo', 'bar'); equal(get(proxy, 'foo'), 'bar'); set(proxy, 'foo', 'foo'); equal(get(content, 'foo'), 'foo'); equal(get(proxy, 'foo'), 'foo'); ember_metal__observer.addObserver(proxy, 'foo', observer); equal(count, 0); equal(get(proxy, 'foo'), 'foo'); set(content, 'foo', 'bar'); equal(count, 1); equal(get(proxy, 'foo'), 'bar'); set(proxy, 'foo', 'foo'); equal(count, 2); equal(get(content, 'foo'), 'foo'); equal(get(proxy, 'foo'), 'foo'); ember_metal__observer.removeObserver(proxy, 'foo', observer); set(content, 'foo', 'bar'); equal(get(proxy, 'foo'), 'bar'); set(proxy, 'foo', 'foo'); equal(get(content, 'foo'), 'foo'); equal(get(proxy, 'foo'), 'foo'); }); props_helper.testBoth('setting `undefined` to a proxied content property should override its existing value', function(get, set) { var proxyObject = ObjectProxy['default'].create({ content: { prop: 'emberjs' } }); set(proxyObject, 'prop', undefined); equal(get(proxyObject, 'prop'), undefined, 'sets the `undefined` value to the proxied content'); }); }); enifed('ember-runtime/tests/system/object_proxy_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system'); test('ember-runtime/tests/system/object_proxy_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/object_proxy_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/object_proxy_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system'); test('ember-runtime/tests/system/object_proxy_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/object_proxy_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/set/copyable_suite_test', ['ember-runtime/tests/suites/copyable', 'ember-runtime/system/set', 'ember-metal/utils', 'ember-metal/property_get'], function (CopyableTests, Set, utils, property_get) { 'use strict'; CopyableTests['default'].extend({ name: 'Ember.Set Copyable', newObject: function() { var set, originalCopy; ignoreDeprecation(function() { set = new Set['default'](); }); set.addObject(utils.generateGuid()); originalCopy = set.copy; set.copy = function() { var ret; ignoreDeprecation(function() { ret = originalCopy.apply(set, arguments); }); return ret; }; return set; }, isEqual: function(a, b) { if (!(a instanceof Set['default'])) { return false; } if (!(b instanceof Set['default'])) { return false; } return property_get.get(a, 'firstObject') === property_get.get(b, 'firstObject'); }, shouldBeFreezable: true }).run(); }); enifed('ember-runtime/tests/system/set/copyable_suite_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/set'); test('ember-runtime/tests/system/set/copyable_suite_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/set/copyable_suite_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/set/copyable_suite_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/set'); test('ember-runtime/tests/system/set/copyable_suite_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/set/copyable_suite_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/set/enumerable_suite_test', ['ember-runtime/tests/suites/mutable_enumerable', 'ember-runtime/system/set', 'ember-metal/property_get'], function (MutableEnumerableTests, Set, property_get) { 'use strict'; MutableEnumerableTests['default'].extend({ name: 'Ember.Set', newObject: function(ary) { var ret; ary = ary ? ary.slice() : this.newFixture(3); ignoreDeprecation(function() { ret = new Set['default'](); ret.addObjects(ary); }); return ret; }, mutate: function(obj) { ignoreDeprecation(function() { obj.addObject(property_get.get(obj, 'length')+1); }); }, toArray: function(obj) { var ret; ignoreDeprecation(function() { ret = obj.toArray ? obj.toArray() : obj.slice(); // make a copy. }); return ret; } }).run(); }); enifed('ember-runtime/tests/system/set/enumerable_suite_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/set'); test('ember-runtime/tests/system/set/enumerable_suite_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/set/enumerable_suite_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/set/enumerable_suite_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/set'); test('ember-runtime/tests/system/set/enumerable_suite_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/set/enumerable_suite_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/set/extra_test', ['ember-metal/enumerable_utils', 'ember-metal/property_get', 'ember-metal/observer', 'ember-runtime/system/set'], function (EnumerableUtils, property_get, observer, Set) { 'use strict'; QUnit.module('Set.init'); QUnit.test('passing an array to new Set() should instantiate w/ items', function() { var aSet; var ary = [1,2,3]; var count = 0; ignoreDeprecation(function() { aSet = new Set['default'](ary); }); equal(property_get.get(aSet, 'length'), 3, 'should have three items'); aSet.forEach(function(x) { ok(EnumerableUtils['default'].indexOf(ary, x)>=0, 'should find passed item in array'); count++; }); equal(count, 3, 'iterating should have returned three objects'); }); QUnit.module('Set.clear'); QUnit.test('should clear a set of its content', function() { var aSet; var count = 0; ignoreDeprecation(function() { aSet = new Set['default']([1,2,3]); }); equal(property_get.get(aSet, 'length'), 3, 'should have three items'); ok(property_get.get(aSet, 'firstObject'), 'firstObject should return an object'); ok(property_get.get(aSet, 'lastObject'), 'lastObject should return an object'); observer.addObserver(aSet, '[]', function() { count++; }); aSet.clear(); equal(property_get.get(aSet, 'length'), 0, 'should have 0 items'); equal(count, 1, 'should have notified of content change'); equal(property_get.get(aSet, 'firstObject'), null, 'firstObject should return nothing'); equal(property_get.get(aSet, 'lastObject'), null, 'lastObject should return nothing'); count = 0; aSet.forEach(function() { count++; }); equal(count, 0, 'iterating over items should not invoke callback'); }); // .......................................................... // Set.pop // QUnit.module('Set.pop'); QUnit.test('calling pop should return an object and remove it', function() { var aSet, obj; var count = 0; ignoreDeprecation(function() { aSet = new Set['default']([1,2,3]); }); while (count<10 && (obj = aSet.pop())) { equal(aSet.contains(obj), false, 'set should no longer contain object'); count++; equal(property_get.get(aSet, 'length'), 3-count, 'length should be shorter'); } equal(count, 3, 'should only pop 3 objects'); equal(property_get.get(aSet, 'length'), 0, 'final length should be zero'); equal(aSet.pop(), null, 'extra pops should do nothing'); }); // .......................................................... // Set.aliases // QUnit.module('Set aliases'); QUnit.test('method aliases', function() { var aSet; ignoreDeprecation(function() { aSet = new Set['default'](); }); equal(aSet.add, aSet.addObject, 'add -> addObject'); equal(aSet.remove, aSet.removeObject, 'remove -> removeObject'); equal(aSet.addEach, aSet.addObjects, 'addEach -> addObjects'); equal(aSet.removeEach, aSet.removeObjects, 'removeEach -> removeObjects'); equal(aSet.push, aSet.addObject, 'push -> addObject'); equal(aSet.unshift, aSet.addObject, 'unshift -> addObject'); equal(aSet.shift, aSet.pop, 'shift -> pop'); }); }); enifed('ember-runtime/tests/system/set/extra_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/set'); test('ember-runtime/tests/system/set/extra_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/set/extra_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/set/extra_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/set'); test('ember-runtime/tests/system/set/extra_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/set/extra_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/camelize_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.camelize'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.camelize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.camelize, 'String.prototype helper disabled'); }); } QUnit.test("camelize normal string", function() { deepEqual(string.camelize('my favorite items'), 'myFavoriteItems'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('my favorite items'.camelize(), 'myFavoriteItems'); } }); QUnit.test("camelize capitalized string", function() { deepEqual(string.camelize('I Love Ramen'), 'iLoveRamen'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('I Love Ramen'.camelize(), 'iLoveRamen'); } }); QUnit.test("camelize dasherized string", function() { deepEqual(string.camelize('css-class-name'), 'cssClassName'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('css-class-name'.camelize(), 'cssClassName'); } }); QUnit.test("camelize underscored string", function() { deepEqual(string.camelize('action_name'), 'actionName'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action_name'.camelize(), 'actionName'); } }); QUnit.test("camelize dot notation string", function() { deepEqual(string.camelize('action.name'), 'actionName'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action.name'.camelize(), 'actionName'); } }); QUnit.test("does nothing with camelcased string", function() { deepEqual(string.camelize('innerHTML'), 'innerHTML'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('innerHTML'.camelize(), 'innerHTML'); } }); }); enifed('ember-runtime/tests/system/string/camelize_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/camelize_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/camelize_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/camelize_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/camelize_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/camelize_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/capitalize_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.capitalize'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.capitalize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.capitalize, 'String.prototype helper disabled'); }); } QUnit.test("capitalize normal string", function() { deepEqual(string.capitalize('my favorite items'), 'My favorite items'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('my favorite items'.capitalize(), 'My favorite items'); } }); QUnit.test("capitalize dasherized string", function() { deepEqual(string.capitalize('css-class-name'), 'Css-class-name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('css-class-name'.capitalize(), 'Css-class-name'); } }); QUnit.test("capitalize underscored string", function() { deepEqual(string.capitalize('action_name'), 'Action_name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action_name'.capitalize(), 'Action_name'); } }); QUnit.test("capitalize camelcased string", function() { deepEqual(string.capitalize('innerHTML'), 'InnerHTML'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('innerHTML'.capitalize(), 'InnerHTML'); } }); QUnit.test("does nothing with capitalized string", function() { deepEqual(string.capitalize('Capitalized string'), 'Capitalized string'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('Capitalized string'.capitalize(), 'Capitalized string'); } }); }); enifed('ember-runtime/tests/system/string/capitalize_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/capitalize_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/capitalize_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/capitalize_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/capitalize_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/capitalize_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/classify_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.classify'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.classify is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.classify, 'String.prototype helper disabled'); }); } QUnit.test("classify normal string", function() { deepEqual(string.classify('my favorite items'), 'MyFavoriteItems'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('my favorite items'.classify(), 'MyFavoriteItems'); } }); QUnit.test("classify dasherized string", function() { deepEqual(string.classify('css-class-name'), 'CssClassName'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('css-class-name'.classify(), 'CssClassName'); } }); QUnit.test("classify underscored string", function() { deepEqual(string.classify('action_name'), 'ActionName'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action_name'.classify(), 'ActionName'); } }); QUnit.test("does nothing with classified string", function() { deepEqual(string.classify('InnerHTML'), 'InnerHTML'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('InnerHTML'.classify(), 'InnerHTML'); } }); }); enifed('ember-runtime/tests/system/string/classify_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/classify_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/classify_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/classify_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/classify_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/classify_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/dasherize_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.dasherize'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.dasherize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.dasherize, 'String.prototype helper disabled'); }); } QUnit.test("dasherize normal string", function() { deepEqual(string.dasherize('my favorite items'), 'my-favorite-items'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('my favorite items'.dasherize(), 'my-favorite-items'); } }); QUnit.test("does nothing with dasherized string", function() { deepEqual(string.dasherize('css-class-name'), 'css-class-name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('css-class-name'.dasherize(), 'css-class-name'); } }); QUnit.test("dasherize underscored string", function() { deepEqual(string.dasherize('action_name'), 'action-name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action_name'.dasherize(), 'action-name'); } }); QUnit.test("dasherize camelcased string", function() { deepEqual(string.dasherize('innerHTML'), 'inner-html'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('innerHTML'.dasherize(), 'inner-html'); } }); QUnit.test("dasherize string that is the property name of Object.prototype", function() { deepEqual(string.dasherize('toString'), 'to-string'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('toString'.dasherize(), 'to-string'); } }); }); enifed('ember-runtime/tests/system/string/dasherize_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/dasherize_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/dasherize_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/dasherize_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/dasherize_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/dasherize_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/decamelize_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.decamelize'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.decamelize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.decamelize, 'String.prototype helper disabled'); }); } QUnit.test("does nothing with normal string", function() { deepEqual(string.decamelize('my favorite items'), 'my favorite items'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('my favorite items'.decamelize(), 'my favorite items'); } }); QUnit.test("does nothing with dasherized string", function() { deepEqual(string.decamelize('css-class-name'), 'css-class-name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('css-class-name'.decamelize(), 'css-class-name'); } }); QUnit.test("does nothing with underscored string", function() { deepEqual(string.decamelize('action_name'), 'action_name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action_name'.decamelize(), 'action_name'); } }); QUnit.test("converts a camelized string into all lower case separated by underscores.", function() { deepEqual(string.decamelize('innerHTML'), 'inner_html'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('innerHTML'.decamelize(), 'inner_html'); } }); QUnit.test("decamelizes strings with numbers", function() { deepEqual(string.decamelize('size160Url'), 'size160_url'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('size160Url'.decamelize(), 'size160_url'); } }); }); enifed('ember-runtime/tests/system/string/decamelize_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/decamelize_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/decamelize_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/decamelize_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/decamelize_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/decamelize_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/fmt_string_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.fmt'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.fmt is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.fmt, 'String.prototype helper disabled'); }); } QUnit.test("'Hello %@ %@'.fmt('John', 'Doe') => 'Hello John Doe'", function() { equal(string.fmt('Hello %@ %@', ['John', 'Doe']), 'Hello John Doe'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('Hello %@ %@'.fmt('John', 'Doe'), 'Hello John Doe'); } }); QUnit.test("'Hello %@2 %@1'.fmt('John', 'Doe') => 'Hello Doe John'", function() { equal(string.fmt('Hello %@2 %@1', ['John', 'Doe']), 'Hello Doe John'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('Hello %@2 %@1'.fmt('John', 'Doe'), 'Hello Doe John'); } }); QUnit.test("'%@08 %@07 %@06 %@05 %@04 %@03 %@02 %@01'.fmt('One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight') => 'Eight Seven Six Five Four Three Two One'", function() { equal(string.fmt('%@08 %@07 %@06 %@05 %@04 %@03 %@02 %@01', ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight']), 'Eight Seven Six Five Four Three Two One'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('%@08 %@07 %@06 %@05 %@04 %@03 %@02 %@01'.fmt('One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'), 'Eight Seven Six Five Four Three Two One'); } }); QUnit.test("'data: %@'.fmt({ id: 3 }) => 'data: {id: 3}'", function() { equal(string.fmt('data: %@', [{ id: 3 }]), 'data: {id: 3}'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('data: %@'.fmt({ id: 3 }), 'data: {id: 3}'); } }); QUnit.test("works with argument form", function() { equal(string.fmt('%@', 'John'), 'John'); equal(string.fmt('%@ %@', ['John'], 'Doe'), '[John] Doe'); }); }); enifed('ember-runtime/tests/system/string/fmt_string_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/fmt_string_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/fmt_string_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/fmt_string_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/fmt_string_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/fmt_string_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/loc_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; var oldString; QUnit.module('EmberStringUtils.loc', { setup: function() { oldString = Ember['default'].STRINGS; Ember['default'].STRINGS = { '_Hello World': 'Bonjour le monde', '_Hello %@': 'Bonjour %@', '_Hello %@ %@': 'Bonjour %@ %@', '_Hello %@# %@#': 'Bonjour %@2 %@1' }; }, teardown: function() { Ember['default'].STRINGS = oldString; } }); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.loc is not available without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.loc, 'String.prototype helper disabled'); }); } QUnit.test("'_Hello World'.loc() => 'Bonjour le monde'", function() { equal(string.loc('_Hello World'), 'Bonjour le monde'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('_Hello World'.loc(), 'Bonjour le monde'); } }); QUnit.test("'_Hello %@ %@'.loc('John', 'Doe') => 'Bonjour John Doe'", function() { equal(string.loc('_Hello %@ %@', ['John', 'Doe']), 'Bonjour John Doe'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('_Hello %@ %@'.loc('John', 'Doe'), 'Bonjour John Doe'); } }); QUnit.test("'_Hello %@# %@#'.loc('John', 'Doe') => 'Bonjour Doe John'", function() { equal(string.loc('_Hello %@# %@#', ['John', 'Doe']), 'Bonjour Doe John'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('_Hello %@# %@#'.loc('John', 'Doe'), 'Bonjour Doe John'); } }); QUnit.test("'_Not In Strings'.loc() => '_Not In Strings'", function() { equal(string.loc('_Not In Strings'), '_Not In Strings'); if (Ember['default'].EXTEND_PROTOTYPES) { equal('_Not In Strings'.loc(), '_Not In Strings'); } }); QUnit.test("works with argument form", function() { equal(string.loc('_Hello %@', 'John'), 'Bonjour John'); equal(string.loc('_Hello %@ %@', ['John'], 'Doe'), 'Bonjour [John] Doe'); }); }); enifed('ember-runtime/tests/system/string/loc_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/loc_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/loc_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/loc_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/loc_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/loc_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/underscore_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.underscore'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.underscore is not available without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.underscore, 'String.prototype helper disabled'); }); } QUnit.test("with normal string", function() { deepEqual(string.underscore('my favorite items'), 'my_favorite_items'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('my favorite items'.underscore(), 'my_favorite_items'); } }); QUnit.test("with dasherized string", function() { deepEqual(string.underscore('css-class-name'), 'css_class_name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('css-class-name'.underscore(), 'css_class_name'); } }); QUnit.test("does nothing with underscored string", function() { deepEqual(string.underscore('action_name'), 'action_name'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('action_name'.underscore(), 'action_name'); } }); QUnit.test("with camelcased string", function() { deepEqual(string.underscore('innerHTML'), 'inner_html'); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('innerHTML'.underscore(), 'inner_html'); } }); }); enifed('ember-runtime/tests/system/string/underscore_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/underscore_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/underscore_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/underscore_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/underscore_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/underscore_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/string/w_test', ['ember-metal/core', 'ember-runtime/system/string'], function (Ember, string) { 'use strict'; QUnit.module('EmberStringUtils.w'); if (!Ember['default'].EXTEND_PROTOTYPES && !Ember['default'].EXTEND_PROTOTYPES.String) { QUnit.test("String.prototype.w is not available without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.w, 'String.prototype helper disabled'); }); } QUnit.test("'one two three'.w() => ['one','two','three']", function() { deepEqual(string.w('one two three'), ['one','two','three']); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('one two three'.w(), ['one','two','three']); } }); QUnit.test("'one two three'.w() with extra spaces between words => ['one','two','three']", function() { deepEqual(string.w('one two three'), ['one','two','three']); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('one two three'.w(), ['one','two','three']); } }); QUnit.test("'one two three'.w() with tabs", function() { deepEqual(string.w('one\ttwo three'), ['one','two','three']); if (Ember['default'].EXTEND_PROTOTYPES) { deepEqual('one\ttwo three'.w(), ['one','two','three']); } }); }); enifed('ember-runtime/tests/system/string/w_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/w_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/string/w_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/string/w_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system/string'); test('ember-runtime/tests/system/string/w_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/string/w_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/subarray_test', ['ember-metal/enumerable_utils', 'ember-runtime/system/subarray'], function (enumerable_utils, SubArray) { 'use strict'; var subarray; QUnit.module('SubArray', { setup: function () { subarray = new SubArray['default'](); } }); function operationsString() { var str = ""; enumerable_utils.forEach(subarray._operations, function (operation) { str += " " + operation.type + ":" + operation.count; }); return str.substring(1); } QUnit.test("Subarray operations are initially retain:n", function() { subarray = new SubArray['default'](10); equal(operationsString(), "r:10", "subarray operations are initially retain n"); }); QUnit.test("Retains compose with retains on insert", function() { subarray.addItem(0, true); subarray.addItem(1, true); subarray.addItem(2, true); equal(operationsString(), "r:3", "Retains compose with retains on insert."); }); QUnit.test("Retains compose with retains on removal", function() { subarray.addItem(0, true); subarray.addItem(1, false); subarray.addItem(2, true); equal(operationsString(), "r:1 f:1 r:1", "precond - operations are initially correct."); subarray.removeItem(1); equal(operationsString(), "r:2", "Retains compose with retains on removal."); }); QUnit.test("Filters compose with filters on insert", function() { subarray.addItem(0, false); subarray.addItem(1, false); subarray.addItem(2, false); equal(operationsString(), "f:3", "Retains compose with retains on insert."); }); QUnit.test("Filters compose with filters on removal", function() { subarray.addItem(0, false); subarray.addItem(1, true); subarray.addItem(2, false); equal(operationsString(), "f:1 r:1 f:1", "precond - operations are initially correct."); subarray.removeItem(1); equal(operationsString(), "f:2", "Filters compose with filters on removal."); }); QUnit.test("Filters split retains", function() { subarray.addItem(0, true); subarray.addItem(1, true); subarray.addItem(1, false); equal(operationsString(), "r:1 f:1 r:1", "Filters split retains."); }); QUnit.test("Retains split filters", function() { subarray.addItem(0, false); subarray.addItem(1, false); subarray.addItem(1, true); equal(operationsString(), "f:1 r:1 f:1", "Retains split filters."); }); QUnit.test("`addItem` returns the index of the item in the subarray", function() { equal(subarray.addItem(0, true), 0, "`addItem` returns the index of the item in the subarray"); subarray.addItem(1, false); equal(subarray.addItem(2, true), 1, "`addItem` returns the index of the item in the subarray"); equal(operationsString(), "r:1 f:1 r:1", "Operations are correct."); }); QUnit.test("`addItem` returns -1 if the new item is not in the subarray", function() { equal(subarray.addItem(0, false), -1, "`addItem` returns -1 if the item is not in the subarray"); }); QUnit.test("`removeItem` returns the index of the item in the subarray", function() { subarray.addItem(0, true); subarray.addItem(1, false); subarray.addItem(2, true); equal(subarray.removeItem(2), 1, "`removeItem` returns the index of the item in the subarray"); equal(subarray.removeItem(0), 0, "`removeItem` returns the index of the item in the subarray"); }); QUnit.test("`removeItem` returns -1 if the item was not in the subarray", function() { subarray.addItem(0, true); subarray.addItem(1, false); equal(subarray.removeItem(1), -1, "`removeItem` returns -1 if the item is not in the subarray"); }); QUnit.test("`removeItem` raises a sensible exception when there are no operations in the subarray", function() { var subarrayExploder = function() { subarray.removeItem(9); }; throws(subarrayExploder, /never\ been\ added/, "`removeItem` raises a sensible exception when there are no operations in the subarray"); }); QUnit.test("left composition does not confuse a subsequent right non-composition", function() { subarray.addItem(0, true); subarray.addItem(1, false); subarray.addItem(2, true); equal(operationsString(), "r:1 f:1 r:1", "precond - initial state of subarray is as expected"); subarray.addItem(1, true); equal(operationsString(), "r:2 f:1 r:1", "left-composition does not confuse right non-composition"); }); }); enifed('ember-runtime/tests/system/subarray_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system'); test('ember-runtime/tests/system/subarray_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/subarray_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/subarray_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system'); test('ember-runtime/tests/system/subarray_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/subarray_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/system/tracked_array_test', ['ember-runtime/system/tracked_array'], function (TrackedArray) { 'use strict'; var trackedArray; var RETAIN = TrackedArray['default'].RETAIN; var INSERT = TrackedArray['default'].INSERT; var DELETE = TrackedArray['default'].DELETE; QUnit.module('Ember.TrackedArray'); QUnit.test("operations for a tracked array of length n are initially retain:n", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); equal("r:4", trackedArray.toString(), "initial mutation is retain n"); }); QUnit.test("insert zero items is a no-op", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(2, []); equal(trackedArray.toString(), "r:4", "insert:0 is a no-op"); deepEqual(trackedArray._operations[0].items, [1,2,3,4], "after a no-op, existing operation has right items"); }); QUnit.test("inserts can split retains", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(2, ['a']); equal(trackedArray.toString(), "r:2 i:1 r:2", "inserts can split retains"); deepEqual(trackedArray._operations[0].items, [1,2], "split retains have the right items"); deepEqual(trackedArray._operations[1].items, ['a'], "inserts have the right items"); deepEqual(trackedArray._operations[2].items, [3,4], "split retains have the right items"); }); QUnit.test("inserts can expand (split/compose) inserts", function() { trackedArray = new TrackedArray['default']([]); trackedArray.addItems(0, [1,2,3,4]); trackedArray.addItems(2, ['a']); equal(trackedArray.toString(), "i:5", "inserts can expand inserts"); deepEqual(trackedArray._operations[0].items, [1,2,'a',3,4], "expanded inserts have the right items"); }); QUnit.test("inserts left of inserts compose", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(2, ['b']); trackedArray.addItems(2, ['a']); equal(trackedArray.toString(), "r:2 i:2 r:2", "inserts left of inserts compose"); deepEqual(trackedArray._operations[0].items, [1,2], "split retains have the right items"); deepEqual(trackedArray._operations[1].items, ['a', 'b'], "composed inserts have the right items"); deepEqual(trackedArray._operations[2].items, [3,4], "split retains have the right items"); }); QUnit.test("inserts right of inserts compose", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(2, ['a']); trackedArray.addItems(3, ['b']); equal(trackedArray.toString(), "r:2 i:2 r:2", "inserts right of inserts compose"); deepEqual(trackedArray._operations[0].items, [1,2], "split retains have the right items"); deepEqual(trackedArray._operations[1].items, ['a', 'b'], "composed inserts have the right items"); deepEqual(trackedArray._operations[2].items, [3,4], "split retains have the right items"); }); QUnit.test("delete zero items is a no-op", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(2, []); equal(trackedArray.toString(), "r:4", "insert:0 is a no-op"); deepEqual(trackedArray._operations[0].items, [1,2,3,4], "after a no-op, existing operation has right items"); }); QUnit.test("deletes compose with several inserts and retains", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(4, ['e']); trackedArray.addItems(3, ['d']); trackedArray.addItems(2, ['c']); trackedArray.addItems(1, ['b']); trackedArray.addItems(0, ['a']); // a1b2c3d4e i1r1i1r1i1r1i1r1i1 trackedArray.removeItems(0, 9); equal(trackedArray.toString(), "d:4", "deletes compose with several inserts and retains"); }); QUnit.test("deletes compose with several inserts and retains and an adjacent delete", function() { trackedArray = new TrackedArray['default']([1,2,3,4,5]); trackedArray.removeItems(0, 1); trackedArray.addItems(4, ['e']); trackedArray.addItems(3, ['d']); trackedArray.addItems(2, ['c']); trackedArray.addItems(1, ['b']); trackedArray.addItems(0, ['a']); // a2b3c4d5e d1i1r1i1r1i1r1i1r1i1 trackedArray.removeItems(0, 9); equal(trackedArray.toString(), "d:5", "deletes compose with several inserts, retains, and a single prior delete"); }); QUnit.test("deletes compose with several inserts and retains and can reduce the last one", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(4, ['e', 'f']); trackedArray.addItems(3, ['d']); trackedArray.addItems(2, ['c']); trackedArray.addItems(1, ['b']); trackedArray.addItems(0, ['a']); // a1b2c3d4e i1r1i1r1i1r1i1r1i2 trackedArray.removeItems(0, 9); equal(trackedArray.toString(), "d:4 i:1", "deletes compose with several inserts and retains, reducing the last one"); deepEqual(trackedArray._operations[1].items, ['f'], "last mutation's items is correct"); }); QUnit.test("deletes can split retains", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.removeItems(0, 2); equal(trackedArray.toString(), "d:2 r:2", "deletes can split retains"); deepEqual(trackedArray._operations[1].items, [3,4], "retains reduced by delete have the right items"); }); QUnit.test("deletes can trim retains on the right", function() { trackedArray = new TrackedArray['default']([1,2,3]); trackedArray.removeItems(2, 1); equal(trackedArray.toString(), "r:2 d:1", "deletes can trim retains on the right"); deepEqual(trackedArray._operations[0].items, [1,2], "retains reduced by delete have the right items"); }); QUnit.test("deletes can trim retains on the left", function() { trackedArray = new TrackedArray['default']([1,2,3]); trackedArray.removeItems(0, 1); equal(trackedArray.toString(), "d:1 r:2", "deletes can trim retains on the left"); deepEqual(trackedArray._operations[1].items, [2,3], "retains reduced by delete have the right items"); }); QUnit.test("deletes can split inserts", function() { trackedArray = new TrackedArray['default']([]); trackedArray.addItems(0, ['a','b','c']); trackedArray.removeItems(0, 1); equal(trackedArray.toString(), "i:2", "deletes can split inserts"); deepEqual(trackedArray._operations[0].items, ['b', 'c'], "inserts reduced by delete have the right items"); }); QUnit.test("deletes can trim inserts on the right", function() { trackedArray = new TrackedArray['default']([]); trackedArray.addItems(0, ['a','b','c']); trackedArray.removeItems(2, 1); equal(trackedArray.toString(), "i:2", "deletes can trim inserts on the right"); deepEqual(trackedArray._operations[0].items, ['a', 'b'], "inserts reduced by delete have the right items"); }); QUnit.test("deletes can trim inserts on the left", function() { trackedArray = new TrackedArray['default']([]); trackedArray.addItems(0, ['a','b','c']); trackedArray.removeItems(0, 1); equal(trackedArray.toString(), "i:2", "deletes can trim inserts on the right"); deepEqual(trackedArray._operations[0].items, ['b', 'c'], "inserts reduced by delete have the right items"); }); QUnit.test("deletes can trim inserts on the left while composing with a delete on the left", function() { trackedArray = new TrackedArray['default'](['a']); trackedArray.removeItems(0, 1); trackedArray.addItems(0, ['b', 'c']); trackedArray.removeItems(0, 1); equal(trackedArray.toString(), "d:1 i:1", "deletes can trim inserts and compose with a delete on the left"); deepEqual(trackedArray._operations[1].items, ['c'], "inserts reduced by delete have the right items"); }); QUnit.test("deletes can reduce an insert or retain, compose with several mutations of different types and reduce the last mutation if it is non-delete", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(4, ['e', 'f']); // 1234ef trackedArray.addItems(3, ['d']); // 123d4ef trackedArray.addItems(2, ['c']); // 12c3d4ef trackedArray.addItems(1, ['b']); // 1b2c3d4ef trackedArray.addItems(0, ['a','a','a']); // aaa1b2c3d4ef i3r1i1r1i1r1i1r1i2 trackedArray.removeItems(1, 10); equal(trackedArray.toString(), "i:1 d:4 i:1", "deletes reduce an insert, compose with several inserts and retains, reducing the last one"); deepEqual(trackedArray._operations[0].items, ['a'], "first reduced mutation's items is correct"); deepEqual(trackedArray._operations[2].items, ['f'], "last reduced mutation's items is correct"); }); QUnit.test("removeItems returns the removed items", function() { trackedArray = new TrackedArray['default']([1,2,3,4]); deepEqual(trackedArray.removeItems(1, 2), [2,3], "`removeItems` returns the removed items"); }); QUnit.test("apply invokes the callback with each group of items and the mutation's calculated offset", function() { var i = 0; trackedArray = new TrackedArray['default']([1,2,3,4]); trackedArray.addItems(2, ['a','b','c']); // 12abc34 trackedArray.removeItems(4, 2); // 12ab4 trackedArray.addItems(1, ['d']); // 1d2ab4 r1 i1 r1 i2 d1 r1 equal(trackedArray.toString(), "r:1 i:1 r:1 i:2 d:1 r:1", "precond - trackedArray is in expected state"); trackedArray.apply(function (items, offset, operation) { switch (i++) { case 0: deepEqual(items, [1], "callback passed right items"); equal(offset, 0, "callback passed right offset"); equal(operation, RETAIN, "callback passed right operation"); break; case 1: deepEqual(items, ['d'], "callback passed right items"); equal(offset, 1, "callback passed right offset"); equal(operation, INSERT, "callback passed right operation"); break; case 2: deepEqual(items, [2], "callback passed right items"); equal(offset, 2, "callback passed right offset"); equal(operation, RETAIN, "callback passed right operation"); break; case 3: deepEqual(items, ['a','b'], "callback passed right items"); equal(offset, 3, "callback passed right offset"); equal(operation, INSERT, "callback passed right operation"); break; case 4: // deletes not passed items at the moment; that might need to be added // if TrackedArray is used more widely equal(offset, 5, "callback passed right offset"); equal(operation, DELETE, "callback passed right operation"); break; case 5: deepEqual(items, [4], "callback passed right items"); equal(offset, 5, "callback passed right offset"); equal(operation, RETAIN, "callback passed right operation"); break; } }); equal(i, 6, "`apply` invoked callback right number of times"); equal(trackedArray.toString(), "r:6", "after `apply` operations become retain:n"); }); }); enifed('ember-runtime/tests/system/tracked_array_test.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests/system'); test('ember-runtime/tests/system/tracked_array_test.js should pass jscs', function() { ok(true, 'ember-runtime/tests/system/tracked_array_test.js should pass jscs.'); }); }); enifed('ember-runtime/tests/system/tracked_array_test.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests/system'); test('ember-runtime/tests/system/tracked_array_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/system/tracked_array_test.js should pass jshint.'); }); }); enifed('ember-runtime/tests/utils', ['exports', 'ember-metal/run_loop'], function (exports, run) { 'use strict'; exports.runAppend = runAppend; exports.runDestroy = runDestroy; function runAppend(view) { run['default'](view, "appendTo", "#qunit-fixture"); } function runDestroy(destroyed) { if (destroyed) { run['default'](destroyed, "destroy"); } } }); enifed('ember-runtime/tests/utils.jscs-test', function () { 'use strict'; module('JSCS - ember-runtime/tests'); test('ember-runtime/tests/utils.js should pass jscs', function() { ok(true, 'ember-runtime/tests/utils.js should pass jscs.'); }); }); enifed('ember-runtime/tests/utils.jshint', function () { 'use strict'; module('JSHint - ember-runtime/tests'); test('ember-runtime/tests/utils.js should pass jshint', function() { ok(true, 'ember-runtime/tests/utils.js should pass jshint.'); }); }); enifed('ember-template-compiler.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-template-compiler.js should pass jscs', function() { ok(true, 'ember-template-compiler.js should pass jscs.'); }); }); enifed('ember-template-compiler.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-template-compiler.js should pass jshint', function() { ok(true, 'ember-template-compiler.js should pass jshint.'); }); }); enifed('ember-template-compiler/compat.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler'); test('ember-template-compiler/compat.js should pass jscs', function() { ok(true, 'ember-template-compiler/compat.js should pass jscs.'); }); }); enifed('ember-template-compiler/compat.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler'); test('ember-template-compiler/compat.js should pass jshint', function() { ok(true, 'ember-template-compiler/compat.js should pass jshint.'); }); }); enifed('ember-template-compiler/compat/precompile.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/compat'); test('ember-template-compiler/compat/precompile.js should pass jscs', function() { ok(true, 'ember-template-compiler/compat/precompile.js should pass jscs.'); }); }); enifed('ember-template-compiler/compat/precompile.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/compat'); test('ember-template-compiler/compat/precompile.js should pass jshint', function() { ok(true, 'ember-template-compiler/compat/precompile.js should pass jshint.'); }); }); enifed('ember-template-compiler/plugins.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler'); test('ember-template-compiler/plugins.js should pass jscs', function() { ok(true, 'ember-template-compiler/plugins.js should pass jscs.'); }); }); enifed('ember-template-compiler/plugins.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler'); test('ember-template-compiler/plugins.js should pass jshint', function() { ok(true, 'ember-template-compiler/plugins.js should pass jshint.'); }); }); enifed('ember-template-compiler/plugins/transform-each-in-to-hash.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/plugins'); test('ember-template-compiler/plugins/transform-each-in-to-hash.js should pass jscs', function() { ok(true, 'ember-template-compiler/plugins/transform-each-in-to-hash.js should pass jscs.'); }); }); enifed('ember-template-compiler/plugins/transform-each-in-to-hash.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/plugins'); test('ember-template-compiler/plugins/transform-each-in-to-hash.js should pass jshint', function() { ok(true, 'ember-template-compiler/plugins/transform-each-in-to-hash.js should pass jshint.'); }); }); enifed('ember-template-compiler/plugins/transform-with-as-to-hash.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/plugins'); test('ember-template-compiler/plugins/transform-with-as-to-hash.js should pass jscs', function() { ok(true, 'ember-template-compiler/plugins/transform-with-as-to-hash.js should pass jscs.'); }); }); enifed('ember-template-compiler/plugins/transform-with-as-to-hash.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/plugins'); test('ember-template-compiler/plugins/transform-with-as-to-hash.js should pass jshint', function() { ok(true, 'ember-template-compiler/plugins/transform-with-as-to-hash.js should pass jshint.'); }); }); enifed('ember-template-compiler/system/compile.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/system'); test('ember-template-compiler/system/compile.js should pass jscs', function() { ok(true, 'ember-template-compiler/system/compile.js should pass jscs.'); }); }); enifed('ember-template-compiler/system/compile.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/system'); test('ember-template-compiler/system/compile.js should pass jshint', function() { ok(true, 'ember-template-compiler/system/compile.js should pass jshint.'); }); }); enifed('ember-template-compiler/system/compile_options.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/system'); test('ember-template-compiler/system/compile_options.js should pass jscs', function() { ok(true, 'ember-template-compiler/system/compile_options.js should pass jscs.'); }); }); enifed('ember-template-compiler/system/compile_options.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/system'); test('ember-template-compiler/system/compile_options.js should pass jshint', function() { ok(true, 'ember-template-compiler/system/compile_options.js should pass jshint.'); }); }); enifed('ember-template-compiler/system/precompile.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/system'); test('ember-template-compiler/system/precompile.js should pass jscs', function() { ok(true, 'ember-template-compiler/system/precompile.js should pass jscs.'); }); }); enifed('ember-template-compiler/system/precompile.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/system'); test('ember-template-compiler/system/precompile.js should pass jshint', function() { ok(true, 'ember-template-compiler/system/precompile.js should pass jshint.'); }); }); enifed('ember-template-compiler/system/template.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/system'); test('ember-template-compiler/system/template.js should pass jscs', function() { ok(true, 'ember-template-compiler/system/template.js should pass jscs.'); }); }); enifed('ember-template-compiler/system/template.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/system'); test('ember-template-compiler/system/template.js should pass jshint', function() { ok(true, 'ember-template-compiler/system/template.js should pass jshint.'); }); }); enifed('ember-template-compiler/tests/main_test', function () { 'use strict'; }); enifed('ember-template-compiler/tests/main_test.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/tests'); test('ember-template-compiler/tests/main_test.js should pass jscs', function() { ok(true, 'ember-template-compiler/tests/main_test.js should pass jscs.'); }); }); enifed('ember-template-compiler/tests/main_test.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/tests'); test('ember-template-compiler/tests/main_test.js should pass jshint', function() { ok(true, 'ember-template-compiler/tests/main_test.js should pass jshint.'); }); }); enifed('ember-template-compiler/tests/plugins/transform-each-in-to-hash-test', ['ember-template-compiler'], function (ember_template_compiler) { 'use strict'; QUnit.module('ember-template-compiler: transform-each-in-to-hash'); QUnit.test('cannot use block params and keyword syntax together', function() { expect(1); throws(function() { ember_template_compiler.compile('{{#each thing in controller as |other-thing|}}{{thing}}-{{other-thing}}{{/each}}', true); }, /You cannot use keyword \(`{{each foo in bar}}`\) and block params \(`{{each bar as \|foo\|}}`\) at the same time\./); }); }); enifed('ember-template-compiler/tests/plugins/transform-each-in-to-hash-test.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/tests/plugins'); test('ember-template-compiler/tests/plugins/transform-each-in-to-hash-test.js should pass jscs', function() { ok(true, 'ember-template-compiler/tests/plugins/transform-each-in-to-hash-test.js should pass jscs.'); }); }); enifed('ember-template-compiler/tests/plugins/transform-each-in-to-hash-test.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/tests/plugins'); test('ember-template-compiler/tests/plugins/transform-each-in-to-hash-test.js should pass jshint', function() { ok(true, 'ember-template-compiler/tests/plugins/transform-each-in-to-hash-test.js should pass jshint.'); }); }); enifed('ember-template-compiler/tests/plugins/transform-with-as-to-hash-test', ['ember-template-compiler'], function (ember_template_compiler) { 'use strict'; QUnit.module('ember-template-compiler: transform-with-as-to-hash'); QUnit.test('cannot use block params and keyword syntax together', function() { expect(1); throws(function() { ember_template_compiler.compile('{{#with foo as thing as |other-thing|}}{{thing}}-{{other-thing}}{{/with}}'); }, /You cannot use keyword/); }); }); enifed('ember-template-compiler/tests/plugins/transform-with-as-to-hash-test.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/tests/plugins'); test('ember-template-compiler/tests/plugins/transform-with-as-to-hash-test.js should pass jscs', function() { ok(true, 'ember-template-compiler/tests/plugins/transform-with-as-to-hash-test.js should pass jscs.'); }); }); enifed('ember-template-compiler/tests/plugins/transform-with-as-to-hash-test.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/tests/plugins'); test('ember-template-compiler/tests/plugins/transform-with-as-to-hash-test.js should pass jshint', function() { ok(true, 'ember-template-compiler/tests/plugins/transform-with-as-to-hash-test.js should pass jshint.'); }); }); enifed('ember-template-compiler/tests/plugins_test', ['ember-template-compiler/plugins', 'ember-template-compiler/system/compile'], function (plugins, compile) { 'use strict'; var originalASTPlugins; QUnit.module("ember-htmlbars: Ember.HTMLBars.registerASTPlugin", { setup: function() { originalASTPlugins = plugins["default"].ast.slice(); }, teardown: function() { plugins["default"].ast = originalASTPlugins; } }); QUnit.test("registering a plugin adds it to htmlbars-compiler options", function() { expect(2); function TestPlugin() { ok(true, 'TestPlugin instantiated'); } TestPlugin.prototype.transform = function(ast) { ok(true, 'transform was called'); return ast; }; plugins.registerPlugin('ast', TestPlugin); compile['default']('some random template'); }); QUnit.test('registering an unknown type throws an error', function() { throws(function() { plugins.registerPlugin('asdf', "whatever"); }, /Attempting to register "whatever" as "asdf" which is not a valid HTMLBars plugin type./); }); }); enifed('ember-template-compiler/tests/plugins_test.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/tests'); test('ember-template-compiler/tests/plugins_test.js should pass jscs', function() { ok(true, 'ember-template-compiler/tests/plugins_test.js should pass jscs.'); }); }); enifed('ember-template-compiler/tests/plugins_test.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/tests'); test('ember-template-compiler/tests/plugins_test.js should pass jshint', function() { ok(true, 'ember-template-compiler/tests/plugins_test.js should pass jshint.'); }); }); enifed('ember-template-compiler/tests/system/compile_test', ['ember-template-compiler/system/compile', 'htmlbars-compiler/compiler'], function (compile, compiler) { 'use strict'; // jscs:disable validateIndentation QUnit.module('ember-htmlbars: compile'); QUnit.test('compiles the provided template with htmlbars', function() { var templateString = "{{foo}} -- {{some-bar blah='foo'}}"; var actual = compile['default'](templateString); var expected = compiler.compile(templateString); equal(actual.toString(), expected.toString(), 'compile function matches content with htmlbars compile'); }); QUnit.test('calls template on the compiled function', function() { var templateString = "{{foo}} -- {{some-bar blah='foo'}}"; var actual = compile['default'](templateString); ok(actual.isTop, 'sets isTop via template function'); ok(actual.isMethod === false, 'sets isMethod via template function'); }); // jscs:enable validateIndentation }); enifed('ember-template-compiler/tests/system/compile_test.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/tests/system'); test('ember-template-compiler/tests/system/compile_test.js should pass jscs', function() { ok(true, 'ember-template-compiler/tests/system/compile_test.js should pass jscs.'); }); }); enifed('ember-template-compiler/tests/system/compile_test.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/tests/system'); test('ember-template-compiler/tests/system/compile_test.js should pass jshint', function() { ok(true, 'ember-template-compiler/tests/system/compile_test.js should pass jshint.'); }); }); enifed('ember-template-compiler/tests/system/template_test', ['ember-template-compiler/system/template'], function (template) { 'use strict'; // jscs:disable validateIndentation QUnit.module('ember-htmlbars: template'); QUnit.test('sets `isTop` on the provided function', function() { function test() { } template['default'](test); equal(test.isTop, true, 'sets isTop on the provided function'); }); QUnit.test('sets `isMethod` on the provided function', function() { function test() { } template['default'](test); equal(test.isMethod, false, 'sets isMethod on the provided function'); }); // jscs:enable validateIndentation }); enifed('ember-template-compiler/tests/system/template_test.jscs-test', function () { 'use strict'; module('JSCS - ember-template-compiler/tests/system'); test('ember-template-compiler/tests/system/template_test.js should pass jscs', function() { ok(true, 'ember-template-compiler/tests/system/template_test.js should pass jscs.'); }); }); enifed('ember-template-compiler/tests/system/template_test.jshint', function () { 'use strict'; module('JSHint - ember-template-compiler/tests/system'); test('ember-template-compiler/tests/system/template_test.js should pass jshint', function() { ok(true, 'ember-template-compiler/tests/system/template_test.js should pass jshint.'); }); }); enifed('ember-testing.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-testing.js should pass jscs', function() { ok(true, 'ember-testing.js should pass jscs.'); }); }); enifed('ember-testing.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-testing.js should pass jshint', function() { ok(true, 'ember-testing.js should pass jshint.'); }); }); enifed('ember-testing/adapters/adapter.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/adapters'); test('ember-testing/adapters/adapter.js should pass jscs', function() { ok(true, 'ember-testing/adapters/adapter.js should pass jscs.'); }); }); enifed('ember-testing/adapters/adapter.jshint', function () { 'use strict'; module('JSHint - ember-testing/adapters'); test('ember-testing/adapters/adapter.js should pass jshint', function() { ok(true, 'ember-testing/adapters/adapter.js should pass jshint.'); }); }); enifed('ember-testing/adapters/qunit.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/adapters'); test('ember-testing/adapters/qunit.js should pass jscs', function() { ok(true, 'ember-testing/adapters/qunit.js should pass jscs.'); }); }); enifed('ember-testing/adapters/qunit.jshint', function () { 'use strict'; module('JSHint - ember-testing/adapters'); test('ember-testing/adapters/qunit.js should pass jshint', function() { ok(true, 'ember-testing/adapters/qunit.js should pass jshint.'); }); }); enifed('ember-testing/helpers.jscs-test', function () { 'use strict'; module('JSCS - ember-testing'); test('ember-testing/helpers.js should pass jscs', function() { ok(true, 'ember-testing/helpers.js should pass jscs.'); }); }); enifed('ember-testing/helpers.jshint', function () { 'use strict'; module('JSHint - ember-testing'); test('ember-testing/helpers.js should pass jshint', function() { ok(true, 'ember-testing/helpers.js should pass jshint.'); }); }); enifed('ember-testing/initializers.jscs-test', function () { 'use strict'; module('JSCS - ember-testing'); test('ember-testing/initializers.js should pass jscs', function() { ok(true, 'ember-testing/initializers.js should pass jscs.'); }); }); enifed('ember-testing/initializers.jshint', function () { 'use strict'; module('JSHint - ember-testing'); test('ember-testing/initializers.js should pass jshint', function() { ok(true, 'ember-testing/initializers.js should pass jshint.'); }); }); enifed('ember-testing/setup_for_testing.jscs-test', function () { 'use strict'; module('JSCS - ember-testing'); test('ember-testing/setup_for_testing.js should pass jscs', function() { ok(true, 'ember-testing/setup_for_testing.js should pass jscs.'); }); }); enifed('ember-testing/setup_for_testing.jshint', function () { 'use strict'; module('JSHint - ember-testing'); test('ember-testing/setup_for_testing.js should pass jshint', function() { ok(true, 'ember-testing/setup_for_testing.js should pass jshint.'); }); }); enifed('ember-testing/support.jscs-test', function () { 'use strict'; module('JSCS - ember-testing'); test('ember-testing/support.js should pass jscs', function() { ok(true, 'ember-testing/support.js should pass jscs.'); }); }); enifed('ember-testing/support.jshint', function () { 'use strict'; module('JSHint - ember-testing'); test('ember-testing/support.js should pass jshint', function() { ok(true, 'ember-testing/support.js should pass jshint.'); }); }); enifed('ember-testing/test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing'); test('ember-testing/test.js should pass jscs', function() { ok(true, 'ember-testing/test.js should pass jscs.'); }); }); enifed('ember-testing/test.jshint', function () { 'use strict'; module('JSHint - ember-testing'); test('ember-testing/test.js should pass jshint', function() { ok(true, 'ember-testing/test.js should pass jshint.'); }); }); enifed('ember-testing/tests/acceptance_test', ['ember-metal/run_loop', 'ember-views/system/jquery', 'ember-testing/test', 'ember-testing/adapters/qunit', 'ember-views/views/view', 'ember-testing/initializers', 'ember-application/system/application', 'ember-routing/system/route', 'ember-template-compiler/system/compile', 'ember-routing'], function (run, jQuery, Test, QUnitAdapter, EmberView, __dep5__, EmberApplication, EmberRoute, compile) { 'use strict'; var App, find, click, fillIn, currentRoute, visit, originalAdapter, andThen, indexHitCount; QUnit.module("ember-testing Acceptance", { setup: function() { jQuery['default']('').appendTo('head'); jQuery['default']('
    ').appendTo('body'); run['default'](function() { indexHitCount = 0; App = EmberApplication['default'].create({ rootElement: '#ember-testing' }); App.Router.map(function() { this.route('posts'); this.route('comments'); this.route('abort_transition'); }); App.IndexRoute = EmberRoute['default'].extend({ model: function() { indexHitCount += 1; } }); App.PostsRoute = EmberRoute['default'].extend({ renderTemplate: function() { currentRoute = 'posts'; this._super.apply(this, arguments); } }); App.PostsView = EmberView['default'].extend({ defaultTemplate: compile['default']("
    {{#link-to 'comments'}}Comments{{/link-to}}
    "), classNames: ['posts-view'] }); App.CommentsRoute = EmberRoute['default'].extend({ renderTemplate: function() { currentRoute = 'comments'; this._super.apply(this, arguments); } }); App.CommentsView = EmberView['default'].extend({ defaultTemplate: compile['default']('{{input type="text"}}') }); App.AbortTransitionRoute = EmberRoute['default'].extend({ beforeModel: function(transition) { transition.abort(); } }); App.setupForTesting(); }); Test['default'].registerAsyncHelper('slowHelper', function() { return Test['default'].promise(function(resolve) { setTimeout(resolve, 10); }); }); App.injectTestHelpers(); find = window.find; click = window.click; fillIn = window.fillIn; visit = window.visit; andThen = window.andThen; originalAdapter = Test['default'].adapter; }, teardown: function() { App.removeTestHelpers(); Test['default'].unregisterHelper('slowHelper'); jQuery['default']('#ember-testing-container, #ember-testing').remove(); run['default'](App, App.destroy); App = null; Test['default'].adapter = originalAdapter; indexHitCount = 0; } }); QUnit.test("helpers can be chained with then", function() { expect(5); currentRoute = 'index'; visit('/posts').then(function() { equal(currentRoute, 'posts', "Successfully visited posts route"); return click('a:contains("Comments")'); }).then(function() { equal(currentRoute, 'comments', "visit chained with click"); return fillIn('.ember-text-field', "yeah"); }).then(function() { equal(jQuery['default']('.ember-text-field').val(), 'yeah', "chained with fillIn"); return fillIn('.ember-text-field', '#ember-testing-container', "context working"); }).then(function() { equal(jQuery['default']('.ember-text-field').val(), 'context working', "chained with fillIn"); return click(".does-not-exist"); }).then(null, function(e) { equal(e.message, "Element .does-not-exist not found.", "Non-existent click exception caught"); }); }); // Keep this for backwards compatibility QUnit.test("helpers can be chained to each other", function() { expect(5); currentRoute = 'index'; visit('/posts') .click('a:first', '#comments-link') .fillIn('.ember-text-field', "hello") .then(function() { equal(currentRoute, 'comments', "Successfully visited comments route"); equal(jQuery['default']('.ember-text-field').val(), 'hello', "Fillin successfully works"); find('.ember-text-field').one('keypress', function(e) { equal(e.keyCode, 13, "keyevent chained with correct keyCode."); equal(e.which, 13, "keyevent chained with correct which."); }); }) .keyEvent('.ember-text-field', 'keypress', 13) .visit('/posts') .then(function() { equal(currentRoute, 'posts', "Thens can also be chained to helpers"); }); }); QUnit.test("helpers don't need to be chained", function() { expect(3); currentRoute = 'index'; visit('/posts'); click('a:first', '#comments-link'); fillIn('.ember-text-field', "hello"); andThen(function() { equal(currentRoute, 'comments', "Successfully visited comments route"); equal(find('.ember-text-field').val(), 'hello', "Fillin successfully works"); }); visit('/posts'); andThen(function() { equal(currentRoute, 'posts'); }); }); QUnit.test("Nested async helpers", function() { expect(3); currentRoute = 'index'; visit('/posts'); andThen(function() { click('a:first', '#comments-link'); fillIn('.ember-text-field', "hello"); }); andThen(function() { equal(currentRoute, 'comments', "Successfully visited comments route"); equal(find('.ember-text-field').val(), 'hello', "Fillin successfully works"); }); visit('/posts'); andThen(function() { equal(currentRoute, 'posts'); }); }); QUnit.test("Multiple nested async helpers", function() { expect(2); visit('/posts'); andThen(function() { click('a:first', '#comments-link'); fillIn('.ember-text-field', "hello"); fillIn('.ember-text-field', "goodbye"); }); andThen(function() { equal(find('.ember-text-field').val(), 'goodbye', "Fillin successfully works"); equal(currentRoute, 'comments', "Successfully visited comments route"); }); }); QUnit.test("Helpers nested in thens", function() { expect(3); currentRoute = 'index'; visit('/posts').then(function() { click('a:first', '#comments-link'); }); andThen(function() { fillIn('.ember-text-field', "hello"); }); andThen(function() { equal(currentRoute, 'comments', "Successfully visited comments route"); equal(find('.ember-text-field').val(), 'hello', "Fillin successfully works"); }); visit('/posts'); andThen(function() { equal(currentRoute, 'posts'); }); }); QUnit.test("Aborted transitions are not logged via Ember.Test.adapter#exception", function () { expect(0); Test['default'].adapter = QUnitAdapter['default'].create({ exception: function(error) { ok(false, "aborted transitions are not logged"); } }); visit("/abort_transition"); }); QUnit.test("Unhandled exceptions are logged via Ember.Test.adapter#exception", function () { expect(2); var asyncHandled; Test['default'].adapter = QUnitAdapter['default'].create({ exception: function(error) { equal(error.message, "Element .does-not-exist not found.", "Exception successfully caught and passed to Ember.Test.adapter.exception"); asyncHandled['catch'](function() { }); // handle the rejection so it doesn't leak later. } }); visit('/posts'); click(".invalid-element").then(null, function(error) { equal(error.message, "Element .invalid-element not found.", "Exception successfully handled in the rejection handler"); }); asyncHandled = click(".does-not-exist"); }); QUnit.test("Unhandled exceptions in `andThen` are logged via Ember.Test.adapter#exception", function () { expect(1); Test['default'].adapter = QUnitAdapter['default'].create({ exception: function(error) { equal(error.message, "Catch me", "Exception successfully caught and passed to Ember.Test.adapter.exception"); } }); visit('/posts'); andThen(function() { throw new Error('Catch me'); }); }); QUnit.test("should not start routing on the root URL when visiting another", function() { visit('/posts'); andThen(function() { ok(find('#comments-link'), 'found comments-link'); equal(currentRoute, 'posts', "Successfully visited posts route"); equal(indexHitCount, 0, 'should not hit index route when visiting another route'); }); }); QUnit.test("only enters the index route once when visiting /", function() { visit('/'); andThen(function() { equal(indexHitCount, 1, 'should hit index once when visiting /'); }); }); QUnit.test("test must not finish while asyncHelpers are pending", function () { var async = 0; var innerRan = false; Test['default'].adapter = QUnitAdapter['default'].extend({ asyncStart: function() { async++; this._super(); }, asyncEnd: function() { async--; this._super(); } }).create(); App.testHelpers.slowHelper(); andThen(function() { innerRan = true; }); equal(innerRan, false, 'should not have run yet'); ok(async > 0, 'should have told the adapter to pause'); if (async === 0) { // If we failed the test, prevent zalgo from escaping and breaking // our other tests. Test['default'].adapter.asyncStart(); Test['default'].resolve().then(function() { Test['default'].adapter.asyncEnd(); }); } }); }); enifed('ember-testing/tests/acceptance_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests'); test('ember-testing/tests/acceptance_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/acceptance_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/acceptance_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests'); test('ember-testing/tests/acceptance_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/acceptance_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/adapters/adapter_test', ['ember-metal/run_loop', 'ember-testing/adapters/adapter'], function (run, Adapter) { 'use strict'; var adapter; QUnit.module("ember-testing Adapter", { setup: function() { adapter = new Adapter['default'](); }, teardown: function() { run['default'](adapter, adapter.destroy); } }); // Can't test these this way anymore since we have nothing to compare to // test("asyncStart is a noop", function() { // equal(adapter.asyncStart, K); // }); // test("asyncEnd is a noop", function() { // equal(adapter.asyncEnd, K); // }); QUnit.test("exception throws", function() { var error = "Hai"; var thrown; try { adapter.exception(error); } catch (e) { thrown = e; } equal(thrown, error); }); }); enifed('ember-testing/tests/adapters/adapter_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests/adapters'); test('ember-testing/tests/adapters/adapter_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/adapters/adapter_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/adapters/adapter_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests/adapters'); test('ember-testing/tests/adapters/adapter_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/adapters/adapter_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/adapters/qunit_test', ['ember-metal/run_loop', 'ember-testing/adapters/qunit'], function (run, QUnitAdapter) { 'use strict'; var adapter; QUnit.module("ember-testing QUnitAdapter", { setup: function() { adapter = new QUnitAdapter['default'](); }, teardown: function() { run['default'](adapter, adapter.destroy); } }); QUnit.test("asyncStart calls stop", function() { var originalStop = QUnit.stop; try { QUnit.stop = function() { ok(true, "stop called"); }; adapter.asyncStart(); } finally { QUnit.stop = originalStop; } }); QUnit.test("asyncEnd calls start", function() { var originalStart = QUnit.start; try { QUnit.start = function() { ok(true, "start called"); }; adapter.asyncEnd(); } finally { QUnit.start = originalStart; } }); QUnit.test("exception causes a failing assertion", function() { var error = { err: 'hai' }; var originalOk = window.ok; try { window.ok = function(val, msg) { originalOk(!val, "ok is called with false"); originalOk(msg, '{err: "hai"}'); }; adapter.exception(error); } finally { window.ok = originalOk; } }); }); enifed('ember-testing/tests/adapters/qunit_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests/adapters'); test('ember-testing/tests/adapters/qunit_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/adapters/qunit_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/adapters/qunit_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests/adapters'); test('ember-testing/tests/adapters/qunit_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/adapters/qunit_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/adapters_test', ['ember-metal/run_loop', 'ember-testing/test', 'ember-testing/adapters/adapter', 'ember-testing/adapters/qunit', 'ember-application/system/application'], function (run, Test, Adapter, QUnitAdapter, EmberApplication) { 'use strict'; var App, originalAdapter; QUnit.module("ember-testing Adapters", { setup: function() { originalAdapter = Test['default'].adapter; }, teardown: function() { run['default'](App, App.destroy); App.removeTestHelpers(); App = null; Test['default'].adapter = originalAdapter; } }); QUnit.test("Setting a test adapter manually", function() { expect(1); var CustomAdapter; CustomAdapter = Adapter['default'].extend({ asyncStart: function() { ok(true, "Correct adapter was used"); } }); run['default'](function() { App = EmberApplication['default'].create(); Test['default'].adapter = CustomAdapter.create(); App.setupForTesting(); }); Test['default'].adapter.asyncStart(); }); QUnit.test("QUnitAdapter is used by default", function() { expect(1); Test['default'].adapter = null; run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); ok(Test['default'].adapter instanceof QUnitAdapter['default']); }); }); enifed('ember-testing/tests/adapters_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests'); test('ember-testing/tests/adapters_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/adapters_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/adapters_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests'); test('ember-testing/tests/adapters_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/adapters_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/helper_registration_test', ['ember-metal/run_loop', 'ember-testing/test', 'ember-application/system/application'], function (run, Test, EmberApplication) { 'use strict'; var App, appBooted, helperContainer; function registerHelper() { Test['default'].registerHelper('boot', function(app) { run['default'](app, app.advanceReadiness); appBooted = true; return app.testHelpers.wait(); }); } function unregisterHelper() { Test['default'].unregisterHelper('boot'); } var originalAdapter = Test['default'].adapter; function setupApp() { appBooted = false; helperContainer = {}; run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); App.injectTestHelpers(helperContainer); }); } function destroyApp() { if (App) { run['default'](App, 'destroy'); App = null; } } QUnit.module("Test - registerHelper/unregisterHelper", { teardown: function() { Test['default'].adapter = originalAdapter; destroyApp(); } }); QUnit.test("Helper gets registered", function() { expect(2); registerHelper(); setupApp(); ok(App.testHelpers.boot); ok(helperContainer.boot); }); QUnit.test("Helper is ran when called", function() { expect(1); registerHelper(); setupApp(); App.testHelpers.boot().then(function() { ok(appBooted); }); }); QUnit.test("Helper can be unregistered", function() { expect(4); registerHelper(); setupApp(); ok(App.testHelpers.boot); ok(helperContainer.boot); unregisterHelper(); setupApp(); ok(!App.testHelpers.boot, "once unregistered the helper is not added to App.testHelpers"); ok(!helperContainer.boot, "once unregistered the helper is not added to the helperContainer"); }); }); enifed('ember-testing/tests/helper_registration_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests'); test('ember-testing/tests/helper_registration_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/helper_registration_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/helper_registration_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests'); test('ember-testing/tests/helper_registration_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/helper_registration_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/helpers_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-runtime/ext/rsvp', 'ember-views/views/view', 'ember-views/system/jquery', 'ember-testing/test', 'ember-testing/helpers', 'ember-testing/initializers', 'ember-testing/setup_for_testing', 'ember-routing/system/router', 'ember-routing/system/route', 'ember-application/system/application', 'ember-template-compiler/system/compile'], function (Ember, run, EmberObject, RSVP, EmberView, jQuery, Test, __dep7__, __dep8__, setupForTesting, EmberRouter, EmberRoute, EmberApplication, compile) { 'use strict'; var App; var originalAdapter = Test['default'].adapter; function cleanup() { // Teardown setupForTesting Test['default'].adapter = originalAdapter; run['default'](function() { jQuery['default'](document).off('ajaxSend'); jQuery['default'](document).off('ajaxComplete'); }); Test['default'].pendingAjaxRequests = null; Test['default'].waiters = null; // Other cleanup if (App) { run['default'](App, App.destroy); App.removeTestHelpers(); App = null; } Ember['default'].TEMPLATES = {}; } function assertHelpers(application, helperContainer, expected) { if (!helperContainer) { helperContainer = window; } if (expected === undefined) { expected = true; } function checkHelperPresent(helper, expected) { var presentInHelperContainer = !!helperContainer[helper]; var presentInTestHelpers = !!application.testHelpers[helper]; ok(presentInHelperContainer === expected, "Expected '" + helper + "' to be present in the helper container (defaults to window)."); ok(presentInTestHelpers === expected, "Expected '" + helper + "' to be present in App.testHelpers."); } checkHelperPresent('visit', expected); checkHelperPresent('click', expected); checkHelperPresent('keyEvent', expected); checkHelperPresent('fillIn', expected); checkHelperPresent('wait', expected); checkHelperPresent('triggerEvent', expected); if (Ember['default'].FEATURES.isEnabled('ember-testing-checkbox-helpers')) { checkHelperPresent('check', expected); checkHelperPresent('uncheck', expected); } } function assertNoHelpers(application, helperContainer) { assertHelpers(application, helperContainer, false); } function currentRouteName(app) { return app.testHelpers.currentRouteName(); } function currentPath(app) { return app.testHelpers.currentPath(); } function currentURL(app) { return app.testHelpers.currentURL(); } function setupApp() { run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); App.injectTestHelpers(); }); } QUnit.module("ember-testing: Helper setup", { setup: function() { cleanup(); }, teardown: function() { cleanup(); } }); QUnit.test("Ember.Application#injectTestHelpers/#removeTestHelpers", function() { App = run['default'](EmberApplication['default'], EmberApplication['default'].create); assertNoHelpers(App); App.injectTestHelpers(); assertHelpers(App); App.removeTestHelpers(); assertNoHelpers(App); }); QUnit.test("Ember.Application#setupForTesting", function() { run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); equal(App.__container__.lookup('router:main').location.implementation, 'none'); }); QUnit.test("Ember.Application.setupForTesting sets the application to `testing`.", function() { run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); equal(App.testing, true, "Application instance is set to testing."); }); QUnit.test("Ember.Application.setupForTesting leaves the system in a deferred state.", function() { run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); equal(App._readinessDeferrals, 1, "App is in deferred state after setupForTesting."); }); QUnit.test("App.reset() after Application.setupForTesting leaves the system in a deferred state.", function() { run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); equal(App._readinessDeferrals, 1, "App is in deferred state after setupForTesting."); App.reset(); equal(App._readinessDeferrals, 1, "App is in deferred state after setupForTesting."); }); QUnit.test("Ember.Application#setupForTesting attaches ajax listeners", function() { var documentEvents; documentEvents = jQuery['default']._data(document, 'events'); if (!documentEvents) { documentEvents = {}; } ok(documentEvents['ajaxSend'] === undefined, 'there are no ajaxSend listers setup prior to calling injectTestHelpers'); ok(documentEvents['ajaxComplete'] === undefined, 'there are no ajaxComplete listers setup prior to calling injectTestHelpers'); run['default'](function() { setupForTesting['default'](); }); documentEvents = jQuery['default']._data(document, 'events'); equal(documentEvents['ajaxSend'].length, 1, 'calling injectTestHelpers registers an ajaxSend handler'); equal(documentEvents['ajaxComplete'].length, 1, 'calling injectTestHelpers registers an ajaxComplete handler'); }); QUnit.test("Ember.Application#setupForTesting attaches ajax listeners only once", function() { var documentEvents; documentEvents = jQuery['default']._data(document, 'events'); if (!documentEvents) { documentEvents = {}; } ok(documentEvents['ajaxSend'] === undefined, 'there are no ajaxSend listeners setup prior to calling injectTestHelpers'); ok(documentEvents['ajaxComplete'] === undefined, 'there are no ajaxComplete listeners setup prior to calling injectTestHelpers'); run['default'](function() { setupForTesting['default'](); }); run['default'](function() { setupForTesting['default'](); }); documentEvents = jQuery['default']._data(document, 'events'); equal(documentEvents['ajaxSend'].length, 1, 'calling injectTestHelpers registers an ajaxSend handler'); equal(documentEvents['ajaxComplete'].length, 1, 'calling injectTestHelpers registers an ajaxComplete handler'); }); QUnit.test("Ember.Application#injectTestHelpers calls callbacks registered with onInjectHelpers", function() { var injected = 0; Test['default'].onInjectHelpers(function() { injected++; }); run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); equal(injected, 0, 'onInjectHelpers are not called before injectTestHelpers'); App.injectTestHelpers(); equal(injected, 1, 'onInjectHelpers are called after injectTestHelpers'); }); QUnit.test("Ember.Application#injectTestHelpers adds helpers to provided object.", function() { var helpers = {}; run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); App.injectTestHelpers(helpers); assertHelpers(App, helpers); App.removeTestHelpers(); assertNoHelpers(App, helpers); }); QUnit.test("Ember.Application#removeTestHelpers resets the helperContainer's original values", function() { var helpers = { visit: 'snazzleflabber' }; run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); }); App.injectTestHelpers(helpers); ok(helpers.visit !== 'snazzleflabber', "helper added to container"); App.removeTestHelpers(); ok(helpers.visit === 'snazzleflabber', "original value added back to container"); }); QUnit.module("ember-testing: Helper methods", { setup: function() { setupApp(); }, teardown: function() { cleanup(); } }); QUnit.test("`wait` respects registerWaiters", function() { expect(3); var counter=0; function waiter() { return ++counter > 2; } var other=0; function otherWaiter() { return ++other > 2; } run['default'](App, App.advanceReadiness); Test['default'].registerWaiter(waiter); Test['default'].registerWaiter(otherWaiter); App.testHelpers.wait().then(function() { equal(waiter(), true, 'should not resolve until our waiter is ready'); Test['default'].unregisterWaiter(waiter); equal(Test['default'].waiters.length, 1, 'should not leave the waiter registered'); other = 0; return App.testHelpers.wait(); }).then(function() { equal(otherWaiter(), true, 'other waiter is still registered'); }); }); QUnit.test("`visit` advances readiness.", function() { expect(2); equal(App._readinessDeferrals, 1, "App is in deferred state after setupForTesting."); App.testHelpers.visit('/').then(function() { equal(App._readinessDeferrals, 0, "App's readiness was advanced by visit."); }); }); QUnit.test("`wait` helper can be passed a resolution value", function() { expect(4); var promise, wait; promise = new RSVP['default'].Promise(function(resolve) { run['default'](null, resolve, 'promise'); }); run['default'](App, App.advanceReadiness); wait = App.testHelpers.wait; wait('text').then(function(val) { equal(val, 'text', 'can resolve to a string'); return wait(1); }).then(function(val) { equal(val, 1, 'can resolve to an integer'); return wait({ age: 10 }); }).then(function(val) { deepEqual(val, { age: 10 }, 'can resolve to an object'); return wait(promise); }).then(function(val) { equal(val, 'promise', 'can resolve to a promise resolution value'); }); }); QUnit.test("`click` triggers appropriate events in order", function() { expect(5); var click, wait, events; App.IndexView = EmberView['default'].extend({ classNames: 'index-view', didInsertElement: function() { this.$().on('mousedown focusin mouseup click', function(e) { events.push(e.type); }); }, Checkbox: Ember['default'].Checkbox.extend({ click: function() { events.push('click:' + this.get('checked')); }, change: function() { events.push('change:' + this.get('checked')); } }) }); Ember['default'].TEMPLATES.index = compile['default']('{{input type="text"}} {{view view.Checkbox}} {{textarea}}
    '); run['default'](App, App.advanceReadiness); click = App.testHelpers.click; wait = App.testHelpers.wait; wait().then(function() { events = []; return click('.index-view'); }).then(function() { deepEqual(events, ['mousedown', 'mouseup', 'click'], 'fires events in order'); }).then(function() { events = []; return click('.index-view input[type=text]'); }).then(function() { deepEqual(events, ['mousedown', 'focusin', 'mouseup', 'click'], 'fires focus events on inputs'); }).then(function() { events = []; return click('.index-view textarea'); }).then(function() { deepEqual(events, ['mousedown', 'focusin', 'mouseup', 'click'], 'fires focus events on textareas'); }).then(function() { events = []; return click('.index-view div'); }).then(function() { deepEqual(events, ['mousedown', 'focusin', 'mouseup', 'click'], 'fires focus events on contenteditable'); }).then(function() { // In IE (< 8), the change event only fires when the value changes before element focused. jQuery['default']('.index-view input[type=checkbox]').focus(); events = []; return click('.index-view input[type=checkbox]'); }).then(function() { // i.e. mousedown, mouseup, change:true, click, click:true // Firefox differs so we can't assert the exact ordering here. // See https://bugzilla.mozilla.org/show_bug.cgi?id=843554. equal(events.length, 5, 'fires click and change on checkboxes'); }); }); QUnit.test("`wait` waits for outstanding timers", function() { expect(1); var wait_done = false; run['default'](App, App.advanceReadiness); run['default'].later(this, function() { wait_done = true; }, 500); App.testHelpers.wait().then(function() { equal(wait_done, true, 'should wait for the timer to be fired.'); }); }); QUnit.test("`wait` respects registerWaiters with optional context", function() { expect(3); var obj = { counter: 0, ready: function() { return ++this.counter > 2; } }; var other=0; function otherWaiter() { return ++other > 2; } run['default'](App, App.advanceReadiness); Test['default'].registerWaiter(obj, obj.ready); Test['default'].registerWaiter(otherWaiter); App.testHelpers.wait().then(function() { equal(obj.ready(), true, 'should not resolve until our waiter is ready'); Test['default'].unregisterWaiter(obj, obj.ready); equal(Test['default'].waiters.length, 1, 'should not leave the waiter registered'); return App.testHelpers.wait(); }).then(function() { equal(otherWaiter(), true, 'other waiter should still be registered'); }); }); QUnit.test("`wait` does not error if routing has not begun", function() { expect(1); App.testHelpers.wait().then(function() { ok(true, 'should not error without `visit`'); }); }); QUnit.test("`triggerEvent accepts an optional options hash without context", function() { expect(3); var triggerEvent, wait, event; App.IndexView = EmberView['default'].extend({ template: compile['default']('{{input type="text" id="scope" class="input"}}'), didInsertElement: function() { this.$('.input').on('blur change', function(e) { event = e; }); } }); run['default'](App, App.advanceReadiness); triggerEvent = App.testHelpers.triggerEvent; wait = App.testHelpers.wait; wait().then(function() { return triggerEvent('.input', 'blur', { keyCode: 13 }); }).then(function() { equal(event.keyCode, 13, 'options were passed'); equal(event.type, 'blur', 'correct event was triggered'); equal(event.target.getAttribute('id'), 'scope', 'triggered on the correct element'); }); }); QUnit.test("`triggerEvent can limit searching for a selector to a scope", function() { expect(2); var triggerEvent, wait, event; App.IndexView = EmberView['default'].extend({ template: compile['default']('{{input type="text" id="outside-scope" class="input"}}
    {{input type="text" id="inside-scope" class="input"}}
    '), didInsertElement: function() { this.$('.input').on('blur change', function(e) { event = e; }); } }); run['default'](App, App.advanceReadiness); triggerEvent = App.testHelpers.triggerEvent; wait = App.testHelpers.wait; wait().then(function() { return triggerEvent('.input', '#limited', 'blur'); }).then(function() { equal(event.type, 'blur', 'correct event was triggered'); equal(event.target.getAttribute('id'), 'inside-scope', 'triggered on the correct element'); }); }); QUnit.test("`triggerEvent` can be used to trigger arbitrary events", function() { expect(2); var triggerEvent, wait, event; App.IndexView = EmberView['default'].extend({ template: compile['default']('{{input type="text" id="foo"}}'), didInsertElement: function() { this.$('#foo').on('blur change', function(e) { event = e; }); } }); run['default'](App, App.advanceReadiness); triggerEvent = App.testHelpers.triggerEvent; wait = App.testHelpers.wait; wait().then(function() { return triggerEvent('#foo', 'blur'); }).then(function() { equal(event.type, 'blur', 'correct event was triggered'); equal(event.target.getAttribute('id'), 'foo', 'triggered on the correct element'); }); }); QUnit.test("`fillIn` takes context into consideration", function() { expect(2); var fillIn, find, visit, andThen; App.IndexView = EmberView['default'].extend({ template: compile['default']('
    {{input type="text" id="first" class="current"}}
    {{input type="text" id="second" class="current"}}') }); run['default'](App, App.advanceReadiness); fillIn = App.testHelpers.fillIn; find = App.testHelpers.find; visit = App.testHelpers.visit; andThen = App.testHelpers.andThen; visit('/'); fillIn('.current', '#parent', 'current value'); andThen(function() { equal(find('#first').val(), 'current value'); equal(find('#second').val(), ''); }); }); QUnit.test("`fillIn` focuses on the element", function() { expect(2); var fillIn, find, visit, andThen; App.ApplicationRoute = Ember['default'].Route.extend({ actions: { wasFocused: function() { ok(true, 'focusIn event was triggered'); } } }); App.IndexView = EmberView['default'].extend({ template: compile['default']('
    {{input type="text" id="first" focus-in="wasFocused"}}
    ') }); run['default'](App, App.advanceReadiness); fillIn = App.testHelpers.fillIn; find = App.testHelpers.find; visit = App.testHelpers.visit; andThen = App.testHelpers.andThen; visit('/'); fillIn('#first', 'current value'); andThen(function() { equal(find('#first').val(), 'current value'); }); }); if (Ember['default'].FEATURES.isEnabled('ember-testing-checkbox-helpers')) { QUnit.test("`check` ensures checkboxes are `checked` state for checkboxes", function() { expect(2); var check, find, visit, andThen; App.IndexView = EmberView['default'].extend({ template: compile['default']('') }); run['default'](App, App.advanceReadiness); check = App.testHelpers.check; find = App.testHelpers.find; visit = App.testHelpers.visit; andThen = App.testHelpers.andThen; visit('/'); check('#unchecked'); check('#checked'); andThen(function() { equal(find('#unchecked').is(":checked"), true, "can check an unchecked checkbox"); equal(find('#checked').is(":checked"), true, "can check a checked checkbox"); }); }); QUnit.test("`uncheck` ensures checkboxes are not `checked`", function() { expect(2); var uncheck, find, visit, andThen; App.IndexView = EmberView['default'].extend({ template: compile['default']('') }); run['default'](App, App.advanceReadiness); uncheck = App.testHelpers.uncheck; find = App.testHelpers.find; visit = App.testHelpers.visit; andThen = App.testHelpers.andThen; visit('/'); uncheck('#unchecked'); uncheck('#checked'); andThen(function() { equal(find('#unchecked').is(":checked"), false, "can uncheck an unchecked checkbox"); equal(find('#checked').is(":checked"), false, "can uncheck a checked checkbox"); }); }); QUnit.test("`check` asserts the selected inputs are checkboxes", function() { var check, visit; App.IndexView = EmberView['default'].extend({ template: compile['default']('') }); run['default'](App, App.advanceReadiness); check = App.testHelpers.check; visit = App.testHelpers.visit; visit('/').then(function() { expectAssertion(function() { check('#text'); }, /must be a checkbox/); }); }); QUnit.test("`uncheck` asserts the selected inputs are checkboxes", function() { var visit, uncheck; App.IndexView = EmberView['default'].extend({ template: compile['default']('') }); run['default'](App, App.advanceReadiness); visit = App.testHelpers.visit; uncheck = App.testHelpers.uncheck; visit('/').then(function() { expectAssertion(function() { uncheck('#text'); }, /must be a checkbox/); }); }); } QUnit.test("`triggerEvent accepts an optional options hash and context", function() { expect(3); var triggerEvent, wait, event; App.IndexView = EmberView['default'].extend({ template: compile['default']('{{input type="text" id="outside-scope" class="input"}}
    {{input type="text" id="inside-scope" class="input"}}
    '), didInsertElement: function() { this.$('.input').on('blur change', function(e) { event = e; }); } }); run['default'](App, App.advanceReadiness); triggerEvent = App.testHelpers.triggerEvent; wait = App.testHelpers.wait; wait().then(function() { return triggerEvent('.input', '#limited', 'blur', { keyCode: 13 }); }).then(function() { equal(event.keyCode, 13, 'options were passed'); equal(event.type, 'blur', 'correct event was triggered'); equal(event.target.getAttribute('id'), 'inside-scope', 'triggered on the correct element'); }); }); QUnit.module("ember-testing debugging helpers", { setup: function() { setupApp(); run['default'](function() { App.Router = EmberRouter['default'].extend({ location: 'none' }); }); run['default'](App, 'advanceReadiness'); }, teardown: function() { cleanup(); } }); QUnit.test("pauseTest pauses", function() { expect(1); function fakeAdapterAsyncStart() { ok(true, 'Async start should be called'); } Test['default'].adapter.asyncStart = fakeAdapterAsyncStart; App.testHelpers.pauseTest(); }); QUnit.module("ember-testing routing helpers", { setup: function() { run['default'](function() { App = EmberApplication['default'].create(); App.setupForTesting(); App.injectTestHelpers(); App.Router = EmberRouter['default'].extend({ location: 'none' }); App.Router.map(function() { this.resource("posts", function() { this.route("new"); }); }); }); run['default'](App, 'advanceReadiness'); }, teardown: function() { cleanup(); } }); QUnit.test("currentRouteName for '/'", function() { expect(3); App.testHelpers.visit('/').then(function() { equal(App.testHelpers.currentRouteName(), 'index', "should equal 'index'."); equal(App.testHelpers.currentPath(), 'index', "should equal 'index'."); equal(App.testHelpers.currentURL(), '/', "should equal '/'."); }); }); QUnit.test("currentRouteName for '/posts'", function() { expect(3); App.testHelpers.visit('/posts').then(function() { equal(App.testHelpers.currentRouteName(), 'posts.index', "should equal 'posts.index'."); equal(App.testHelpers.currentPath(), 'posts.index', "should equal 'posts.index'."); equal(App.testHelpers.currentURL(), '/posts', "should equal '/posts'."); }); }); QUnit.test("currentRouteName for '/posts/new'", function() { expect(3); App.testHelpers.visit('/posts/new').then(function() { equal(App.testHelpers.currentRouteName(), 'posts.new', "should equal 'posts.new'."); equal(App.testHelpers.currentPath(), 'posts.new', "should equal 'posts.new'."); equal(App.testHelpers.currentURL(), '/posts/new', "should equal '/posts/new'."); }); }); QUnit.module("ember-testing pendingAjaxRequests", { setup: function() { setupApp(); }, teardown: function() { cleanup(); } }); QUnit.test("pendingAjaxRequests is maintained for ajaxSend and ajaxComplete events", function() { equal(Test['default'].pendingAjaxRequests, 0); var xhr = { some: 'xhr' }; jQuery['default'](document).trigger('ajaxSend', xhr); equal(Test['default'].pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests was incremented'); jQuery['default'](document).trigger('ajaxComplete', xhr); equal(Test['default'].pendingAjaxRequests, 0, 'Ember.Test.pendingAjaxRequests was decremented'); }); QUnit.test("pendingAjaxRequests is ignores ajaxComplete events from past setupForTesting calls", function() { equal(Test['default'].pendingAjaxRequests, 0); var xhr = { some: 'xhr' }; jQuery['default'](document).trigger('ajaxSend', xhr); equal(Test['default'].pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests was incremented'); run['default'](function() { setupForTesting['default'](); }); equal(Test['default'].pendingAjaxRequests, 0, 'Ember.Test.pendingAjaxRequests was reset'); var altXhr = { some: 'more xhr' }; jQuery['default'](document).trigger('ajaxSend', altXhr); equal(Test['default'].pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests was incremented'); jQuery['default'](document).trigger('ajaxComplete', xhr); equal(Test['default'].pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests is not impressed with your unexpected complete'); }); QUnit.test("pendingAjaxRequests is reset by setupForTesting", function() { Test['default'].pendingAjaxRequests = 1; run['default'](function() { setupForTesting['default'](); }); equal(Test['default'].pendingAjaxRequests, 0, 'pendingAjaxRequests is reset'); }); QUnit.module("ember-testing async router", { setup: function() { cleanup(); run['default'](function() { App = EmberApplication['default'].create(); App.Router = EmberRouter['default'].extend({ location: 'none' }); App.Router.map(function() { this.resource("user", function() { this.route("profile"); this.route("edit"); }); }); App.UserRoute = EmberRoute['default'].extend({ model: function() { return resolveLater(); } }); App.UserProfileRoute = EmberRoute['default'].extend({ beforeModel: function() { var self = this; return resolveLater().then(function() { self.transitionTo('user.edit'); }); } }); // Emulates a long-running unscheduled async operation. function resolveLater() { var promise; run['default'](function() { promise = new RSVP['default'].Promise(function(resolve) { // The wait() helper has a 10ms tick. We should resolve() after at least one tick // to test whether wait() held off while the async router was still loading. 20ms // should be enough. setTimeout(function() { run['default'](function() { resolve(EmberObject['default'].create({ firstName: 'Tom' })); }); }, 20); }); }); return promise; } App.setupForTesting(); }); App.injectTestHelpers(); run['default'](App, 'advanceReadiness'); }, teardown: function() { cleanup(); } }); QUnit.test("currentRouteName for '/user'", function() { expect(4); App.testHelpers.visit('/user').then(function() { equal(currentRouteName(App), 'user.index', "should equal 'user.index'."); equal(currentPath(App), 'user.index', "should equal 'user.index'."); equal(currentURL(App), '/user', "should equal '/user'."); equal(App.__container__.lookup('route:user').get('controller.model.firstName'), 'Tom', "should equal 'Tom'."); }); }); QUnit.test("currentRouteName for '/user/profile'", function() { expect(4); App.testHelpers.visit('/user/profile').then(function() { equal(currentRouteName(App), 'user.edit', "should equal 'user.edit'."); equal(currentPath(App), 'user.edit', "should equal 'user.edit'."); equal(currentURL(App), '/user/edit', "should equal '/user/edit'."); equal(App.__container__.lookup('route:user').get('controller.model.firstName'), 'Tom', "should equal 'Tom'."); }); }); var originalVisitHelper, originalFindHelper, originalWaitHelper; QUnit.module('can override built-in helpers', { setup: function() { originalVisitHelper = Test['default']._helpers.visit; originalFindHelper = Test['default']._helpers.find; originalWaitHelper = Test['default']._helpers.wait; jQuery['default']('').appendTo('head'); jQuery['default']('
    ').appendTo('body'); run['default'](function() { App = Ember['default'].Application.create({ rootElement: '#ember-testing' }); App.setupForTesting(); }); }, teardown: function() { App.removeTestHelpers(); jQuery['default']('#ember-testing-container, #ember-testing').remove(); run['default'](App, App.destroy); App = null; Test['default']._helpers.visit = originalVisitHelper; Test['default']._helpers.find = originalFindHelper; Test['default']._helpers.wait = originalWaitHelper; } }); QUnit.test("can override visit helper", function() { expect(1); Test['default'].registerHelper('visit', function() { ok(true, 'custom visit helper was called'); }); App.injectTestHelpers(); App.testHelpers.visit(); }); QUnit.test("can override find helper", function() { expect(1); Test['default'].registerHelper('find', function() { ok(true, 'custom find helper was called'); return ['not empty array']; }); App.injectTestHelpers(); App.testHelpers.findWithAssert('.who-cares'); }); }); enifed('ember-testing/tests/helpers_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests'); test('ember-testing/tests/helpers_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/helpers_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/helpers_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests'); test('ember-testing/tests/helpers_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/helpers_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/integration_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-runtime/controllers/array_controller', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-testing/test', 'ember-routing/system/route', 'ember-application/system/application', 'ember-template-compiler/system/compile', 'ember-application'], function (Ember, run, EmberObject, ArrayController, jQuery, EmberView, Test, EmberRoute, EmberApplication, compile) { 'use strict'; var App, find, visit; var originalAdapter = Test['default'].adapter; QUnit.module("ember-testing Integration", { setup: function() { jQuery['default']('
    ').appendTo('body'); run['default'](function() { App = EmberApplication['default'].create({ rootElement: '#ember-testing' }); App.Router.map(function() { this.resource("people", { path: "/" }); }); App.PeopleRoute = EmberRoute['default'].extend({ model: function() { return App.Person.find(); } }); App.PeopleView = EmberView['default'].extend({ defaultTemplate: compile['default']("{{#each person in controller}}
    {{person.firstName}}
    {{/each}}") }); App.PeopleController = ArrayController['default'].extend({}); App.Person = EmberObject['default'].extend({ firstName: '' }); App.Person.reopenClass({ find: function() { return Ember['default'].A(); } }); App.ApplicationView = EmberView['default'].extend({ defaultTemplate: compile['default']("{{outlet}}") }); App.setupForTesting(); }); run['default'](function() { App.reset(); }); App.injectTestHelpers(); find = window.find; visit = window.visit; }, teardown: function() { App.removeTestHelpers(); jQuery['default']('#ember-testing-container, #ember-testing').remove(); run['default'](App, App.destroy); App = null; Test['default'].adapter = originalAdapter; } }); QUnit.test("template is bound to empty array of people", function() { App.Person.find = function() { return Ember['default'].A(); }; run['default'](App, 'advanceReadiness'); visit("/").then(function() { var rows = find(".name").length; equal(rows, 0, "successfully stubbed an empty array of people"); }); }); QUnit.test("template is bound to array of 2 people", function() { App.Person.find = function() { var people = Ember['default'].A(); var first = App.Person.create({ firstName: "x" }); var last = App.Person.create({ firstName: "y" }); run['default'](people, people.pushObject, first); run['default'](people, people.pushObject, last); return people; }; run['default'](App, 'advanceReadiness'); visit("/").then(function() { var rows = find(".name").length; equal(rows, 2, "successfully stubbed a non empty array of people"); }); }); QUnit.test("template is again bound to empty array of people", function() { App.Person.find = function() { return Ember['default'].A(); }; run['default'](App, 'advanceReadiness'); visit("/").then(function() { var rows = find(".name").length; equal(rows, 0, "successfully stubbed another empty array of people"); }); }); QUnit.test("`visit` can be called without advancedReadiness.", function() { App.Person.find = function() { return Ember['default'].A(); }; visit("/").then(function() { var rows = find(".name").length; equal(rows, 0, "stubbed an empty array of people without calling advancedReadiness."); }); }); }); enifed('ember-testing/tests/integration_test.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests'); test('ember-testing/tests/integration_test.js should pass jscs', function() { ok(true, 'ember-testing/tests/integration_test.js should pass jscs.'); }); }); enifed('ember-testing/tests/integration_test.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests'); test('ember-testing/tests/integration_test.js should pass jshint', function() { ok(true, 'ember-testing/tests/integration_test.js should pass jshint.'); }); }); enifed('ember-testing/tests/simple_setup', ['ember-metal/run_loop', 'ember-views/system/jquery'], function (run, jQuery) { 'use strict'; var App; QUnit.module('Simple Testing Setup', { teardown: function() { if (App) { App.removeTestHelpers(); jQuery['default']('#ember-testing-container, #ember-testing').remove(); run['default'](App, 'destroy'); App = null; } } }); }); enifed('ember-testing/tests/simple_setup.jscs-test', function () { 'use strict'; module('JSCS - ember-testing/tests'); test('ember-testing/tests/simple_setup.js should pass jscs', function() { ok(true, 'ember-testing/tests/simple_setup.js should pass jscs.'); }); }); enifed('ember-testing/tests/simple_setup.jshint', function () { 'use strict'; module('JSHint - ember-testing/tests'); test('ember-testing/tests/simple_setup.js should pass jshint', function() { ok(true, 'ember-testing/tests/simple_setup.js should pass jshint.'); }); }); enifed('ember-views.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember-views.js should pass jscs', function() { ok(true, 'ember-views.js should pass jscs.'); }); }); enifed('ember-views.jshint', function () { 'use strict'; module('JSHint - .'); test('ember-views.js should pass jshint', function() { ok(true, 'ember-views.js should pass jshint.'); }); }); enifed('ember-views/attr_nodes/attr_node.jscs-test', function () { 'use strict'; module('JSCS - ember-views/attr_nodes'); test('ember-views/attr_nodes/attr_node.js should pass jscs', function() { ok(true, 'ember-views/attr_nodes/attr_node.js should pass jscs.'); }); }); enifed('ember-views/attr_nodes/attr_node.jshint', function () { 'use strict'; module('JSHint - ember-views/attr_nodes'); test('ember-views/attr_nodes/attr_node.js should pass jshint', function() { ok(true, 'ember-views/attr_nodes/attr_node.js should pass jshint.'); }); }); enifed('ember-views/attr_nodes/legacy_bind.jscs-test', function () { 'use strict'; module('JSCS - ember-views/attr_nodes'); test('ember-views/attr_nodes/legacy_bind.js should pass jscs', function() { ok(true, 'ember-views/attr_nodes/legacy_bind.js should pass jscs.'); }); }); enifed('ember-views/attr_nodes/legacy_bind.jshint', function () { 'use strict'; module('JSHint - ember-views/attr_nodes'); test('ember-views/attr_nodes/legacy_bind.js should pass jshint', function() { ok(true, 'ember-views/attr_nodes/legacy_bind.js should pass jshint.'); }); }); enifed('ember-views/component_lookup.jscs-test', function () { 'use strict'; module('JSCS - ember-views'); test('ember-views/component_lookup.js should pass jscs', function() { ok(true, 'ember-views/component_lookup.js should pass jscs.'); }); }); enifed('ember-views/component_lookup.jshint', function () { 'use strict'; module('JSHint - ember-views'); test('ember-views/component_lookup.js should pass jshint', function() { ok(true, 'ember-views/component_lookup.js should pass jshint.'); }); }); enifed('ember-views/mixins/component_template_deprecation.jscs-test', function () { 'use strict'; module('JSCS - ember-views/mixins'); test('ember-views/mixins/component_template_deprecation.js should pass jscs', function() { ok(true, 'ember-views/mixins/component_template_deprecation.js should pass jscs.'); }); }); enifed('ember-views/mixins/component_template_deprecation.jshint', function () { 'use strict'; module('JSHint - ember-views/mixins'); test('ember-views/mixins/component_template_deprecation.js should pass jshint', function() { ok(true, 'ember-views/mixins/component_template_deprecation.js should pass jshint.'); }); }); enifed('ember-views/mixins/normalized_rerender_if_needed.jscs-test', function () { 'use strict'; module('JSCS - ember-views/mixins'); test('ember-views/mixins/normalized_rerender_if_needed.js should pass jscs', function() { ok(true, 'ember-views/mixins/normalized_rerender_if_needed.js should pass jscs.'); }); }); enifed('ember-views/mixins/normalized_rerender_if_needed.jshint', function () { 'use strict'; module('JSHint - ember-views/mixins'); test('ember-views/mixins/normalized_rerender_if_needed.js should pass jshint', function() { ok(true, 'ember-views/mixins/normalized_rerender_if_needed.js should pass jshint.'); }); }); enifed('ember-views/mixins/text_support.jscs-test', function () { 'use strict'; module('JSCS - ember-views/mixins'); test('ember-views/mixins/text_support.js should pass jscs', function() { ok(true, 'ember-views/mixins/text_support.js should pass jscs.'); }); }); enifed('ember-views/mixins/text_support.jshint', function () { 'use strict'; module('JSHint - ember-views/mixins'); test('ember-views/mixins/text_support.js should pass jshint', function() { ok(true, 'ember-views/mixins/text_support.js should pass jshint.'); }); }); enifed('ember-views/mixins/view_target_action_support.jscs-test', function () { 'use strict'; module('JSCS - ember-views/mixins'); test('ember-views/mixins/view_target_action_support.js should pass jscs', function() { ok(true, 'ember-views/mixins/view_target_action_support.js should pass jscs.'); }); }); enifed('ember-views/mixins/view_target_action_support.jshint', function () { 'use strict'; module('JSHint - ember-views/mixins'); test('ember-views/mixins/view_target_action_support.js should pass jshint', function() { ok(true, 'ember-views/mixins/view_target_action_support.js should pass jshint.'); }); }); enifed('ember-views/streams/class_name_binding.jscs-test', function () { 'use strict'; module('JSCS - ember-views/streams'); test('ember-views/streams/class_name_binding.js should pass jscs', function() { ok(true, 'ember-views/streams/class_name_binding.js should pass jscs.'); }); }); enifed('ember-views/streams/class_name_binding.jshint', function () { 'use strict'; module('JSHint - ember-views/streams'); test('ember-views/streams/class_name_binding.js should pass jshint', function() { ok(true, 'ember-views/streams/class_name_binding.js should pass jshint.'); }); }); enifed('ember-views/streams/context_stream.jscs-test', function () { 'use strict'; module('JSCS - ember-views/streams'); test('ember-views/streams/context_stream.js should pass jscs', function() { ok(true, 'ember-views/streams/context_stream.js should pass jscs.'); }); }); enifed('ember-views/streams/context_stream.jshint', function () { 'use strict'; module('JSHint - ember-views/streams'); test('ember-views/streams/context_stream.js should pass jshint', function() { ok(true, 'ember-views/streams/context_stream.js should pass jshint.'); }); }); enifed('ember-views/streams/key_stream.jscs-test', function () { 'use strict'; module('JSCS - ember-views/streams'); test('ember-views/streams/key_stream.js should pass jscs', function() { ok(true, 'ember-views/streams/key_stream.js should pass jscs.'); }); }); enifed('ember-views/streams/key_stream.jshint', function () { 'use strict'; module('JSHint - ember-views/streams'); test('ember-views/streams/key_stream.js should pass jshint', function() { ok(true, 'ember-views/streams/key_stream.js should pass jshint.'); }); }); enifed('ember-views/streams/should_display.jscs-test', function () { 'use strict'; module('JSCS - ember-views/streams'); test('ember-views/streams/should_display.js should pass jscs', function() { ok(true, 'ember-views/streams/should_display.js should pass jscs.'); }); }); enifed('ember-views/streams/should_display.jshint', function () { 'use strict'; module('JSHint - ember-views/streams'); test('ember-views/streams/should_display.js should pass jshint', function() { ok(true, 'ember-views/streams/should_display.js should pass jshint.'); }); }); enifed('ember-views/streams/utils.jscs-test', function () { 'use strict'; module('JSCS - ember-views/streams'); test('ember-views/streams/utils.js should pass jscs', function() { ok(true, 'ember-views/streams/utils.js should pass jscs.'); }); }); enifed('ember-views/streams/utils.jshint', function () { 'use strict'; module('JSHint - ember-views/streams'); test('ember-views/streams/utils.js should pass jshint', function() { ok(true, 'ember-views/streams/utils.js should pass jshint.'); }); }); enifed('ember-views/system/action_manager.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/action_manager.js should pass jscs', function() { ok(true, 'ember-views/system/action_manager.js should pass jscs.'); }); }); enifed('ember-views/system/action_manager.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/action_manager.js should pass jshint', function() { ok(true, 'ember-views/system/action_manager.js should pass jshint.'); }); }); enifed('ember-views/system/event_dispatcher.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/event_dispatcher.js should pass jscs', function() { ok(true, 'ember-views/system/event_dispatcher.js should pass jscs.'); }); }); enifed('ember-views/system/event_dispatcher.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/event_dispatcher.js should pass jshint', function() { ok(true, 'ember-views/system/event_dispatcher.js should pass jshint.'); }); }); enifed('ember-views/system/ext.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/ext.js should pass jscs', function() { ok(true, 'ember-views/system/ext.js should pass jscs.'); }); }); enifed('ember-views/system/ext.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/ext.js should pass jshint', function() { ok(true, 'ember-views/system/ext.js should pass jshint.'); }); }); enifed('ember-views/system/jquery.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/jquery.js should pass jscs', function() { ok(true, 'ember-views/system/jquery.js should pass jscs.'); }); }); enifed('ember-views/system/jquery.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/jquery.js should pass jshint', function() { ok(true, 'ember-views/system/jquery.js should pass jshint.'); }); }); enifed('ember-views/system/lookup_partial.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/lookup_partial.js should pass jscs', function() { ok(true, 'ember-views/system/lookup_partial.js should pass jscs.'); }); }); enifed('ember-views/system/lookup_partial.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/lookup_partial.js should pass jshint', function() { ok(true, 'ember-views/system/lookup_partial.js should pass jshint.'); }); }); enifed('ember-views/system/platform.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/platform.js should pass jscs', function() { ok(true, 'ember-views/system/platform.js should pass jscs.'); }); }); enifed('ember-views/system/platform.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/platform.js should pass jshint', function() { ok(true, 'ember-views/system/platform.js should pass jshint.'); }); }); enifed('ember-views/system/render_buffer.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/render_buffer.js should pass jscs', function() { ok(true, 'ember-views/system/render_buffer.js should pass jscs.'); }); }); enifed('ember-views/system/render_buffer.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/render_buffer.js should pass jshint', function() { ok(true, 'ember-views/system/render_buffer.js should pass jshint.'); }); }); enifed('ember-views/system/renderer.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/renderer.js should pass jscs', function() { ok(true, 'ember-views/system/renderer.js should pass jscs.'); }); }); enifed('ember-views/system/renderer.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/renderer.js should pass jshint', function() { ok(true, 'ember-views/system/renderer.js should pass jshint.'); }); }); enifed('ember-views/system/sanitize_attribute_value.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/sanitize_attribute_value.js should pass jscs', function() { ok(true, 'ember-views/system/sanitize_attribute_value.js should pass jscs.'); }); }); enifed('ember-views/system/sanitize_attribute_value.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/sanitize_attribute_value.js should pass jshint', function() { ok(true, 'ember-views/system/sanitize_attribute_value.js should pass jshint.'); }); }); enifed('ember-views/system/utils.jscs-test', function () { 'use strict'; module('JSCS - ember-views/system'); test('ember-views/system/utils.js should pass jscs', function() { ok(true, 'ember-views/system/utils.js should pass jscs.'); }); }); enifed('ember-views/system/utils.jshint', function () { 'use strict'; module('JSHint - ember-views/system'); test('ember-views/system/utils.js should pass jshint', function() { ok(true, 'ember-views/system/utils.js should pass jshint.'); }); }); enifed('ember-views/tests/mixins/view_target_action_support_test', ['ember-runtime/system/object', 'ember-views/views/view', 'ember-views/mixins/view_target_action_support'], function (EmberObject, View, ViewTargetActionSupport) { 'use strict'; QUnit.module("ViewTargetActionSupport"); QUnit.test("it should return false if no action is specified", function() { expect(1); var view = View['default'].createWithMixins(ViewTargetActionSupport['default'], { controller: EmberObject['default'].create() }); ok(false === view.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should support actions specified as strings", function() { expect(2); var view = View['default'].createWithMixins(ViewTargetActionSupport['default'], { controller: EmberObject['default'].create({ anEvent: function() { ok(true, "anEvent method was called"); } }), action: 'anEvent' }); ok(true === view.triggerAction(), "a valid target and action were specified"); }); QUnit.test("it should invoke the send() method on the controller with the view's context", function() { expect(3); var view = View['default'].createWithMixins(ViewTargetActionSupport['default'], { context: {}, controller: EmberObject['default'].create({ send: function(evt, context) { equal(evt, 'anEvent', "send() method was invoked with correct event name"); equal(context, view.context, "send() method was invoked with correct context"); } }), action: 'anEvent' }); ok(true === view.triggerAction(), "a valid target and action were specified"); }); }); enifed('ember-views/tests/mixins/view_target_action_support_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/mixins'); test('ember-views/tests/mixins/view_target_action_support_test.js should pass jscs', function() { ok(true, 'ember-views/tests/mixins/view_target_action_support_test.js should pass jscs.'); }); }); enifed('ember-views/tests/mixins/view_target_action_support_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/mixins'); test('ember-views/tests/mixins/view_target_action_support_test.js should pass jshint', function() { ok(true, 'ember-views/tests/mixins/view_target_action_support_test.js should pass jshint.'); }); }); enifed('ember-views/tests/streams/class_string_for_value_test', ['ember-views/streams/class_name_binding'], function (class_name_binding) { 'use strict'; QUnit.module("EmberView - classStringForValue"); QUnit.test("returns dasherized version of last path part if value is true", function() { equal(class_name_binding.classStringForValue("propertyName", true), "property-name", "class is dasherized"); equal(class_name_binding.classStringForValue("content.propertyName", true), "property-name", "class is dasherized"); }); QUnit.test("returns className if value is true and className is specified", function() { equal(class_name_binding.classStringForValue("propertyName", true, "truthyClass"), "truthyClass", "returns className if given"); equal(class_name_binding.classStringForValue("content.propertyName", true, "truthyClass"), "truthyClass", "returns className if given"); }); QUnit.test("returns falsyClassName if value is false and falsyClassName is specified", function() { equal(class_name_binding.classStringForValue("propertyName", false, "truthyClass", "falsyClass"), "falsyClass", "returns falsyClassName if given"); equal(class_name_binding.classStringForValue("content.propertyName", false, "truthyClass", "falsyClass"), "falsyClass", "returns falsyClassName if given"); }); QUnit.test("returns null if value is false and falsyClassName is not specified", function() { equal(class_name_binding.classStringForValue("propertyName", false, "truthyClass"), null, "returns null if falsyClassName is not specified"); equal(class_name_binding.classStringForValue("content.propertyName", false, "truthyClass"), null, "returns null if falsyClassName is not specified"); }); QUnit.test("returns null if value is false", function() { equal(class_name_binding.classStringForValue("propertyName", false), null, "returns null if value is false"); equal(class_name_binding.classStringForValue("content.propertyName", false), null, "returns null if value is false"); }); QUnit.test("returns null if value is true and className is not specified and falsyClassName is specified", function() { equal(class_name_binding.classStringForValue("propertyName", true, undefined, "falsyClassName"), null, "returns null if value is true"); equal(class_name_binding.classStringForValue("content.propertyName", true, undefined, "falsyClassName"), null, "returns null if value is true"); }); QUnit.test("returns the value if the value is truthy", function() { equal(class_name_binding.classStringForValue("propertyName", "myString"), "myString", "returns value if the value is truthy"); equal(class_name_binding.classStringForValue("content.propertyName", "myString"), "myString", "returns value if the value is truthy"); equal(class_name_binding.classStringForValue("propertyName", "123"), 123, "returns value if the value is truthy"); equal(class_name_binding.classStringForValue("content.propertyName", 123), 123, "returns value if the value is truthy"); }); QUnit.test("treat empty array as falsy value and return null", function() { equal(class_name_binding.classStringForValue("propertyName", [], "truthyClass"), null, "returns null if value is false"); equal(class_name_binding.classStringForValue("content.propertyName", [], "truthyClass"), null, "returns null if value is false"); }); QUnit.test("treat non-empty array as truthy value and return the className if specified", function() { equal(class_name_binding.classStringForValue("propertyName", ['emberjs'], "truthyClass"), "truthyClass", "returns className if given"); equal(class_name_binding.classStringForValue("content.propertyName", ['emberjs'], "truthyClass"), "truthyClass", "returns className if given"); }); }); enifed('ember-views/tests/streams/class_string_for_value_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/streams'); test('ember-views/tests/streams/class_string_for_value_test.js should pass jscs', function() { ok(true, 'ember-views/tests/streams/class_string_for_value_test.js should pass jscs.'); }); }); enifed('ember-views/tests/streams/class_string_for_value_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/streams'); test('ember-views/tests/streams/class_string_for_value_test.js should pass jshint', function() { ok(true, 'ember-views/tests/streams/class_string_for_value_test.js should pass jshint.'); }); }); enifed('ember-views/tests/streams/parse_property_path_test', ['ember-views/streams/class_name_binding'], function (class_name_binding) { 'use strict'; QUnit.module("EmberView - parsePropertyPath"); QUnit.test("it works with a simple property path", function() { var parsed = class_name_binding.parsePropertyPath("simpleProperty"); equal(parsed.path, "simpleProperty", "path is parsed correctly"); equal(parsed.className, undefined, "there is no className"); equal(parsed.falsyClassName, undefined, "there is no falsyClassName"); equal(parsed.classNames, "", "there is no classNames"); }); QUnit.test("it works with a more complex property path", function() { var parsed = class_name_binding.parsePropertyPath("content.simpleProperty"); equal(parsed.path, "content.simpleProperty", "path is parsed correctly"); equal(parsed.className, undefined, "there is no className"); equal(parsed.falsyClassName, undefined, "there is no falsyClassName"); equal(parsed.classNames, "", "there is no classNames"); }); QUnit.test("className is extracted", function() { var parsed = class_name_binding.parsePropertyPath("content.simpleProperty:class"); equal(parsed.path, "content.simpleProperty", "path is parsed correctly"); equal(parsed.className, "class", "className is extracted"); equal(parsed.falsyClassName, undefined, "there is no falsyClassName"); equal(parsed.classNames, ":class", "there is a classNames"); }); QUnit.test("falsyClassName is extracted", function() { var parsed = class_name_binding.parsePropertyPath("content.simpleProperty:class:falsyClass"); equal(parsed.path, "content.simpleProperty", "path is parsed correctly"); equal(parsed.className, "class", "className is extracted"); equal(parsed.falsyClassName, "falsyClass", "falsyClassName is extracted"); equal(parsed.classNames, ":class:falsyClass", "there is a classNames"); }); QUnit.test("it works with an empty true class", function() { var parsed = class_name_binding.parsePropertyPath("content.simpleProperty::falsyClass"); equal(parsed.path, "content.simpleProperty", "path is parsed correctly"); equal(parsed.className, undefined, "className is undefined"); equal(parsed.falsyClassName, "falsyClass", "falsyClassName is extracted"); equal(parsed.classNames, "::falsyClass", "there is a classNames"); }); }); enifed('ember-views/tests/streams/parse_property_path_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/streams'); test('ember-views/tests/streams/parse_property_path_test.js should pass jscs', function() { ok(true, 'ember-views/tests/streams/parse_property_path_test.js should pass jscs.'); }); }); enifed('ember-views/tests/streams/parse_property_path_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/streams'); test('ember-views/tests/streams/parse_property_path_test.js should pass jshint', function() { ok(true, 'ember-views/tests/streams/parse_property_path_test.js should pass jshint.'); }); }); enifed('ember-views/tests/system/event_dispatcher_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-views/system/event_dispatcher', 'ember-views/views/container_view'], function (property_get, run, EmberObject, jQuery, View, EventDispatcher, ContainerView) { 'use strict'; var view; var dispatcher; QUnit.module("EventDispatcher", { setup: function() { run['default'](function() { dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); }); }, teardown: function() { run['default'](function() { if (view) { view.destroy(); } dispatcher.destroy(); }); } }); QUnit.test("should dispatch events to views", function() { var receivedEvent; var parentMouseDownCalled = 0; var childKeyDownCalled = 0; var parentKeyDownCalled = 0; var childView = View['default'].createWithMixins({ render: function(buffer) { buffer.push('ewot'); }, keyDown: function(evt) { childKeyDownCalled++; return false; } }); view = View['default'].createWithMixins({ render: function(buffer) { buffer.push('some awesome content'); this.appendChild(childView); }, mouseDown: function(evt) { parentMouseDownCalled++; receivedEvent = evt; }, keyDown: function(evt) { parentKeyDownCalled++; } }); run['default'](function() { view.appendTo('#qunit-fixture'); }); view.$().trigger('mousedown'); ok(receivedEvent, "passes event to associated event method"); receivedEvent = null; parentMouseDownCalled = 0; view.$('span#awesome').trigger('mousedown'); ok(receivedEvent, "event bubbles up to nearest View"); equal(parentMouseDownCalled, 1, "does not trigger the parent handlers twice because of browser bubbling"); receivedEvent = null; jQuery['default']('#wot').trigger('mousedown'); ok(receivedEvent, "event bubbles up to nearest View"); jQuery['default']('#wot').trigger('keydown'); equal(childKeyDownCalled, 1, "calls keyDown on child view"); equal(parentKeyDownCalled, 0, "does not call keyDown on parent if child handles event"); }); QUnit.test("should not dispatch events to views not inDOM", function() { var receivedEvent; view = View['default'].createWithMixins({ render: function(buffer) { buffer.push('some awesome content'); this._super(buffer); }, mouseDown: function(evt) { receivedEvent = evt; } }); run['default'](function() { view.append(); }); var $element = view.$(); // TODO change this test not to use private API // Force into preRender view._renderer.remove(view, false, true); $element.trigger('mousedown'); ok(!receivedEvent, "does not pass event to associated event method"); receivedEvent = null; $element.find('span#awesome').trigger('mousedown'); ok(!receivedEvent, "event does not bubble up to nearest View"); receivedEvent = null; // Cleanup $element.remove(); }); QUnit.test("should send change events up view hierarchy if view contains form elements", function() { var receivedEvent; view = View['default'].create({ render: function(buffer) { buffer.push(''); }, change: function(evt) { receivedEvent = evt; } }); run['default'](function() { view.append(); }); jQuery['default']('#is-done').trigger('change'); ok(receivedEvent, "calls change method when a child element is changed"); equal(receivedEvent.target, jQuery['default']('#is-done')[0], "target property is the element that was clicked"); }); QUnit.test("events should stop propagating if the view is destroyed", function() { var parentViewReceived, receivedEvent; var parentView = ContainerView['default'].create({ change: function(evt) { parentViewReceived = true; } }); view = parentView.createChildView(View['default'], { render: function(buffer) { buffer.push(''); }, change: function(evt) { receivedEvent = true; var self = this; run['default'](function() { property_get.get(self, 'parentView').destroy(); }); } }); parentView.pushObject(view); run['default'](function() { parentView.append(); }); ok(jQuery['default']('#is-done').length, "precond - view is in the DOM"); jQuery['default']('#is-done').trigger('change'); ok(!jQuery['default']('#is-done').length, "precond - view is not in the DOM"); ok(receivedEvent, "calls change method when a child element is changed"); ok(!parentViewReceived, "parent view does not receive the event"); }); QUnit.test('should not interfere with event propagation of virtualViews', function() { var receivedEvent; var view = View['default'].create({ isVirtual: true, render: function(buffer) { buffer.push('
    '); } }); run['default'](function() { view.append(); }); jQuery['default'](window).bind('click', function(evt) { receivedEvent = evt; }); jQuery['default']('#propagate-test-div').click(); ok(receivedEvent, 'allowed event to propagate'); deepEqual(receivedEvent && receivedEvent.target, jQuery['default']('#propagate-test-div')[0], 'target property is the element that was clicked'); }); QUnit.test("should dispatch events to nearest event manager", function() { var receivedEvent=0; view = ContainerView['default'].create({ render: function(buffer) { buffer.push(''); }, eventManager: EmberObject['default'].create({ mouseDown: function() { receivedEvent++; } }), mouseDown: function() {} }); run['default'](function() { view.append(); }); jQuery['default']('#is-done').trigger('mousedown'); equal(receivedEvent, 1, "event should go to manager and not view"); }); QUnit.test("event manager should be able to re-dispatch events to view", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); var receivedEvent=0; view = ContainerView['default'].createWithMixins({ elementId: 'containerView', eventManager: EmberObject['default'].create({ mouseDown: function(evt, view) { // Re-dispatch event when you get it. // // The second parameter tells the dispatcher // that this event has been handled. This // API will clearly need to be reworked since // multiple eventManagers in a single view // hierarchy would break, but it shows that // re-dispatching works view.$().trigger('mousedown', this); } }), childViews: ['child'], child: View['default'].extend({ elementId: 'nestedView', mouseDown: function(evt) { receivedEvent++; } }), mouseDown: function(evt) { receivedEvent++; } }); run['default'](function() { view.append(); }); jQuery['default']('#nestedView').trigger('mousedown'); equal(receivedEvent, 2, "event should go to manager and not view"); }); QUnit.test("event handlers should be wrapped in a run loop", function() { expect(1); view = View['default'].createWithMixins({ elementId: 'test-view', eventManager: EmberObject['default'].create({ mouseDown: function() { ok(run['default'].currentRunLoop, 'a run loop should have started'); } }) }); run['default'](function() { view.append(); }); jQuery['default']('#test-view').trigger('mousedown'); }); QUnit.module("EventDispatcher#setup", { setup: function() { run['default'](function() { dispatcher = EventDispatcher['default'].create({ rootElement: "#qunit-fixture" }); }); }, teardown: function() { run['default'](function() { if (view) { view.destroy(); } dispatcher.destroy(); }); } }); QUnit.test("additional events which should be listened on can be passed", function () { expect(1); run['default'](function () { dispatcher.setup({ myevent: "myEvent" }); view = View['default'].create({ elementId: "leView", myEvent: function() { ok(true, "custom event has been triggered"); } }).appendTo(dispatcher.get("rootElement")); }); jQuery['default']("#leView").trigger("myevent"); }); QUnit.test("additional events and rootElement can be specified", function () { expect(3); jQuery['default']("#qunit-fixture").append("
    "); run['default'](function () { dispatcher.setup({ myevent: "myEvent" }, ".custom-root"); view = View['default'].create({ elementId: "leView", myEvent: function() { ok(true, "custom event has been triggered"); } }).appendTo(dispatcher.get("rootElement")); }); ok(jQuery['default'](".custom-root").hasClass("ember-application"), "the custom rootElement is used"); equal(dispatcher.get("rootElement"), ".custom-root", "the rootElement is updated"); jQuery['default']("#leView").trigger("myevent"); }); }); enifed('ember-views/tests/system/event_dispatcher_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/system'); test('ember-views/tests/system/event_dispatcher_test.js should pass jscs', function() { ok(true, 'ember-views/tests/system/event_dispatcher_test.js should pass jscs.'); }); }); enifed('ember-views/tests/system/event_dispatcher_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/system'); test('ember-views/tests/system/event_dispatcher_test.js should pass jshint', function() { ok(true, 'ember-views/tests/system/event_dispatcher_test.js should pass jshint.'); }); }); enifed('ember-views/tests/system/ext_test', ['ember-metal/run_loop', 'ember-views/views/view'], function (run, View) { 'use strict'; QUnit.module("Ember.View additions to run queue"); QUnit.test("View hierarchy is done rendering to DOM when functions queued in afterRender execute", function() { var didInsert = 0; var childView = View['default'].create({ elementId: 'child_view', didInsertElement: function() { didInsert++; } }); var parentView = View['default'].create({ elementId: 'parent_view', render: function(buffer) { this.appendChild(childView); }, didInsertElement: function() { didInsert++; } }); run['default'](function() { parentView.appendTo('#qunit-fixture'); run['default'].schedule('afterRender', this, function() { equal(didInsert, 2, 'all didInsertElement hooks fired for hierarchy'); }); }); run['default'](function() { parentView.destroy(); }); }); }); enifed('ember-views/tests/system/ext_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/system'); test('ember-views/tests/system/ext_test.js should pass jscs', function() { ok(true, 'ember-views/tests/system/ext_test.js should pass jscs.'); }); }); enifed('ember-views/tests/system/ext_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/system'); test('ember-views/tests/system/ext_test.js should pass jshint', function() { ok(true, 'ember-views/tests/system/ext_test.js should pass jshint.'); }); }); enifed('ember-views/tests/system/jquery_ext_test', ['ember-metal/run_loop', 'ember-views/system/event_dispatcher', 'ember-views/system/jquery', 'ember-views/views/view'], function (run, EventDispatcher, jQuery, View) { 'use strict'; var view, dispatcher; // Adapted from https://github.com/jquery/jquery/blob/f30f7732e7775b6e417c4c22ced7adb2bf76bf89/test/data/testinit.js var canDataTransfer, fireNativeWithDataTransfer; if (document.createEvent) { canDataTransfer = !!document.createEvent('HTMLEvents').dataTransfer; fireNativeWithDataTransfer = function(node, type, dataTransfer) { var event = document.createEvent('HTMLEvents'); event.initEvent(type, true, true); event.dataTransfer = dataTransfer; node.dispatchEvent(event); }; } else { canDataTransfer = !!document.createEventObject().dataTransfer; fireNativeWithDataTransfer = function(node, type, dataTransfer) { var event = document.createEventObject(); event.dataTransfer = dataTransfer; node.fireEvent('on' + type, event); }; } QUnit.module("EventDispatcher", { setup: function() { run['default'](function() { dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); }); }, teardown: function() { run['default'](function() { if (view) { view.destroy(); } dispatcher.destroy(); }); } }); if (canDataTransfer) { QUnit.test("jQuery.event.fix copies over the dataTransfer property", function() { var originalEvent; var receivedEvent; originalEvent = { type: 'drop', dataTransfer: 'success', target: document.body }; receivedEvent = jQuery['default'].event.fix(originalEvent); ok(receivedEvent !== originalEvent, "attributes are copied to a new event object"); equal(receivedEvent.dataTransfer, originalEvent.dataTransfer, "copies dataTransfer property to jQuery event"); }); QUnit.test("drop handler should receive event with dataTransfer property", function() { var receivedEvent; var dropCalled = 0; view = View['default'].createWithMixins({ render: function(buffer) { buffer.push('please drop stuff on me'); this._super(buffer); }, drop: function(evt) { receivedEvent = evt; dropCalled++; } }); run['default'](function() { view.append(); }); fireNativeWithDataTransfer(view.$().get(0), 'drop', 'success'); equal(dropCalled, 1, "called drop handler once"); equal(receivedEvent.dataTransfer, 'success', "copies dataTransfer property to jQuery event"); }); } }); enifed('ember-views/tests/system/jquery_ext_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/system'); test('ember-views/tests/system/jquery_ext_test.js should pass jscs', function() { ok(true, 'ember-views/tests/system/jquery_ext_test.js should pass jscs.'); }); }); enifed('ember-views/tests/system/jquery_ext_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/system'); test('ember-views/tests/system/jquery_ext_test.js should pass jshint', function() { ok(true, 'ember-views/tests/system/jquery_ext_test.js should pass jshint.'); }); }); enifed('ember-views/tests/system/render_buffer_test', ['ember-views/system/jquery', 'ember-views/system/render_buffer', 'dom-helper'], function (jQuery, RenderBuffer, DOMHelper) { 'use strict'; var svgNamespace = "http://www.w3.org/2000/svg"; var xhtmlNamespace = "http://www.w3.org/1999/xhtml"; var trim = jQuery['default'].trim; // ....................................................... // render() // QUnit.module("RenderBuffer"); var domHelper = new DOMHelper['default'](); function createRenderBuffer(tagName, contextualElement) { var buffer = new RenderBuffer['default'](domHelper); buffer.reset(tagName, contextualElement); return buffer; } QUnit.test("RenderBuffers raise a deprecation warning without a contextualElement", function() { var buffer = createRenderBuffer('div'); expectDeprecation(function() { buffer.generateElement(); var el = buffer.element(); equal(el.tagName.toLowerCase(), 'div'); }, /The render buffer expects an outer contextualElement to exist/); }); QUnit.test("reset RenderBuffers raise a deprecation warning without a contextualElement", function() { var buffer = createRenderBuffer('div', document.body); buffer.reset('span'); expectDeprecation(function() { buffer.generateElement(); var el = buffer.element(); equal(el.tagName.toLowerCase(), 'span'); }, /The render buffer expects an outer contextualElement to exist/); }); QUnit.test("RenderBuffers combine strings", function() { var buffer = createRenderBuffer('div', document.body); buffer.generateElement(); buffer.push('a'); buffer.push('b'); var el = buffer.element(); equal(el.tagName.toLowerCase(), 'div'); equal(el.childNodes[0].nodeValue, 'ab', "Multiple pushes should concatenate"); }); QUnit.test("RenderBuffers push fragments", function() { var buffer = createRenderBuffer('div', document.body); var fragment = document.createElement('span'); buffer.generateElement(); buffer.push(fragment); var el = buffer.element(); equal(el.tagName.toLowerCase(), 'div'); equal(el.childNodes[0].tagName, 'SPAN', "Fragment is pushed into the buffer"); }); QUnit.test("RenderBuffers cannot push fragments when something else is in the buffer", function() { var buffer = createRenderBuffer('div', document.body); var fragment = document.createElement('span'); buffer.generateElement(); buffer.push(fragment); expectAssertion(function() { buffer.push(fragment); }); }); QUnit.test("RenderBuffers cannot push strings after fragments", function() { var buffer = createRenderBuffer('div', document.body); var fragment = document.createElement('span'); buffer.generateElement(); buffer.push(fragment); expectAssertion(function() { buffer.push('howdy'); }); }); QUnit.test("value of 0 is included in output", function() { var buffer, el; buffer = createRenderBuffer('input', document.body); buffer.prop('value', 0); buffer.generateElement(); el = buffer.element(); strictEqual(el.value, '0', "generated element has value of '0'"); }); QUnit.test("sets attributes with camelCase", function() { var buffer = createRenderBuffer('div', document.body); var content = "javascript:someCode()"; //jshint ignore:line buffer.attr('onClick', content); buffer.generateElement(); var el = buffer.element(); strictEqual(el.getAttribute('onClick'), content, "attribute with camelCase was set"); }); QUnit.test("prevents XSS injection via `id`", function() { var buffer = createRenderBuffer('div', document.body); buffer.id('hacked" megahax="yes'); buffer.generateElement(); var el = buffer.element(); equal(el.id, 'hacked" megahax="yes'); }); QUnit.test("prevents XSS injection via `attr`", function() { var buffer = createRenderBuffer('div', document.body); buffer.attr('id', 'trololol" onmouseover="pwn()'); buffer.attr('class', "hax>
    foo'); var element = buffer.element(); ok(jQuery['default'](element).html().match(/script/i), "should have script tag"); ok(!jQuery['default'](element).html().match(/­/), "should not have ­"); }); if ('namespaceURI' in document.createElement('div')) { QUnit.module("RenderBuffer namespaces"); QUnit.test("properly makes a content string SVG namespace inside an SVG tag", function() { var buffer = createRenderBuffer('svg', document.body); buffer.generateElement(); buffer.push('foo'); var element = buffer.element(); ok(element.tagName, 'SVG', 'element is svg'); equal(element.namespaceURI, svgNamespace, 'element is svg namespace'); ok(element.childNodes[0].tagName, 'PATH', 'element is path'); equal(element.childNodes[0].namespaceURI, svgNamespace, 'element is svg namespace'); }); QUnit.test("properly makes a path element svg namespace inside SVG context", function() { var buffer = createRenderBuffer('path', document.createElementNS(svgNamespace, 'svg')); buffer.generateElement(); buffer.push(''); var element = buffer.element(); ok(element.tagName, 'PATH', 'element is PATH'); equal(element.namespaceURI, svgNamespace, 'element is svg namespace'); ok(element.childNodes[0].tagName, 'G', 'element is g'); equal(element.childNodes[0].namespaceURI, svgNamespace, 'element is svg namespace'); }); QUnit.test("properly makes a foreignObject svg namespace inside SVG context", function() { var buffer = createRenderBuffer('foreignObject', document.createElementNS(svgNamespace, 'svg')); buffer.generateElement(); buffer.push('
    '); var element = buffer.element(); ok(element.tagName, 'FOREIGNOBJECT', 'element is foreignObject'); equal(element.namespaceURI, svgNamespace, 'element is svg namespace'); ok(element.childNodes[0].tagName, 'DIV', 'element is div'); equal(element.childNodes[0].namespaceURI, xhtmlNamespace, 'element is xhtml namespace'); }); QUnit.test("properly makes a div xhtml namespace inside foreignObject context", function() { var buffer = createRenderBuffer('div', document.createElementNS(svgNamespace, 'foreignObject')); buffer.generateElement(); buffer.push('
    '); var element = buffer.element(); ok(element.tagName, 'DIV', 'element is div'); equal(element.namespaceURI, xhtmlNamespace, 'element is xhtml namespace'); ok(element.childNodes[0].tagName, 'DIV', 'element is div'); equal(element.childNodes[0].namespaceURI, xhtmlNamespace, 'element is xhtml namespace'); }); } }); enifed('ember-views/tests/system/render_buffer_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/system'); test('ember-views/tests/system/render_buffer_test.js should pass jscs', function() { ok(true, 'ember-views/tests/system/render_buffer_test.js should pass jscs.'); }); }); enifed('ember-views/tests/system/render_buffer_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/system'); test('ember-views/tests/system/render_buffer_test.js should pass jshint', function() { ok(true, 'ember-views/tests/system/render_buffer_test.js should pass jshint.'); }); }); enifed('ember-views/tests/system/sanitize_attribute_value_test', ['ember-views/system/sanitize_attribute_value', 'ember-htmlbars/utils/string', 'dom-helper'], function (sanitizeAttributeValue, string, DOMHelper) { 'use strict'; QUnit.module('ember-views: sanitizeAttributeValue(null, "href")'); var goodProtocols = ['https', 'http', 'ftp', 'tel', 'file']; var dom = new DOMHelper['default'](); for (var i = 0, l = goodProtocols.length; i < l; i++) { buildProtocolTest(goodProtocols[i]); } function buildProtocolTest(protocol) { QUnit.test('allows ' + protocol + ' protocol when element is not provided', function() { expect(1); var expected = protocol + '://foo.com'; var actual = sanitizeAttributeValue['default'](dom, null, 'href', expected); equal(actual, expected, 'protocol not escaped'); }); } QUnit.test('blocks javascript: protocol', function() { /* jshint scripturl:true */ expect(1); var expected = 'javascript:alert("foo")'; var actual = sanitizeAttributeValue['default'](dom, null, 'href', expected); equal(actual, 'unsafe:' + expected, 'protocol escaped'); }); QUnit.test('blocks blacklisted protocols', function() { /* jshint scripturl:true */ expect(1); var expected = 'javascript:alert("foo")'; var actual = sanitizeAttributeValue['default'](dom, null, 'href', expected); equal(actual, 'unsafe:' + expected, 'protocol escaped'); }); QUnit.test('does not block SafeStrings', function() { /* jshint scripturl:true */ expect(1); var expected = 'javascript:alert("foo")'; var actual = sanitizeAttributeValue['default'](dom, null, 'href', new string.SafeString(expected)); equal(actual, expected, 'protocol unescaped'); }); }); enifed('ember-views/tests/system/sanitize_attribute_value_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/system'); test('ember-views/tests/system/sanitize_attribute_value_test.js should pass jscs', function() { ok(true, 'ember-views/tests/system/sanitize_attribute_value_test.js should pass jscs.'); }); }); enifed('ember-views/tests/system/sanitize_attribute_value_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/system'); test('ember-views/tests/system/sanitize_attribute_value_test.js should pass jshint', function() { ok(true, 'ember-views/tests/system/sanitize_attribute_value_test.js should pass jshint.'); }); }); enifed('ember-views/tests/system/view_utils_test', ['ember-metal/run_loop', 'ember-views/views/view'], function (run, View) { 'use strict'; var hasGetClientRects, hasGetBoundingClientRect; var ClientRectListCtor, ClientRectCtor; (function() { if (document.createRange) { var range = document.createRange(); if (range.getClientRects) { var clientRectsList = range.getClientRects(); hasGetClientRects = true; ClientRectListCtor = clientRectsList && clientRectsList.constructor; } if (range.getBoundingClientRect) { var clientRect = range.getBoundingClientRect(); hasGetBoundingClientRect = true; ClientRectCtor = clientRect && clientRect.constructor; } } })(); var view; QUnit.module("ViewUtils", { teardown: function() { run['default'](function() { if (view) { view.destroy(); } }); } }); QUnit.test("getViewClientRects", function() { if (!hasGetClientRects || !ClientRectListCtor) { ok(true, "The test environment does not support the DOM API required to run this test."); return; } view = View['default'].create({ render: function(buffer) { buffer.push("Hello, world!"); } }); run['default'](function() { view.appendTo('#qunit-fixture'); }); ok(Ember.ViewUtils.getViewClientRects(view) instanceof ClientRectListCtor); }); QUnit.test("getViewBoundingClientRect", function() { if (!hasGetBoundingClientRect || !ClientRectCtor) { ok(true, "The test environment does not support the DOM API required to run this test."); return; } view = View['default'].create({ render: function(buffer) { buffer.push("Hello, world!"); } }); run['default'](function() { view.appendTo('#qunit-fixture'); }); ok(Ember.ViewUtils.getViewBoundingClientRect(view) instanceof ClientRectCtor); }); }); enifed('ember-views/tests/system/view_utils_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/system'); test('ember-views/tests/system/view_utils_test.js should pass jscs', function() { ok(true, 'ember-views/tests/system/view_utils_test.js should pass jscs.'); }); }); enifed('ember-views/tests/system/view_utils_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/system'); test('ember-views/tests/system/view_utils_test.js should pass jshint', function() { ok(true, 'ember-views/tests/system/view_utils_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/checkbox_test', ['ember-views/views/checkbox', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-views/system/event_dispatcher'], function (Checkbox, property_get, property_set, run, EventDispatcher) { 'use strict'; function set(obj, key, value) { run['default'](function() { property_set.set(obj, key, value); }); } function append() { run['default'](function() { checkboxView.appendTo('#qunit-fixture'); }); } var checkboxView, dispatcher; QUnit.module("Ember.Checkbox", { setup: function() { dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); }, teardown: function() { run['default'](function() { dispatcher.destroy(); checkboxView.destroy(); }); } }); QUnit.test("should begin disabled if the disabled attribute is true", function() { checkboxView = Checkbox['default'].create({}); checkboxView.set('disabled', true); append(); ok(checkboxView.$().is(":disabled")); }); QUnit.test("should become disabled if the disabled attribute is changed", function() { checkboxView = Checkbox['default'].create({}); append(); ok(checkboxView.$().is(":not(:disabled)")); run['default'](function() { checkboxView.set('disabled', true); }); ok(checkboxView.$().is(":disabled")); run['default'](function() { checkboxView.set('disabled', false); }); ok(checkboxView.$().is(":not(:disabled)")); }); QUnit.test("should begin indeterminate if the indeterminate attribute is true", function() { checkboxView = Checkbox['default'].create({}); checkboxView.set('indeterminate', true); append(); equal(checkboxView.$().prop('indeterminate'), true, "Checkbox should be indeterminate"); }); QUnit.test("should become indeterminate if the indeterminate attribute is changed", function() { checkboxView = Checkbox['default'].create({}); append(); equal(checkboxView.$().prop('indeterminate'), false, "Checkbox should not be indeterminate"); run['default'](function() { checkboxView.set('indeterminate', true); }); equal(checkboxView.$().prop('indeterminate'), true, "Checkbox should be indeterminate"); run['default'](function() { checkboxView.set('indeterminate', false); }); equal(checkboxView.$().prop('indeterminate'), false, "Checkbox should not be indeterminate"); }); QUnit.test("should support the tabindex property", function() { checkboxView = Checkbox['default'].create({}); run['default'](function() { checkboxView.set('tabindex', 6); }); append(); equal(checkboxView.$().prop('tabindex'), '6', 'the initial checkbox tabindex is set in the DOM'); run['default'](function() { checkboxView.set('tabindex', 3); }); equal(checkboxView.$().prop('tabindex'), '3', 'the checkbox tabindex changes when it is changed in the view'); }); QUnit.test("checkbox name is updated when setting name property of view", function() { checkboxView = Checkbox['default'].create({}); run['default'](function() { checkboxView.set('name', 'foo'); }); append(); equal(checkboxView.$().attr('name'), "foo", "renders checkbox with the name"); run['default'](function() { checkboxView.set('name', 'bar'); }); equal(checkboxView.$().attr('name'), "bar", "updates checkbox after name changes"); }); QUnit.test("checked property mirrors input value", function() { checkboxView = Checkbox['default'].create({}); run['default'](function() { checkboxView.append(); }); equal(property_get.get(checkboxView, 'checked'), false, "initially starts with a false value"); equal(!!checkboxView.$().prop('checked'), false, "the initial checked property is false"); set(checkboxView, 'checked', true); equal(checkboxView.$().prop('checked'), true, "changing the value property changes the DOM"); run['default'](function() { checkboxView.remove(); }); run['default'](function() { checkboxView.append(); }); equal(checkboxView.$().prop('checked'), true, "changing the value property changes the DOM"); run['default'](function() { checkboxView.remove(); }); run['default'](function() { set(checkboxView, 'checked', false); }); run['default'](function() { checkboxView.append(); }); equal(checkboxView.$().prop('checked'), false, "changing the value property changes the DOM"); }); QUnit.test("checking the checkbox updates the value", function() { checkboxView = Checkbox['default'].create({ checked: true }); append(); equal(property_get.get(checkboxView, 'checked'), true, "precond - initially starts with a true value"); equal(!!checkboxView.$().prop('checked'), true, "precond - the initial checked property is true"); // IE fires 'change' event on blur. checkboxView.$()[0].focus(); checkboxView.$()[0].click(); checkboxView.$()[0].blur(); equal(!!checkboxView.$().prop('checked'), false, "after clicking a checkbox, the checked property changed"); equal(property_get.get(checkboxView, 'checked'), false, "changing the checkbox causes the view's value to get updated"); }); }); enifed('ember-views/tests/views/checkbox_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/checkbox_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/checkbox_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/checkbox_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/checkbox_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/checkbox_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/collection_test', ['ember-metal/core', 'ember-metal/property_set', 'ember-metal/property_get', 'ember-metal/run_loop', 'ember-metal/enumerable_utils', 'ember-metal/mixin', 'ember-runtime/system/string', 'ember-runtime/system/array_proxy', 'ember-runtime/controllers/array_controller', 'ember-views/system/jquery', 'ember-views/views/collection_view', 'ember-views/views/view'], function (Ember, property_set, property_get, run, enumerable_utils, mixin, string, ArrayProxy, ArrayController, jQuery, CollectionView, View) { 'use strict'; var trim = jQuery['default'].trim; var view; var originalLookup; QUnit.module("CollectionView", { setup: function() { CollectionView['default'].CONTAINER_MAP.del = 'em'; originalLookup = Ember['default'].lookup; }, teardown: function() { delete CollectionView['default'].CONTAINER_MAP.del; run['default'](function() { if (view) { view.destroy(); } }); Ember['default'].lookup = originalLookup; } }); QUnit.test("should render a view for each item in its content array", function() { view = CollectionView['default'].create({ content: Ember['default'].A([1, 2, 3, 4]) }); run['default'](function() { view.append(); }); equal(view.$('div').length, 4); }); QUnit.test("should render the emptyView if content array is empty (view class)", function() { view = CollectionView['default'].create({ tagName: 'del', content: Ember['default'].A(), emptyView: View['default'].extend({ tagName: 'kbd', render: function(buf) { buf.push("OY SORRY GUVNAH NO NEWS TODAY EH"); } }) }); run['default'](function() { view.append(); }); ok(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, "displays empty view"); }); QUnit.test("should render the emptyView if content array is empty (view instance)", function() { view = CollectionView['default'].create({ tagName: 'del', content: Ember['default'].A(), emptyView: View['default'].create({ tagName: 'kbd', render: function(buf) { buf.push("OY SORRY GUVNAH NO NEWS TODAY EH"); } }) }); run['default'](function() { view.append(); }); ok(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, "displays empty view"); }); QUnit.test("should be able to override the tag name of itemViewClass even if tag is in default mapping", function() { view = CollectionView['default'].create({ tagName: 'del', content: Ember['default'].A(['NEWS GUVNAH']), itemViewClass: View['default'].extend({ tagName: 'kbd', render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); ok(view.$().find('kbd:contains("NEWS GUVNAH")').length, "displays the item view with proper tag name"); }); QUnit.test("should allow custom item views by setting itemViewClass", function() { var passedContents = []; view = CollectionView['default'].create({ content: Ember['default'].A(['foo', 'bar', 'baz']), itemViewClass: View['default'].extend({ render: function(buf) { passedContents.push(property_get.get(this, 'content')); buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); deepEqual(passedContents, ['foo', 'bar', 'baz'], "sets the content property on each item view"); enumerable_utils.forEach(passedContents, function(item) { equal(view.$(':contains("'+item+'")').length, 1); }); }); QUnit.test("should insert a new item in DOM when an item is added to the content array", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.insertAt(1, 'quux'); }); equal(trim(view.$(':nth-child(2)').text()), 'quux'); }); QUnit.test("should remove an item from DOM when an item is removed from the content array", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.removeAt(1); }); enumerable_utils.forEach(content, function(item, idx) { equal(view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text(), item); }); }); QUnit.test("it updates the view if an item is replaced", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.removeAt(1); content.insertAt(1, "Kazuki"); }); enumerable_utils.forEach(content, function(item, idx) { equal(trim(view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); QUnit.test("can add and replace in the same runloop", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.pushObject("Tom Dale"); content.removeAt(0); content.insertAt(0, "Kazuki"); }); enumerable_utils.forEach(content, function(item, idx) { equal(trim(view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); QUnit.test("can add and replace the object before the add in the same runloop", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.pushObject("Tom Dale"); content.removeAt(1); content.insertAt(1, "Kazuki"); }); enumerable_utils.forEach(content, function(item, idx) { equal(trim(view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); QUnit.test("can add and replace complicatedly", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.pushObject("Tom Dale"); content.removeAt(1); content.insertAt(1, "Kazuki"); content.pushObject("Firestone"); content.pushObject("McMunch"); }); enumerable_utils.forEach(content, function(item, idx) { equal(trim(view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update: "+item.name+"!="+view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text()); }); }); QUnit.test("can add and replace complicatedly harder", function() { var content = Ember['default'].A(['foo', 'bar', 'baz']); view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }) }); run['default'](function() { view.append(); }); enumerable_utils.forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run['default'](function() { content.pushObject("Tom Dale"); content.removeAt(1); content.insertAt(1, "Kazuki"); content.pushObject("Firestone"); content.pushObject("McMunch"); content.removeAt(2); }); enumerable_utils.forEach(content, function(item, idx) { equal(trim(view.$(string.fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); QUnit.test("should allow changes to content object before layer is created", function() { view = CollectionView['default'].create({ content: null }); run['default'](function() { property_set.set(view, 'content', Ember['default'].A()); property_set.set(view, 'content', Ember['default'].A([1, 2, 3])); property_set.set(view, 'content', Ember['default'].A([1, 2])); view.append(); }); ok(view.$().children().length); }); QUnit.test("should fire life cycle events when elements are added and removed", function() { var view; var didInsertElement = 0; var willDestroyElement = 0; var willDestroy = 0; var destroy = 0; var content = Ember['default'].A([1, 2, 3]); run['default'](function () { view = CollectionView['default'].create({ content: content, itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); }, didInsertElement: function () { didInsertElement++; }, willDestroyElement: function () { willDestroyElement++; }, willDestroy: function () { willDestroy++; this._super.apply(this, arguments); }, destroy: function() { destroy++; this._super.apply(this, arguments); } }) }); view.appendTo('#qunit-fixture'); }); equal(didInsertElement, 3); equal(willDestroyElement, 0); equal(willDestroy, 0); equal(destroy, 0); equal(view.$().text(), '123'); run['default'](function () { content.pushObject(4); content.unshiftObject(0); }); equal(didInsertElement, 5); equal(willDestroyElement, 0); equal(willDestroy, 0); equal(destroy, 0); // Remove whitespace added by IE 8 equal(trim(view.$().text()), '01234'); run['default'](function () { content.popObject(); content.shiftObject(); }); equal(didInsertElement, 5); equal(willDestroyElement, 2); equal(willDestroy, 2); equal(destroy, 2); // Remove whitspace added by IE 8 equal(trim(view.$().text()), '123'); run['default'](function () { view.set('content', Ember['default'].A([7,8,9])); }); equal(didInsertElement, 8); equal(willDestroyElement, 5); equal(willDestroy, 5); equal(destroy, 5); // Remove whitespace added by IE 8 equal(trim(view.$().text()), '789'); run['default'](function () { view.destroy(); }); equal(didInsertElement, 8); equal(willDestroyElement, 8); equal(willDestroy, 8); equal(destroy, 8); }); QUnit.test("should allow changing content property to be null", function() { view = CollectionView['default'].create({ content: Ember['default'].A([1, 2, 3]), emptyView: View['default'].extend({ template: function() { return "(empty)"; } }) }); run['default'](function() { view.append(); }); equal(view.$().children().length, 3, "precond - creates three elements"); run['default'](function() { property_set.set(view, 'content', null); }); equal(trim(view.$().children().text()), "(empty)", "should display empty view"); }); QUnit.test("should allow items to access to the CollectionView's current index in the content array", function() { view = CollectionView['default'].create({ content: Ember['default'].A(['zero', 'one', 'two']), itemViewClass: View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'contentIndex')); } }) }); run['default'](function() { view.append(); }); deepEqual(view.$(':nth-child(1)').text(), "0"); deepEqual(view.$(':nth-child(2)').text(), "1"); deepEqual(view.$(':nth-child(3)').text(), "2"); }); QUnit.test("should allow declaration of itemViewClass as a string", function() { var container = { lookupFactory: function() { return Ember['default'].View.extend(); } }; view = CollectionView['default'].create({ container: container, content: Ember['default'].A([1, 2, 3]), itemViewClass: 'simple-view' }); run['default'](function() { view.appendTo('#qunit-fixture'); }); equal(view.$('.ember-view').length, 3); }); QUnit.test("should not render the emptyView if content is emptied and refilled in the same run loop", function() { view = CollectionView['default'].create({ tagName: 'div', content: Ember['default'].A(['NEWS GUVNAH']), emptyView: View['default'].extend({ tagName: 'kbd', render: function(buf) { buf.push("OY SORRY GUVNAH NO NEWS TODAY EH"); } }) }); run['default'](function() { view.append(); }); equal(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, 0); run['default'](function() { view.get('content').popObject(); view.get('content').pushObject(['NEWS GUVNAH']); }); equal(view.$('div').length, 1); equal(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, 0); }); QUnit.test("a array_proxy that backs an sorted array_controller that backs a collection view functions properly", function() { var array = Ember['default'].A([{ name: "Other Katz" }]); var arrayProxy = ArrayProxy['default'].create({ content: array }); var sortedController = ArrayController['default'].create({ content: arrayProxy, sortProperties: ['name'] }); var container = CollectionView['default'].create({ content: sortedController }); run['default'](function() { container.appendTo('#qunit-fixture'); }); run['default'](function() { arrayProxy.addObjects([{ name: "Scumbag Demon" }, { name: "Lord British" }]); }); equal(container.get('content.length'), 3, 'ArrayController should have 3 entries'); equal(container.get('content.content.length'), 3, 'RecordArray should have 3 entries'); equal(container.get('childViews.length'), 3, 'CollectionView should have 3 entries'); run['default'](function() { container.destroy(); }); }); QUnit.test("when a collection view is emptied, deeply nested views elements are not removed from the DOM and then destroyed again", function() { var assertProperDestruction = mixin.Mixin.create({ destroyElement: function() { if (this._state === 'inDOM') { ok(this.get('element'), this + ' still exists in DOM'); } return this._super.apply(this, arguments); } }); var ChildView = View['default'].extend(assertProperDestruction, { render: function(buf) { // emulate nested template this.appendChild(View['default'].createWithMixins(assertProperDestruction, { template: function() { return "
    "; } })); } }); var view = CollectionView['default'].create({ content: Ember['default'].A([1]), itemViewClass: ChildView }); run['default'](function() { view.append(); }); equal(jQuery['default']('.inner_element').length, 1, "precond - generates inner element"); run['default'](function() { view.get('content').clear(); }); equal(jQuery['default']('.inner_element').length, 0, "elements removed"); run['default'](function() { view.remove(); }); }); QUnit.test("should render the emptyView if content array is empty and emptyView is given as string", function() { Ember['default'].lookup = { App: { EmptyView: View['default'].extend({ tagName: 'kbd', render: function(buf) { buf.push("THIS IS AN EMPTY VIEW"); } }) } }; view = CollectionView['default'].create({ tagName: 'del', content: Ember['default'].A(), emptyView: 'App.EmptyView' }); run['default'](function() { view.append(); }); ok(view.$().find('kbd:contains("THIS IS AN EMPTY VIEW")').length, "displays empty view"); }); QUnit.test("should lookup against the container if itemViewClass is given as a string", function() { var ItemView = View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView['default'].create({ container: container, content: Ember['default'].A([1, 2, 3, 4]), itemViewClass: 'item' }); run['default'](function() { view.appendTo('#qunit-fixture'); }); equal(view.$('.ember-view').length, 4); function lookupFactory(fullName) { equal(fullName, 'view:item'); return ItemView; } }); QUnit.test("should lookup only global path against the container if itemViewClass is given as a string", function() { var ItemView = View['default'].extend({ render: function(buf) { buf.push(property_get.get(this, 'content')); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView['default'].create({ container: container, content: Ember['default'].A(['hi']), itemViewClass: 'top' }); run['default'](function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), 'hi'); function lookupFactory(fullName) { equal(fullName, 'view:top'); return ItemView; } }); QUnit.test("should lookup against the container and render the emptyView if emptyView is given as string and content array is empty ", function() { var EmptyView = View['default'].extend({ tagName: 'kbd', render: function(buf) { buf.push("THIS IS AN EMPTY VIEW"); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView['default'].create({ container: container, tagName: 'del', content: Ember['default'].A(), emptyView: 'empty' }); run['default'](function() { view.append(); }); ok(view.$().find('kbd:contains("THIS IS AN EMPTY VIEW")').length, "displays empty view"); function lookupFactory(fullName) { equal(fullName, 'view:empty'); return EmptyView; } }); QUnit.test("should lookup from only global path against the container if emptyView is given as string and content array is empty ", function() { var EmptyView = View['default'].extend({ render: function(buf) { buf.push("EMPTY"); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView['default'].create({ container: container, content: Ember['default'].A(), emptyView: 'top' }); run['default'](function() { view.append(); }); equal(view.$().text(), "EMPTY"); function lookupFactory(fullName) { equal(fullName, 'view:top'); return EmptyView; } }); }); enifed('ember-views/tests/views/collection_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/collection_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/collection_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/collection_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/collection_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/collection_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/component_test', ['ember-metal/property_set', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-runtime/system/service', 'ember-runtime/system/container', 'ember-runtime/inject', 'ember-metal/property_get', 'ember-views/views/view', 'ember-views/views/component'], function (property_set, run, EmberObject, Service, system__container, inject, property_get, EmberView, Component) { 'use strict'; var a_slice = Array.prototype.slice; var component, controller, actionCounts, sendCount, actionArguments; QUnit.module("Ember.Component", { setup: function() { component = Component['default'].create(); }, teardown: function() { run['default'](function() { if (component) { component.destroy(); } if (controller) { controller.destroy(); } }); } }); QUnit.test("The context of an Ember.Component is itself", function() { strictEqual(component, component.get('context'), "A component's context is itself"); }); QUnit.test("The controller (target of `action`) of an Ember.Component is itself", function() { strictEqual(component, component.get('controller'), "A component's controller is itself"); }); QUnit.test("A templateName specified to a component is moved to the layoutName", function() { expectDeprecation(/Do not specify templateName on a Component, use layoutName instead/); component = Component['default'].extend({ templateName: 'blah-blah' }).create(); equal(component.get('layoutName'), 'blah-blah', "The layoutName now contains the templateName specified."); }); QUnit.test("A template specified to a component is moved to the layout", function() { expectDeprecation(/Do not specify template on a Component, use layout instead/); component = Component['default'].extend({ template: 'blah-blah' }).create(); equal(component.get('layout'), 'blah-blah', "The layoutName now contains the templateName specified."); }); QUnit.test("A template specified to a component is deprecated", function() { expectDeprecation(function() { component = Component['default'].extend({ template: 'blah-blah' }).create(); }, 'Do not specify template on a Component, use layout instead.'); }); QUnit.test("A templateName specified to a component is deprecated", function() { expectDeprecation(function() { component = Component['default'].extend({ templateName: 'blah-blah' }).create(); }, 'Do not specify templateName on a Component, use layoutName instead.'); }); QUnit.test("Specifying both templateName and layoutName to a component is NOT deprecated", function() { expectNoDeprecation(); component = Component['default'].extend({ templateName: 'blah-blah', layoutName: 'hum-drum' }).create(); equal(property_get.get(component, 'templateName'), 'blah-blah'); equal(property_get.get(component, 'layoutName'), 'hum-drum'); }); QUnit.test("Specifying a templateName on a component with a layoutName specified in a superclass is NOT deprecated", function() { expectNoDeprecation(); var Parent = Component['default'].extend({ layoutName: 'hum-drum' }); component = Parent.extend({ templateName: 'blah-blah' }).create(); equal(property_get.get(component, 'templateName'), 'blah-blah'); equal(property_get.get(component, 'layoutName'), 'hum-drum'); }); QUnit.module("Ember.Component - Actions", { setup: function() { actionCounts = {}; sendCount = 0; actionArguments = null; controller = EmberObject['default'].create({ send: function(actionName) { sendCount++; actionCounts[actionName] = actionCounts[actionName] || 0; actionCounts[actionName]++; actionArguments = a_slice.call(arguments, 1); } }); component = Component['default'].create({ _parentView: EmberView['default'].create({ controller: controller }) }); }, teardown: function() { run['default'](function() { component.destroy(); controller.destroy(); }); } }); QUnit.test("Calling sendAction on a component without an action defined does nothing", function() { component.sendAction(); equal(sendCount, 0, "addItem action was not invoked"); }); QUnit.test("Calling sendAction on a component with an action defined calls send on the controller", function() { property_set.set(component, 'action', "addItem"); component.sendAction(); equal(sendCount, 1, "send was called once"); equal(actionCounts['addItem'], 1, "addItem event was sent once"); }); QUnit.test("Calling sendAction with a named action uses the component's property as the action name", function() { property_set.set(component, 'playing', "didStartPlaying"); property_set.set(component, 'action', "didDoSomeBusiness"); component.sendAction('playing'); equal(sendCount, 1, "send was called once"); equal(actionCounts['didStartPlaying'], 1, "named action was sent"); component.sendAction('playing'); equal(sendCount, 2, "send was called twice"); equal(actionCounts['didStartPlaying'], 2, "named action was sent"); component.sendAction(); equal(sendCount, 3, "send was called three times"); equal(actionCounts['didDoSomeBusiness'], 1, "default action was sent"); }); QUnit.test("Calling sendAction when the action name is not a string raises an exception", function() { property_set.set(component, 'action', {}); property_set.set(component, 'playing', {}); expectAssertion(function() { component.sendAction(); }); expectAssertion(function() { component.sendAction('playing'); }); }); QUnit.test("Calling sendAction on a component with a context", function() { property_set.set(component, 'playing', "didStartPlaying"); var testContext = { song: 'She Broke My Ember' }; component.sendAction('playing', testContext); deepEqual(actionArguments, [testContext], "context was sent with the action"); }); QUnit.test("Calling sendAction on a component with multiple parameters", function() { property_set.set(component, 'playing', "didStartPlaying"); var firstContext = { song: 'She Broke My Ember' }; var secondContext = { song: 'My Achey Breaky Ember' }; component.sendAction('playing', firstContext, secondContext); deepEqual(actionArguments, [firstContext, secondContext], "arguments were sent to the action"); }); QUnit.module('Ember.Component - injected properties'); QUnit.test("services can be injected into components", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('component:application', Component['default'].extend({ profilerService: inject['default'].service('profiler') })); registry.register('service:profiler', Service['default'].extend()); var appComponent = container.lookup('component:application'); var profilerService = container.lookup('service:profiler'); equal(profilerService, appComponent.get('profilerService'), "service.profiler is injected"); }); QUnit.module('Ember.Component - subscribed and sent actions trigger errors'); QUnit.test('something', function() { expect(2); var appComponent = Component['default'].extend({ actions: { foo: function(message) { equal('bar', message); } } }).create(); appComponent.send('foo', 'bar'); throws(function() { appComponent.send('baz', 'bar'); }, /had no action handler for: baz/, 'asdf'); }); QUnit.test('component with target', function() { expect(2); var target = { send: function(message, payload) { equal('foo', message); equal('baz', payload); } }; var appComponent = Component['default'].create({ target: target }); appComponent.send('foo', 'baz'); }); }); enifed('ember-views/tests/views/component_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/component_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/component_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/component_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/component_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/component_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/container_view_test', ['ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-metal/computed', 'ember-metal/streams/utils', 'ember-runtime/controllers/controller', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, property_set, run, computed, utils, Controller, jQuery, View, ContainerView) { 'use strict'; var trim = jQuery['default'].trim; var container, view, otherContainer; QUnit.module("ember-views/views/container_view_test", { teardown: function() { run['default'](function() { container.destroy(); if (view) { view.destroy(); } if (otherContainer) { otherContainer.destroy(); } }); } }); QUnit.test("should be able to insert views after the DOM representation is created", function() { container = ContainerView['default'].create({ classNameBindings: ['name'], name: 'foo', container: {} }); run['default'](function() { container.appendTo('#qunit-fixture'); }); view = View['default'].create({ template: function() { return "This is my moment"; } }); run['default'](function() { container.pushObject(view); }); equal(view.container, container.container, 'view gains its containerViews container'); equal(view._parentView, container, 'view\'s _parentView is the container'); equal(trim(container.$().text()), "This is my moment"); run['default'](function() { container.destroy(); }); }); QUnit.test("should be able to observe properties that contain child views", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); run['default'](function() { var Container = ContainerView['default'].extend({ childViews: ['displayView'], displayIsDisplayed: computed.computed.alias('displayView.isDisplayed'), displayView: View['default'].extend({ isDisplayed: true }) }); container = Container.create(); container.appendTo('#qunit-fixture'); }); equal(container.get('displayIsDisplayed'), true, "can bind to child view"); run['default'](function () { container.set('displayView.isDisplayed', false); }); equal(container.get('displayIsDisplayed'), false, "can bind to child view"); }); QUnit.test("childViews inherit their parents iocContainer, and retain the original container even when moved", function() { container = ContainerView['default'].create({ container: {} }); otherContainer = ContainerView['default'].create({ container: {} }); view = View['default'].create(); container.pushObject(view); equal(view.get('parentView'), container, "sets the parent view after the childView is appended"); equal(property_get.get(view, 'container'), container.container, "inherits its parentViews iocContainer"); container.removeObject(view); equal(property_get.get(view, 'container'), container.container, "leaves existing iocContainer alone"); otherContainer.pushObject(view); equal(view.get('parentView'), otherContainer, "sets the new parent view after the childView is appended"); equal(property_get.get(view, 'container'), container.container, "still inherits its original parentViews iocContainer"); }); QUnit.test("should set the parentView property on views that are added to the child views array", function() { container = ContainerView['default'].create(); var ViewKlass = View['default'].extend({ template: function() { return "This is my moment"; } }); view = ViewKlass.create(); container.pushObject(view); equal(view.get('parentView'), container, "sets the parent view after the childView is appended"); run['default'](function() { container.removeObject(view); }); equal(property_get.get(view, 'parentView'), null, "sets parentView to null when a view is removed"); run['default'](function() { container.appendTo('#qunit-fixture'); }); run['default'](function() { container.pushObject(view); }); equal(property_get.get(view, 'parentView'), container, "sets the parent view after the childView is appended"); var secondView = ViewKlass.create(); var thirdView = ViewKlass.create(); var fourthView = ViewKlass.create(); run['default'](function() { container.pushObject(secondView); container.replace(1, 0, [thirdView, fourthView]); }); equal(property_get.get(secondView, 'parentView'), container, "sets the parent view of the second view"); equal(property_get.get(thirdView, 'parentView'), container, "sets the parent view of the third view"); equal(property_get.get(fourthView, 'parentView'), container, "sets the parent view of the fourth view"); run['default'](function() { container.replace(2, 2); }); equal(property_get.get(view, 'parentView'), container, "doesn't change non-removed view"); equal(property_get.get(thirdView, 'parentView'), container, "doesn't change non-removed view"); equal(property_get.get(secondView, 'parentView'), null, "clears the parent view of the third view"); equal(property_get.get(fourthView, 'parentView'), null, "clears the parent view of the fourth view"); run['default'](function() { secondView.destroy(); thirdView.destroy(); fourthView.destroy(); }); }); QUnit.test("should trigger parentViewDidChange when parentView is changed", function() { container = ContainerView['default'].create(); var secondContainer = ContainerView['default'].create(); var parentViewChanged = 0; var ViewKlass = View['default'].extend({ parentViewDidChange: function() { parentViewChanged++; } }); view = ViewKlass.create(); container.pushObject(view); container.removeChild(view); secondContainer.pushObject(view); equal(parentViewChanged, 3); run['default'](function() { secondContainer.destroy(); }); }); QUnit.test("should be able to push initial views onto the ContainerView and have it behave", function() { var Container = ContainerView['default'].extend({ init: function () { this._super.apply(this, arguments); this.pushObject(View['default'].create({ name: 'A', template: function () { return 'A'; } })); this.pushObject(View['default'].create({ name: 'B', template: function () { return 'B'; } })); }, // functions here avoid attaching an observer, which is // not supported. lengthSquared: function () { return this.get('length') * this.get('length'); }, mapViewNames: function() { return this.map(function(_view) { return _view.get('name'); }); } }); container = Container.create(); equal(container.lengthSquared(), 4); deepEqual(container.mapViewNames(), ['A','B']); run['default'](container, 'appendTo', '#qunit-fixture'); equal(container.$().text(), 'AB'); run['default'](function () { container.pushObject(View['default'].create({ name: 'C', template: function () { return 'C'; } })); }); equal(container.lengthSquared(), 9); deepEqual(container.mapViewNames(), ['A','B','C']); equal(container.$().text(), 'ABC'); run['default'](container, 'destroy'); }); QUnit.test("views that are removed from a ContainerView should have their child views cleared", function() { container = ContainerView['default'].create(); view = View['default'].createWithMixins({ remove: function() { this._super.apply(this, arguments); }, template: function(context, options) { options.data.view.appendChild(View['default']); } }); container.pushObject(view); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(property_get.get(view, 'childViews.length'), 1, "precond - renders one child view"); run['default'](function() { container.removeObject(view); }); equal(property_get.get(view, 'childViews.length'), 0, "child views are cleared when removed from container view"); equal(container.$().html(), '', "the child view is removed from the DOM"); }); QUnit.test("if a ContainerView starts with an empty currentView, nothing is displayed", function() { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), '', "has a empty contents"); equal(property_get.get(container, 'childViews.length'), 0, "should not have any child views"); }); QUnit.test("if a ContainerView starts with a currentView, it is rendered as a child view", function() { var controller = Controller['default'].create(); container = ContainerView['default'].create({ controller: controller }); var context = null; var mainView = View['default'].create({ template: function(ctx, opts) { context = ctx; return "This is the main view."; } }); property_set.set(container, 'currentView', mainView); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(trim(container.$().text()), "This is the main view.", "should render its child"); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); equal(mainView.get('parentView'), container, "parentView is setup"); equal(context, container.get('context'), 'context preserved'); equal(utils.read(mainView._keywords.controller), controller, 'controller keyword is setup'); equal(utils.read(mainView._keywords.view), mainView, 'view keyword is setup'); }); QUnit.test("if a ContainerView is created with a currentView, it is rendered as a child view", function() { var context = null; var mainView = View['default'].create({ template: function(ctx, opts) { context = ctx; return "This is the main view."; } }); var controller = Controller['default'].create(); container = ContainerView['default'].create({ currentView: mainView, controller: controller }); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); equal(mainView.get('parentView'), container, "parentView is setup"); equal(context, container.get('context'), 'context preserved'); equal(utils.read(mainView._keywords.controller), controller, 'controller keyword is setup'); equal(utils.read(mainView._keywords.view), mainView, 'view keyword is setup'); }); QUnit.test("if a ContainerView starts with no currentView and then one is set, the ContainerView is updated", function() { var context = null; var mainView = View['default'].create({ template: function(ctx, opts) { context = ctx; return "This is the main view."; } }); var controller = Controller['default'].create(); container = ContainerView['default'].create({ controller: controller }); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), '', "has a empty contents"); equal(property_get.get(container, 'childViews.length'), 0, "should not have any child views"); run['default'](function() { property_set.set(container, 'currentView', mainView); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); equal(mainView.get('parentView'), container, "parentView is setup"); equal(context, container.get('context'), 'context preserved'); equal(utils.read(mainView._keywords.controller), controller, 'controller keyword is setup'); equal(utils.read(mainView._keywords.view), mainView, 'view keyword is setup'); }); QUnit.test("if a ContainerView starts with a currentView and then is set to null, the ContainerView is updated", function() { var context = null; var mainView = View['default'].create({ template: function(ctx, opts) { context = ctx; return "This is the main view."; } }); var controller = Controller['default'].create(); container = ContainerView['default'].create({ controller: controller }); container.set('currentView', mainView); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); equal(mainView.get('parentView'), container, "parentView is setup"); equal(context, container.get('context'), 'context preserved'); equal(utils.read(mainView._keywords.controller), controller, 'controller keyword is setup'); equal(utils.read(mainView._keywords.view), mainView, 'view keyword is setup'); run['default'](function() { property_set.set(container, 'currentView', null); }); equal(container.$().text(), '', "has a empty contents"); equal(property_get.get(container, 'childViews.length'), 0, "should not have any child views"); }); QUnit.test("if a ContainerView starts with a currentView and then is set to null, the ContainerView is updated and the previous currentView is destroyed", function() { var context = null; var mainView = View['default'].create({ template: function(ctx, opts) { context = ctx; return "This is the main view."; } }); var controller = Controller['default'].create(); container = ContainerView['default'].create({ controller: controller }); container.set('currentView', mainView); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); equal(mainView.get('parentView'), container, "parentView is setup"); equal(context, container.get('context'), 'context preserved'); equal(utils.read(mainView._keywords.controller), controller, 'controller keyword is setup'); equal(utils.read(mainView._keywords.view), mainView, 'view keyword is setup'); run['default'](function() { property_set.set(container, 'currentView', null); }); equal(mainView.isDestroyed, true, 'should destroy the previous currentView.'); equal(container.$().text(), '', "has a empty contents"); equal(property_get.get(container, 'childViews.length'), 0, "should not have any child views"); }); QUnit.test("if a ContainerView starts with a currentView and then a different currentView is set, the old view is destroyed and the new one is added", function() { container = ContainerView['default'].create(); var mainView = View['default'].create({ template: function() { return "This is the main view."; } }); var secondaryView = View['default'].create({ template: function() { return "This is the secondary view."; } }); var tertiaryView = View['default'].create({ template: function() { return "This is the tertiary view."; } }); container.set('currentView', mainView); run['default'](function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); run['default'](function() { property_set.set(container, 'currentView', secondaryView); }); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), secondaryView, "should have the currentView as the only child view"); equal(mainView.isDestroyed, true, 'should destroy the previous currentView: mainView.'); equal(trim(container.$().text()), "This is the secondary view.", "should render its child"); run['default'](function() { property_set.set(container, 'currentView', tertiaryView); }); equal(property_get.get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), tertiaryView, "should have the currentView as the only child view"); equal(secondaryView.isDestroyed, true, 'should destroy the previous currentView: secondaryView.'); equal(trim(container.$().text()), "This is the tertiary view.", "should render its child"); }); QUnit.test("should be able to modify childViews many times during an run loop", function () { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var one = View['default'].create({ template: function() { return 'one'; } }); var two = View['default'].create({ template: function() { return 'two'; } }); var three = View['default'].create({ template: function() { return 'three'; } }); run['default'](function() { // initial order container.pushObjects([three, one, two]); // sort container.removeObject(three); container.pushObject(three); }); // Remove whitespace added by IE 8 equal(trim(container.$().text()), 'onetwothree'); }); QUnit.test("should be able to modify childViews then remove the ContainerView in same run loop", function () { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var count = 0; var child = View['default'].create({ template: function () { count++; return 'child'; } }); run['default'](function() { container.pushObject(child); container.remove(); }); equal(count, 0, 'did not render child'); }); QUnit.test("should be able to modify childViews then destroy the ContainerView in same run loop", function () { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var count = 0; var child = View['default'].create({ template: function () { count++; return 'child'; } }); run['default'](function() { container.pushObject(child); container.destroy(); }); equal(count, 0, 'did not render child'); }); QUnit.test("should be able to modify childViews then rerender the ContainerView in same run loop", function () { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var count = 0; var child = View['default'].create({ template: function () { count++; return 'child'; } }); run['default'](function() { container.pushObject(child); container.rerender(); }); // TODO: Fix with Priority Queue for now ensure valid rendering //equal(count, 1, 'rendered child only once'); equal(trim(container.$().text()), 'child'); }); QUnit.test("should be able to modify childViews then rerender then modify again the ContainerView in same run loop", function () { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var Child = View['default'].extend({ count: 0, render: function (buffer) { this.count++; buffer.push(this.label); } }); var one = Child.create({ label: 'one' }); var two = Child.create({ label: 'two' }); run['default'](function() { container.pushObject(one); container.pushObject(two); }); equal(one.count, 1, 'rendered one.count child only once'); equal(two.count, 1, 'rendered two.count child only once'); // Remove whitespace added by IE 8 equal(trim(container.$().text()), 'onetwo'); }); QUnit.test("should be able to modify childViews then rerender again the ContainerView in same run loop and then modify again", function () { container = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var Child = View['default'].extend({ count: 0, render: function (buffer) { this.count++; buffer.push(this.label); } }); var one = Child.create({ label: 'one' }); var two = Child.create({ label: 'two' }); run['default'](function() { container.pushObject(one); container.rerender(); }); // TODO: Fix with Priority Queue for now ensure valid rendering //equal(one.count, 1, 'rendered one child only once'); equal(container.$().text(), 'one'); run['default'](function () { container.pushObject(two); }); // TODO: Fix with Priority Queue for now ensure valid rendering //equal(one.count, 1, 'rendered one child only once'); equal(two.count, 1, 'rendered two child only once'); // IE 8 adds a line break but this shouldn't affect validity equal(trim(container.$().text()), 'onetwo'); }); QUnit.test("should invalidate `element` on itself and childViews when being rendered by ensureChildrenAreInDOM", function () { expectDeprecation("Setting `childViews` on a Container is deprecated."); var root = ContainerView['default'].create(); view = View['default'].create({ template: function() {} }); container = ContainerView['default'].create({ childViews: ['child'], child: view }); run['default'](function() { root.appendTo('#qunit-fixture'); }); run['default'](function() { root.pushObject(container); // Get the parent and child's elements to cause them to be cached as null container.get('element'); view.get('element'); }); ok(!!container.get('element'), "Parent's element should have been recomputed after being rendered"); ok(!!view.get('element'), "Child's element should have been recomputed after being rendered"); run['default'](function() { root.destroy(); }); }); QUnit.test("Child view can only be added to one container at a time", function () { expect(2); container = ContainerView['default'].create(); var secondContainer = ContainerView['default'].create(); run['default'](function() { container.appendTo('#qunit-fixture'); }); var view = View['default'].create(); run['default'](function() { container.set('currentView', view); }); expectAssertion(function() { run['default'](function() { secondContainer.set('currentView', view); }); }); expectAssertion(function() { run['default'](function() { secondContainer.pushObject(view); }); }); run['default'](function() { secondContainer.destroy(); }); }); QUnit.test("if a containerView appends a child in its didInsertElement event, the didInsertElement event of the child view should be fired once", function () { var counter = 0; var root = ContainerView['default'].create({}); container = ContainerView['default'].create({ didInsertElement: function() { var view = ContainerView['default'].create({ didInsertElement: function() { counter++; } }); this.pushObject(view); } }); run['default'](function() { root.appendTo('#qunit-fixture'); }); run['default'](function() { root.pushObject(container); }); equal(container.get('childViews').get('length'), 1 , "containerView should only have a child"); equal(counter, 1 , "didInsertElement should be fired once"); run['default'](function() { root.destroy(); }); }); QUnit.test("ContainerView is observable [DEPRECATED]", function() { container = ContainerView['default'].create(); var observerFired = false; expectDeprecation(function() { container.addObserver('this.[]', function() { observerFired = true; }); }, /ContainerViews should not be observed as arrays. This behavior will change in future implementations of ContainerView./); ok(!observerFired, 'Nothing changed, no observer fired'); container.pushObject(View['default'].create()); ok(observerFired, 'View pushed, observer fired'); }); }); enifed('ember-views/tests/views/container_view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/container_view_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/container_view_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/container_view_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/container_view_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/container_view_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/instrumentation_test', ['ember-metal/instrumentation', 'ember-metal/run_loop', 'ember-views/views/view'], function (instrumentation, run, EmberView) { 'use strict'; var view, beforeCalls, afterCalls; function confirmPayload(payload, view) { equal(payload && payload.object, view.toString(), 'payload object equals view.toString()'); equal(payload && payload.containerKey, view._debugContainerKey, 'payload contains the containerKey'); equal(payload && payload.view, view, 'payload contains the view itself'); } QUnit.module("EmberView#instrumentation", { setup: function () { beforeCalls = []; afterCalls = []; instrumentation.subscribe("render", { before: function(name, timestamp, payload) { beforeCalls.push(payload); }, after: function(name, timestamp, payload) { afterCalls.push(payload); } }); view = EmberView['default'].create({ _debugContainerKey: 'suchryzsd', instrumentDisplay: 'asdfasdfmewj' }); }, teardown: function() { if (view) { run['default'](view, 'destroy'); } instrumentation.reset(); } }); QUnit.test("generates the proper instrumentation details when called directly", function() { var payload = {}; view.instrumentDetails(payload); confirmPayload(payload, view); }); QUnit.test("should add ember-view to views", function() { run['default'](view, 'createElement'); confirmPayload(beforeCalls[0], view); }); }); enifed('ember-views/tests/views/instrumentation_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/instrumentation_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/instrumentation_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/instrumentation_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/instrumentation_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/instrumentation_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/metamorph_view_test', ['ember-views/system/jquery', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-template-compiler/system/compile', 'ember-views/views/metamorph_view'], function (jQuery, run, EmberView, property_get, property_set, compile, _MetamorphView) { 'use strict'; var view, childView, metamorphView; QUnit.module("Metamorph views", { setup: function() { view = EmberView['default'].create({ render: function(buffer) { buffer.push("

    View

    "); this.appendChild(metamorphView); } }); }, teardown: function() { run['default'](function() { view.destroy(); if (childView && !childView.isDestroyed) { childView.destroy(); } if (metamorphView && !metamorphView.isDestroyed) { metamorphView.destroy(); } }); } }); QUnit.test("a Metamorph view is not a view's parentView", function() { childView = EmberView['default'].create({ render: function(buffer) { buffer.push("

    Bye bros

    "); } }); metamorphView = _MetamorphView['default'].create({ render: function(buffer) { buffer.push("

    Meta

    "); this.appendChild(childView); } }); run['default'](function() { view.appendTo("#qunit-fixture"); }); equal(property_get.get(childView, 'parentView'), view, "A child of a metamorph view cannot see the metamorph view as its parent"); var children = property_get.get(view, 'childViews'); equal(property_get.get(children, 'length'), 1, "precond - there is only one child of the main node"); equal(children.objectAt(0), childView, "... and it is not the metamorph"); }); QUnit.module("Metamorph views correctly handle DOM", { setup: function() { view = EmberView['default'].create({ render: function(buffer) { buffer.push("

    View

    "); this.appendChild(metamorphView); } }); metamorphView = _MetamorphView['default'].create({ powerRanger: "Jason", render: function(buffer) { buffer.push("

    "+property_get.get(this, 'powerRanger')+"

    "); } }); run['default'](function() { view.appendTo("#qunit-fixture"); }); }, teardown: function() { run['default'](function() { view.destroy(); if (!metamorphView.isDestroyed) { metamorphView.destroy(); } }); } }); QUnit.test("a metamorph view generates without a DOM node", function() { var meta = jQuery['default']("> h2", "#" + property_get.get(view, 'elementId')); equal(meta.length, 1, "The metamorph element should be directly inside its parent"); }); QUnit.test("a metamorph view can be removed from the DOM", function() { run['default'](function() { metamorphView.destroy(); }); var meta = jQuery['default']('#from-morph'); equal(meta.length, 0, "the associated DOM was removed"); }); QUnit.test("a metamorph view can be rerendered", function() { equal(jQuery['default']('#from-meta').text(), "Jason", "precond - renders to the DOM"); property_set.set(metamorphView, 'powerRanger', 'Trini'); run['default'](function() { metamorphView.rerender(); }); equal(jQuery['default']('#from-meta').text(), "Trini", "updates value when re-rendering"); }); // Redefining without setup/teardown QUnit.module("Metamorph views correctly handle DOM"); QUnit.test("a metamorph view calls its children's willInsertElement and didInsertElement", function() { var parentView; var willInsertElementCalled = false; var didInsertElementCalled = false; var didInsertElementSawElement = false; parentView = EmberView['default'].create({ ViewWithCallback: EmberView['default'].extend({ template: compile['default']('
    '), willInsertElement: function() { willInsertElementCalled = true; }, didInsertElement: function() { didInsertElementCalled = true; didInsertElementSawElement = (this.$('div').length === 1); } }), template: compile['default']('{{#if view.condition}}{{view view.ViewWithCallback}}{{/if}}'), condition: false }); run['default'](function() { parentView.append(); }); run['default'](function() { parentView.set('condition', true); }); ok(willInsertElementCalled, "willInsertElement called"); ok(didInsertElementCalled, "didInsertElement called"); ok(didInsertElementSawElement, "didInsertElement saw element"); run['default'](function() { parentView.destroy(); }); }); QUnit.test("replacing a Metamorph should invalidate childView elements", function() { var elementOnDidInsert; view = EmberView['default'].create({ show: false, CustomView: EmberView['default'].extend({ init: function() { this._super.apply(this, arguments); // This will be called in preRender // We want it to cache a null value // Hopefully it will be invalidated when `show` is toggled this.get('element'); }, didInsertElement: function() { elementOnDidInsert = this.get('element'); } }), template: compile['default']("{{#if view.show}}{{view view.CustomView}}{{/if}}") }); run['default'](function() { view.append(); }); run['default'](function() { view.set('show', true); }); ok(elementOnDidInsert, "should have an element on insert"); run['default'](function() { view.destroy(); }); }); QUnit.test("trigger rerender of parent and SimpleBoundView", function () { var view = EmberView['default'].create({ show: true, foo: 'bar', template: compile['default']("{{#if view.show}}{{#if view.foo}}{{view.foo}}{{/if}}{{/if}}") }); run['default'](function() { view.append(); }); equal(view.$().text(), 'bar'); run['default'](function() { view.set('foo', 'baz'); // schedule render of simple bound view.set('show', false); // destroy tree }); equal(view.$().text(), ''); run['default'](function() { view.destroy(); }); }); QUnit.test("re-rendering and then changing the property does not raise an exception", function() { view = EmberView['default'].create({ show: true, foo: 'bar', metamorphView: _MetamorphView['default'], template: compile['default']("{{#view view.metamorphView}}truth{{/view}}") }); run['default'](function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), 'truth'); run['default'](function() { view._childViews[0].rerender(); view._childViews[0].rerender(); }); equal(view.$().text(), 'truth'); run['default'](function() { view.destroy(); }); }); }); enifed('ember-views/tests/views/metamorph_view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/metamorph_view_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/metamorph_view_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/metamorph_view_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/metamorph_view_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/metamorph_view_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/select_test', ['ember-views/views/select', 'ember-runtime/system/object', 'ember-metal/run_loop', 'ember-views/system/jquery', 'ember-metal/enumerable_utils', 'ember-views/system/event_dispatcher'], function (EmberSelect, EmberObject, run, jQuery, enumerable_utils, EventDispatcher) { 'use strict'; var trim = jQuery['default'].trim; var dispatcher, select; QUnit.module("Ember.Select", { setup: function() { dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); select = EmberSelect['default'].create(); }, teardown: function() { run['default'](function() { dispatcher.destroy(); select.destroy(); }); } }); function append() { run['default'](function() { select.appendTo('#qunit-fixture'); }); } function selectedOptions() { return select.get('childViews').mapBy('selected'); } QUnit.test("has 'ember-view' and 'ember-select' CSS classes", function() { deepEqual(select.get('classNames'), ['ember-view', 'ember-select']); }); QUnit.test("should render", function() { append(); ok(select.$().length, "Select renders"); }); QUnit.test("should begin disabled if the disabled attribute is true", function() { select.set('disabled', true); append(); ok(select.$().is(":disabled")); }); // Browsers before IE10 do not support the required property. if (document && ('required' in document.createElement('input'))) { QUnit.test("should begin required if the required attribute is true", function() { select.set('required', true); append(); ok(select.element.required, 'required property is truthy'); }); QUnit.test("should become required if the required attribute is changed", function() { append(); ok(!select.element.required, 'required property is falsy'); run['default'](function() { select.set('required', true); }); ok(select.element.required, 'required property is truthy'); run['default'](function() { select.set('required', false); }); ok(!select.element.required, 'required property is falsy'); }); } QUnit.test("should become disabled if the disabled attribute is changed", function() { append(); ok(!select.element.disabled, 'disabled property is falsy'); run['default'](function() { select.set('disabled', true); }); ok(select.element.disabled, 'disabled property is truthy'); run['default'](function() { select.set('disabled', false); }); ok(!select.element.disabled, 'disabled property is falsy'); }); QUnit.test("can have options", function() { select.set('content', Ember.A([1, 2, 3])); append(); equal(select.$('option').length, 3, "Should have three options"); // IE 8 adds whitespace equal(trim(select.$().text()), "123", "Options should have content"); }); QUnit.test("select tabindex is updated when setting tabindex property of view", function() { run['default'](function() { select.set('tabindex', '4'); }); append(); equal(select.$().attr('tabindex'), "4", "renders select with the tabindex"); run['default'](function() { select.set('tabindex', '1'); }); equal(select.$().attr('tabindex'), "1", "updates select after tabindex changes"); }); QUnit.test("select name is updated when setting name property of view", function() { run['default'](function() { select.set('name', 'foo'); }); append(); equal(select.$().attr('name'), "foo", "renders select with the name"); run['default'](function() { select.set('name', 'bar'); }); equal(select.$().attr('name'), "bar", "updates select after name changes"); }); QUnit.test("can specify the property path for an option's label and value", function() { select.set('content', Ember.A([ { id: 1, firstName: 'Yehuda' }, { id: 2, firstName: 'Tom' } ])); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); append(); equal(select.$('option').length, 2, "Should have two options"); // IE 8 adds whitespace equal(trim(select.$().text()), "YehudaTom", "Options should have content"); deepEqual(enumerable_utils.map(select.$('option').toArray(), function(el) { return jQuery['default'](el).attr('value'); }), ["1", "2"], "Options should have values"); }); QUnit.test("can retrieve the current selected option when multiple=false", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; select.set('content', Ember.A([yehuda, tom])); append(); equal(select.get('selection'), yehuda, "By default, the first option is selected"); select.$()[0].selectedIndex = 1; // select Tom select.$().trigger('change'); equal(select.get('selection'), tom, "On change, the new option should be selected"); }); QUnit.test("can retrieve the current selected options when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.firstName'); append(); deepEqual(select.get('selection'), [], "By default, nothing is selected"); select.$('option').each(function() { if (this.value === 'Tom' || this.value === 'David') { this.selected = true; } }); select.$().trigger('change'); deepEqual(select.get('selection'), [tom, david], "On change, the new options should be selected"); }); QUnit.test("selection can be set when multiple=false", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom])); select.set('multiple', false); select.set('selection', tom); }); append(); equal(select.get('selection'), tom, "Initial selection should be correct"); run['default'](function() { select.set('selection', yehuda); }); equal(select.$()[0].selectedIndex, 0, "After changing it, selection should be correct"); }); QUnit.test("selection can be set from a Promise when multiple=false", function() { expect(1); var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom])); select.set('multiple', false); select.set('selection', Ember.RSVP.Promise.resolve(tom)); }); append(); equal(select.$()[0].selectedIndex, 1, "Should select from Promise content"); }); QUnit.test("selection from a Promise don't overwrite newer selection once resolved, when multiple=false", function() { expect(1); var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var seb = { id: 3, firstName: 'Seb' }; QUnit.stop(); run['default'](function() { select.set('content', Ember.A([yehuda, tom, seb])); select.set('multiple', false); select.set('selection', new Ember.RSVP.Promise(function(resolve, reject) { Ember.run.later(function() { run['default'](function() { resolve(tom); }); QUnit.start(); equal(select.$()[0].selectedIndex, 2, "Should not select from Promise if newer selection"); }, 40); })); select.set('selection', new Ember.RSVP.Promise(function(resolve, reject) { Ember.run.later(function() { run['default'](function() { resolve(seb); }); }, 30); })); }); append(); }); QUnit.test("selection from a Promise resolving to null should not select when multiple=false", function() { expect(1); var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom])); select.set('multiple', false); select.set('selection', Ember.RSVP.Promise.resolve(null)); }); append(); equal(select.$()[0].selectedIndex, -1, "Should not select any object when the Promise resolve to null"); }); QUnit.test("selection can be set when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('selection', tom); }); append(); deepEqual(select.get('selection'), [tom], "Initial selection should be correct"); run['default'](function() { select.set('selection', yehuda); }); deepEqual(select.get('selection'), [yehuda], "After changing it, selection should be correct"); }); QUnit.test("selection can be set when multiple=true and prompt", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('prompt', 'Pick one!'); select.set('selection', tom); }); append(); deepEqual(select.get('selection'), [tom], "Initial selection should be correct"); run['default'](function() { select.set('selection', yehuda); }); deepEqual(select.get('selection'), [yehuda], "After changing it, selection should be correct"); }); QUnit.test("multiple selections can be set when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('optionLabelPath', 'content.firstName'); select.set('multiple', true); select.set('selection', Ember.A([yehuda, david])); }); append(); deepEqual(select.get('selection'), [yehuda, david], "Initial selection should be correct"); run['default'](function() { select.set('selection', Ember.A([tom, brennain])); }); deepEqual( select.$(':selected').map(function() { return trim(jQuery['default'](this).text());}).toArray(), ['Tom', 'Brennain'], "After changing it, selection should be correct"); }); QUnit.test("multiple selections can be set by changing in place the selection array when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; var selection = Ember.A([yehuda, tom]); run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('optionLabelPath', 'content.firstName'); select.set('multiple', true); select.set('selection', selection); }); append(); deepEqual(select.get('selection'), [yehuda, tom], "Initial selection should be correct"); run['default'](function() { selection.replace(0, selection.get('length'), Ember.A([david, brennain])); }); deepEqual( select.$(':selected').map(function() { return trim(jQuery['default'](this).text());}).toArray(), ['David', 'Brennain'], "After updating the selection array in-place, selection should be correct"); }); QUnit.test("multiple selections can be set indirectly via bindings and in-place when multiple=true (issue #1058)", function() { var indirectContent = EmberObject['default'].create(); var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; var cyril = { id: 5, firstName: 'Cyril' }; run['default'](function() { select.destroy(); // Destroy the existing select run['default'](function() { select = EmberSelect['default'].extend({ indirectContent: indirectContent, contentBinding: 'indirectContent.controller.content', selectionBinding: 'indirectContent.controller.selection', multiple: true, optionLabelPath: 'content.firstName' }).create(); indirectContent.set('controller', EmberObject['default'].create({ content: Ember.A([tom, david, brennain]), selection: Ember.A([david]) })); }); append(); }); deepEqual(select.get('content'), [tom, david, brennain], "Initial content should be correct"); deepEqual(select.get('selection'), [david], "Initial selection should be correct"); run['default'](function() { indirectContent.set('controller.content', Ember.A([david, cyril])); indirectContent.set('controller.selection', Ember.A([cyril])); }); deepEqual(select.get('content'), [david, cyril], "After updating bound content, content should be correct"); deepEqual(select.get('selection'), [cyril], "After updating bound selection, selection should be correct"); }); QUnit.test("select with group can group options", function() { var content = Ember.A([ { firstName: 'Yehuda', organization: 'Tilde' }, { firstName: 'Tom', organization: 'Tilde' }, { firstName: 'Keith', organization: 'Envato' } ]); run['default'](function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); }); append(); equal(select.$('optgroup').length, 2); var labels = []; select.$('optgroup').each(function() { labels.push(this.label); }); equal(labels.join(''), ['TildeEnvato']); equal(trim(select.$('optgroup').first().text()), 'YehudaTom'); equal(trim(select.$('optgroup').last().text()), 'Keith'); }); QUnit.test("select with group doesn't break options", function() { var content = Ember.A([ { id: 1, firstName: 'Yehuda', organization: 'Tilde' }, { id: 2, firstName: 'Tom', organization: 'Tilde' }, { id: 3, firstName: 'Keith', organization: 'Envato' } ]); run['default'](function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); }); append(); equal(select.$('option').length, 3); equal(trim(select.$().text()), 'YehudaTomKeith'); run['default'](function() { content.set('firstObject.firstName', 'Peter'); }); equal(select.$().text(), 'PeterTomKeith\n'); select.$('option').get(0).selected = true; select.$().trigger('change'); deepEqual(select.get('selection'), content.get('firstObject')); }); QUnit.test("select with group observes its content", function() { var wycats = { firstName: 'Yehuda', organization: 'Tilde' }; var content = Ember.A([ wycats ]); run['default'](function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); }); append(); run['default'](function() { content.pushObject({ firstName: 'Keith', organization: 'Envato' }); }); equal(select.$('optgroup').length, 2); equal(select.$('optgroup[label=Envato]').length, 1); run['default'](function() { select.set('optionGroupPath', 'firstName'); }); var labels = []; select.$('optgroup').each(function() { labels.push(this.label); }); equal(labels.join(''), 'YehudaKeith'); }); QUnit.test("select with group whose content is undefined doesn't breaks", function() { var content; run['default'](function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); }); append(); equal(select.$('optgroup').length, 0); }); QUnit.test("selection uses the same array when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; var selection = Ember.A([yehuda, david]); run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('optionLabelPath', 'content.firstName'); select.set('selection', selection); }); append(); deepEqual(select.get('selection'), [yehuda, david], "Initial selection should be correct"); select.$('option').each(function() { this.selected = false; }); select.$(':contains("Tom"), :contains("David")').each(function() { this.selected = true; }); select.$().trigger('change'); deepEqual(select.get('selection'), [tom,david], "On change the selection is updated"); deepEqual(selection, [tom,david], "On change the original selection array is updated"); }); QUnit.test("Ember.SelectedOption knows when it is selected when multiple=false", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', false); select.set('selection', david); }); append(); deepEqual(selectedOptions(), [false, false, true, false], "Initial selection should be correct"); run['default'](function() { select.set('selection', brennain); }); deepEqual(selectedOptions(), [false, false, false, true], "After changing it, selection should be correct"); }); QUnit.test("Ember.SelectedOption knows when it is selected when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('selection', [yehuda, david]); }); append(); deepEqual(selectedOptions(), [true, false, true, false], "Initial selection should be correct"); run['default'](function() { select.set('selection', [tom, david]); }); deepEqual(selectedOptions(), [false, true, true, false], "After changing it, selection should be correct"); }); QUnit.test("Ember.SelectedOption knows when it is selected when multiple=true and options are primitives", function() { run['default'](function() { select.set('content', Ember.A([1, 2, 3, 4])); select.set('multiple', true); select.set('selection', [1, 3]); }); append(); deepEqual(selectedOptions(), [true, false, true, false], "Initial selection should be correct"); run['default'](function() { select.set('selection', [2, 3]); }); deepEqual(selectedOptions(), [false, true, true, false], "After changing it, selection should be correct"); }); QUnit.test("a prompt can be specified", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; run['default'](function() { select.set('content', Ember.A([yehuda, tom])); select.set('prompt', 'Pick a person'); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); }); append(); equal(select.$('option').length, 3, "There should be three options"); equal(select.$()[0].selectedIndex, 0, "By default, the prompt is selected in the DOM"); equal(trim(select.$('option:selected').text()), 'Pick a person', "By default, the prompt is selected in the DOM"); equal(select.$().val(), '', "By default, the prompt has no value"); equal(select.get('selection'), null, "When the prompt is selected, the selection should be null"); run['default'](function() { select.set('selection', tom); }); equal(select.$()[0].selectedIndex, 2, "The selectedIndex accounts for the prompt"); select.$()[0].selectedIndex = 0; select.$().trigger('change'); equal(select.get('selection'), null, "When the prompt is selected again after another option, the selection should be null"); select.$()[0].selectedIndex = 2; select.$().trigger('change'); equal(select.get('selection'), tom, "Properly accounts for the prompt when DOM change occurs"); }); QUnit.test("handles null content", function() { append(); run['default'](function() { select.set('content', null); select.set('selection', 'invalid'); select.set('value', 'also_invalid'); }); equal(select.get('element').selectedIndex, -1, "should have no selection"); run['default'](function() { select.set('multiple', true); select.set('selection', [{ content: 'invalid' }]); }); equal(select.get('element').selectedIndex, -1, "should have no selection"); }); QUnit.test("valueBinding handles 0 as initiated value (issue #2763)", function() { var indirectData = EmberObject['default'].create({ value: 0 }); run['default'](function() { select.destroy(); // Destroy the existing select select = EmberSelect['default'].extend({ content: Ember.A([1,0]), indirectData: indirectData, valueBinding: 'indirectData.value' }).create(); // append(); run['default'](function() { select.appendTo('#qunit-fixture'); }); }); equal(select.get('value'), 0, "Value property should equal 0"); }); QUnit.test("should be able to select an option and then reselect the prompt", function() { run['default'](function() { select.set('content', Ember.A(['one', 'two', 'three'])); select.set('prompt', 'Select something'); }); append(); select.$()[0].selectedIndex = 2; select.$().trigger('change'); equal(select.get('selection'), 'two'); select.$()[0].selectedIndex = 0; select.$().trigger('change'); equal(select.get('selection'), null); equal(select.$()[0].selectedIndex, 0); }); QUnit.test("should be able to get the current selection's value", function() { run['default'](function() { select.set('content', Ember.A([ { label: 'Yehuda Katz', value: 'wycats' }, { label: 'Tom Dale', value: 'tomdale' }, { label: 'Peter Wagenet', value: 'wagenet' }, { label: 'Erik Bryn', value: 'ebryn' } ])); select.set('optionLabelPath', 'content.label'); select.set('optionValuePath', 'content.value'); }); append(); equal(select.get('value'), 'wycats'); }); QUnit.test("should be able to set the current selection by value", function() { var ebryn = { label: 'Erik Bryn', value: 'ebryn' }; run['default'](function() { select.set('content', Ember.A([ { label: 'Yehuda Katz', value: 'wycats' }, { label: 'Tom Dale', value: 'tomdale' }, { label: 'Peter Wagenet', value: 'wagenet' }, ebryn ])); select.set('optionLabelPath', 'content.label'); select.set('optionValuePath', 'content.value'); select.set('value', 'ebryn'); }); append(); equal(select.get('value'), 'ebryn'); equal(select.get('selection'), ebryn); }); }); enifed('ember-views/tests/views/select_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/select_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/select_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/select_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/select_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/select_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/simple_bound_view_test', ['ember-metal/streams/stream', 'ember-views/views/simple_bound_view'], function (Stream, SimpleBoundView) { 'use strict'; QUnit.module('SimpleBoundView'); QUnit.test('does not render if update is triggered by normalizedValue is the same as the previous normalizedValue', function() { var value = null; var obj = { 'foo': 'bar' }; var lazyValue = new Stream['default'](function() { return obj.foo; }); var morph = { setContent: function(newValue) { value = newValue; } }; var view = new SimpleBoundView['default'](null, null, morph, lazyValue); equal(value, null); view.update(); equal(value, 'bar', 'expected call to morph.setContent with "bar"'); value = null; view.update(); equal(value, null, 'expected no call to morph.setContent'); obj.foo = 'baz'; // change property lazyValue.notify(); view.update(); equal(value, 'baz', 'expected call to morph.setContent with "baz"'); }); }); enifed('ember-views/tests/views/simple_bound_view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/simple_bound_view_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/simple_bound_view_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/simple_bound_view_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/simple_bound_view_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/simple_bound_view_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/text_area_test', ['ember-runtime/system/object', 'ember-metal/array', 'ember-metal/run_loop', 'ember-views/views/text_area', 'ember-metal/property_get', 'ember-metal/property_set'], function (EmberObject, array, run, TextArea, property_get, property_set) { 'use strict'; var textArea, TestObject; function set(object, key, value) { run['default'](function() { property_set.set(object, key, value); }); } function append() { run['default'](function() { textArea.appendTo('#qunit-fixture'); }); } QUnit.module("TextArea", { setup: function() { TestObject = window.TestObject = EmberObject['default'].create({ value: null }); textArea = TextArea['default'].create(); }, teardown: function() { run['default'](function() { textArea.destroy(); }); TestObject = window.TestObject = textArea = null; } }); QUnit.test("should become disabled if the disabled attribute is true", function() { textArea.set('disabled', true); append(); ok(textArea.$().is(":disabled")); }); QUnit.test("should become disabled if the disabled attribute is true", function() { append(); ok(textArea.$().is(":not(:disabled)")); run['default'](function() { textArea.set('disabled', true); }); ok(textArea.$().is(":disabled")); run['default'](function() { textArea.set('disabled', false); }); ok(textArea.$().is(":not(:disabled)")); }); QUnit.test("input value is updated when setting value property of view", function() { run['default'](function() { set(textArea, 'value', 'foo'); textArea.append(); }); equal(textArea.$().val(), "foo", "renders text field with value"); run['default'](function() { set(textArea, 'value', 'bar'); }); equal(textArea.$().val(), "bar", "updates text field after value changes"); }); QUnit.test("input placeholder is updated when setting placeholder property of view", function() { run['default'](function() { set(textArea, 'placeholder', 'foo'); textArea.append(); }); equal(textArea.$().attr('placeholder'), "foo", "renders text area with placeholder"); run['default'](function() { set(textArea, 'placeholder', 'bar'); }); equal(textArea.$().attr('placeholder'), "bar", "updates text area after placeholder changes"); }); QUnit.test("input name is updated when setting name property of view", function() { run['default'](function() { set(textArea, 'name', 'foo'); textArea.append(); }); equal(textArea.$().attr('name'), "foo", "renders text area with name"); run['default'](function() { set(textArea, 'name', 'bar'); }); equal(textArea.$().attr('name'), "bar", "updates text area after name changes"); }); QUnit.test("input maxlength is updated when setting maxlength property of view", function() { run['default'](function() { set(textArea, 'maxlength', '300'); textArea.append(); }); equal(textArea.$().attr('maxlength'), "300", "renders text area with maxlength"); run['default'](function() { set(textArea, 'maxlength', '400'); }); equal(textArea.$().attr('maxlength'), "400", "updates text area after maxlength changes"); }); QUnit.test("input rows is updated when setting rows property of view", function() { run['default'](function() { set(textArea, 'rows', '3'); textArea.append(); }); equal(textArea.$().attr('rows'), "3", "renders text area with rows"); run['default'](function() { set(textArea, 'rows', '4'); }); equal(textArea.$().attr('rows'), "4", "updates text area after rows changes"); }); QUnit.test("input cols is updated when setting cols property of view", function() { run['default'](function() { set(textArea, 'cols', '30'); textArea.append(); }); equal(textArea.$().attr('cols'), "30", "renders text area with cols"); run['default'](function() { set(textArea, 'cols', '40'); }); equal(textArea.$().attr('cols'), "40", "updates text area after cols changes"); }); QUnit.test("input tabindex is updated when setting tabindex property of view", function() { run['default'](function() { set(textArea, 'tabindex', '4'); textArea.append(); }); equal(textArea.$().attr('tabindex'), "4", "renders text area with the tabindex"); run['default'](function() { set(textArea, 'tabindex', '1'); }); equal(textArea.$().attr('tabindex'), "1", "updates text area after tabindex changes"); }); QUnit.test("input title is updated when setting title property of view", function() { run['default'](function() { set(textArea, 'title', 'FooTitle'); textArea.append(); }); equal(textArea.$().attr('title'), "FooTitle", "renders text area with the title"); run['default'](function() { set(textArea, 'title', 'BarTitle'); }); equal(textArea.$().attr('title'), 'BarTitle', "updates text area after title changes"); }); QUnit.test("value binding works properly for inputs that haven't been created", function() { run['default'](function() { textArea.destroy(); // destroy existing textarea textArea = TextArea['default'].createWithMixins({ valueBinding: 'TestObject.value' }); }); equal(property_get.get(textArea, 'value'), null, "precond - default value is null"); equal(textArea.$(), undefined, "precond - view doesn't have its layer created yet, thus no input element"); run['default'](function() { set(TestObject, 'value', 'ohai'); }); equal(property_get.get(textArea, 'value'), 'ohai', "value property was properly updated"); run['default'](function() { textArea.append(); }); equal(property_get.get(textArea, 'value'), 'ohai', "value property remains the same once the view has been appended"); equal(textArea.$().val(), 'ohai', "value is reflected in the input element once it is created"); }); array.forEach.call(['cut', 'paste', 'input'], function(eventName) { QUnit.test("should update the value on " + eventName + " events", function() { run['default'](function() { textArea.append(); }); textArea.$().val('new value'); textArea.trigger(eventName, EmberObject['default'].create({ type: eventName })); equal(textArea.get('value'), 'new value', 'value property updates on ' + eventName + ' events'); }); }); QUnit.test("should call the insertNewline method when return key is pressed", function() { var wasCalled; var event = EmberObject['default'].create({ keyCode: 13 }); run['default'](function() { textArea.append(); }); textArea.insertNewline = function() { wasCalled = true; }; textArea.trigger('keyUp', event); ok(wasCalled, "invokes insertNewline method"); }); QUnit.test("should call the cancel method when escape key is pressed", function() { var wasCalled; var event = EmberObject['default'].create({ keyCode: 27 }); run['default'](function() { textArea.append(); }); textArea.cancel = function() { wasCalled = true; }; textArea.trigger('keyUp', event); ok(wasCalled, "invokes cancel method"); }); }); enifed('ember-views/tests/views/text_area_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/text_area_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/text_area_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/text_area_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/text_area_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/text_area_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/text_field_test', ['ember-metal/run_loop', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-runtime/system/object', 'ember-views/views/text_field', 'ember-views/system/event_dispatcher', 'ember-views/system/jquery'], function (run, property_get, property_set, EmberObject, TextField, EventDispatcher, jQuery) { 'use strict'; function K() { return this; } var textField; var TestObject; var view; var appendView = function(view) { run['default'](view, 'appendTo', '#qunit-fixture'); }; var caretPosition = function(element) { var ctrl = element[0]; var caretPos = 0; // IE Support if (document.selection) { ctrl.focus(); var selection = document.selection.createRange(); selection.moveStart('character', -ctrl.value.length); caretPos = selection.text.length; } else if (ctrl.selectionStart || ctrl.selectionStart === '0') { // Firefox support caretPos = ctrl.selectionStart; } return caretPos; }; var setCaretPosition = function(element, pos) { var ctrl = element[0]; if (ctrl.setSelectionRange) { ctrl.focus(); ctrl.setSelectionRange(pos, pos); } else if (ctrl.createTextRange) { var range = ctrl.createTextRange(); range.collapse(true); range.moveEnd('character', pos); range.moveStart('character', pos); range.select(); } }; function set(object, key, value) { run['default'](function() { property_set.set(object, key, value); }); } function append() { run['default'](function() { textField.appendTo('#qunit-fixture'); }); } QUnit.module("Ember.TextField", { setup: function() { TestObject = window.TestObject = EmberObject['default'].create({ value: null }); textField = TextField['default'].create(); }, teardown: function() { run['default'](function() { textField.destroy(); }); TestObject = window.TestObject = textField = null; } }); QUnit.test("should become disabled if the disabled attribute is true before append", function() { textField.set('disabled', true); append(); ok(textField.$().is(":disabled")); }); QUnit.test("should become disabled if the disabled attribute is true", function() { append(); ok(textField.$().is(":not(:disabled)")); run['default'](function() { textField.set('disabled', true); }); ok(textField.$().is(":disabled")); run['default'](function() { textField.set('disabled', false); }); ok(textField.$().is(":not(:disabled)")); }); QUnit.test("input value is updated when setting value property of view", function() { run['default'](function() { set(textField, 'value', 'foo'); textField.append(); }); equal(textField.$().val(), "foo", "renders text field with value"); run['default'](function() { set(textField, 'value', 'bar'); }); equal(textField.$().val(), "bar", "updates text field after value changes"); }); QUnit.test("input placeholder is updated when setting placeholder property of view", function() { run['default'](function() { set(textField, 'placeholder', 'foo'); textField.append(); }); equal(textField.$().attr('placeholder'), "foo", "renders text field with placeholder"); run['default'](function() { set(textField, 'placeholder', 'bar'); }); equal(textField.$().attr('placeholder'), "bar", "updates text field after placeholder changes"); }); QUnit.test("input name is updated when setting name property of view", function() { run['default'](function() { set(textField, 'name', 'foo'); textField.append(); }); equal(textField.$().attr('name'), "foo", "renders text field with name"); run['default'](function() { set(textField, 'name', 'bar'); }); equal(textField.$().attr('name'), "bar", "updates text field after name changes"); }); QUnit.test("input maxlength is updated when setting maxlength property of view", function() { run['default'](function() { set(textField, 'maxlength', '30'); textField.append(); }); equal(textField.$().attr('maxlength'), "30", "renders text field with maxlength"); run['default'](function() { set(textField, 'maxlength', '40'); }); equal(textField.$().attr('maxlength'), "40", "updates text field after maxlength changes"); }); QUnit.test("input size is updated when setting size property of view", function() { run['default'](function() { set(textField, 'size', '30'); textField.append(); }); equal(textField.$().attr('size'), "30", "renders text field with size"); run['default'](function() { set(textField, 'size', '40'); }); equal(textField.$().attr('size'), "40", "updates text field after size changes"); }); QUnit.test("input tabindex is updated when setting tabindex property of view", function() { run['default'](function() { set(textField, 'tabindex', '5'); textField.append(); }); equal(textField.$().attr('tabindex'), "5", "renders text field with the tabindex"); run['default'](function() { set(textField, 'tabindex', '3'); }); equal(textField.$().attr('tabindex'), "3", "updates text field after tabindex changes"); }); QUnit.test("input title is updated when setting title property of view", function() { run['default'](function() { set(textField, 'title', 'FooTitle'); textField.append(); }); equal(textField.$().attr('title'), "FooTitle", "renders text field with the title"); run['default'](function() { set(textField, 'title', 'BarTitle'); }); equal(textField.$().attr('title'), "BarTitle", "updates text field after title changes"); }); QUnit.test("input type is configurable when creating view", function() { run['default'](function() { set(textField, 'type', 'password'); textField.append(); }); equal(textField.$().attr('type'), 'password', "renders text field with type"); }); QUnit.test("value binding works properly for inputs that haven't been created", function() { run['default'](function() { textField.destroy(); // destroy existing textField textField = TextField['default'].createWithMixins({ valueBinding: 'TestObject.value' }); }); equal(property_get.get(textField, 'value'), null, "precond - default value is null"); equal(textField.$(), undefined, "precond - view doesn't have its layer created yet, thus no input element"); run['default'](function() { set(TestObject, 'value', 'ohai'); }); equal(property_get.get(textField, 'value'), 'ohai', "value property was properly updated"); run['default'](function() { textField.append(); }); equal(property_get.get(textField, 'value'), 'ohai', "value property remains the same once the view has been appended"); equal(textField.$().val(), 'ohai', "value is reflected in the input element once it is created"); }); QUnit.test("value binding sets value on the element", function() { run['default'](function() { textField.destroy(); // destroy existing textField textField = TextField['default'].createWithMixins({ valueBinding: 'TestObject.value' }); textField.append(); }); // Set the value via the DOM run['default'](function() { textField.$().val('via dom'); // Trigger lets the view know we changed this value (like a real user editing) textField.trigger('input', EmberObject['default'].create({ type: 'input' })); }); equal(property_get.get(textField, 'value'), 'via dom', "value property was properly updated via dom"); equal(textField.$().val(), 'via dom', "dom property was properly updated via dom"); // Now, set it via the binding run['default'](function() { set(TestObject, 'value', 'via view'); }); equal(property_get.get(textField, 'value'), 'via view', "value property was properly updated via view"); equal(textField.$().val(), 'via view', "dom property was properly updated via view"); }); QUnit.test("should call the insertNewline method when return key is pressed", function() { var wasCalled; var event = EmberObject['default'].create({ keyCode: 13 }); run['default'](function() { textField.append(); }); textField.insertNewline = function() { wasCalled = true; }; textField.trigger('keyUp', event); ok(wasCalled, "invokes insertNewline method"); }); QUnit.test("should call the cancel method when escape key is pressed", function() { var wasCalled; var event = EmberObject['default'].create({ keyCode: 27 }); run['default'](function() { textField.append(); }); textField.cancel = function() { wasCalled = true; }; textField.trigger('keyUp', event); ok(wasCalled, "invokes cancel method"); }); QUnit.test("should send an action if one is defined when the return key is pressed", function() { expect(2); var StubController = EmberObject['default'].extend({ send: function(actionName, value, sender) { equal(actionName, 'didTriggerAction', "text field sent correct action name"); equal(value, "textFieldValue", "text field sent its current value as first argument"); } }); textField.set('action', 'didTriggerAction'); textField.set('value', "textFieldValue"); textField.set('targetObject', StubController.create()); run['default'](function() { textField.append(); }); var event = { keyCode: 13, stopPropagation: K }; textField.trigger('keyUp', event); }); QUnit.test("should send an action on keyPress if one is defined with onEvent=keyPress", function() { expect(2); var StubController = EmberObject['default'].extend({ send: function(actionName, value, sender) { equal(actionName, 'didTriggerAction', "text field sent correct action name"); equal(value, "textFieldValue", "text field sent its current value as first argument"); } }); textField.set('action', 'didTriggerAction'); textField.set('onEvent', 'keyPress'); textField.set('value', "textFieldValue"); textField.set('targetObject', StubController.create()); run['default'](function() { textField.append(); }); var event = { keyCode: 48, stopPropagation: K }; textField.trigger('keyPress', event); }); QUnit.test("bubbling of handled actions can be enabled via bubbles property", function() { textField.set('bubbles', true); textField.set('action', 'didTriggerAction'); textField.set('controller', EmberObject['default'].create({ send: K })); append(); var stopPropagationCount = 0; var event = { keyCode: 13, stopPropagation: function() { stopPropagationCount++; } }; textField.trigger('keyUp', event); equal(stopPropagationCount, 0, "propagation was not prevented if bubbles is true"); textField.set('bubbles', false); textField.trigger('keyUp', event); equal(stopPropagationCount, 1, "propagation was prevented if bubbles is false"); }); var dispatcher, StubController; QUnit.module("Ember.TextField - Action events", { setup: function() { dispatcher = EventDispatcher['default'].create(); dispatcher.setup(); StubController = EmberObject['default'].extend({ send: function(actionName, value, sender) { equal(actionName, 'doSomething', "text field sent correct action name"); } }); }, teardown: function() { run['default'](function() { dispatcher.destroy(); if (textField) { textField.destroy(); } if (view) { view.destroy(); } }); } }); QUnit.test("when the text field is blurred, the `focus-out` action is sent to the controller", function() { expect(1); textField = TextField['default'].create({ 'focus-out': 'doSomething', targetObject: StubController.create({}) }); append(); run['default'](function() { textField.$().blur(); }); }); QUnit.test("when the text field is focused, the `focus-in` action is sent to the controller", function() { expect(1); textField = TextField['default'].create({ 'focus-in': 'doSomething', targetObject: StubController.create({}) }); append(); run['default'](function() { textField.$().focusin(); }); }); QUnit.test("when the user presses a key, the `key-press` action is sent to the controller", function() { expect(1); textField = TextField['default'].create({ 'key-press': 'doSomething', targetObject: StubController.create({}) }); append(); run['default'](function() { var event = jQuery['default'].Event("keypress"); event.keyCode = event.which = 13; textField.$().trigger(event); }); }); QUnit.test("when the user inserts a new line, the `insert-newline` action is sent to the controller", function() { expect(1); textField = TextField['default'].create({ 'insert-newline': 'doSomething', targetObject: StubController.create({}) }); append(); run['default'](function() { var event = jQuery['default'].Event("keyup"); event.keyCode = event.which = 13; textField.$().trigger(event); }); }); QUnit.test("when the user presses the `enter` key, the `enter` action is sent to the controller", function() { expect(1); textField = TextField['default'].create({ 'enter': 'doSomething', targetObject: StubController.create({}) }); append(); run['default'](function() { var event = jQuery['default'].Event("keyup"); event.keyCode = event.which = 13; textField.$().trigger(event); }); }); QUnit.test("when the user hits escape, the `escape-press` action is sent to the controller", function() { expect(1); textField = TextField['default'].create({ 'escape-press': 'doSomething', targetObject: StubController.create({}) }); append(); run['default'](function() { var event = jQuery['default'].Event("keyup"); event.keyCode = event.which = 27; textField.$().trigger(event); }); }); QUnit.test("when the user presses a key, the `key-down` action is sent to the controller", function() { expect(3); var event; textField = TextField['default'].create({ 'key-down': 'doSomething', targetObject: StubController.create({ send: function(actionName, value, evt) { equal(actionName, 'doSomething', "text field sent correct action name"); equal(value, '', 'value was blank in key-down'); equal(evt, event, 'event was received as param'); } }) }); append(); run['default'](function() { event = jQuery['default'].Event("keydown"); event.keyCode = event.which = 65; textField.$().val('foo'); textField.$().trigger(event); }); }); QUnit.test("when the user releases a key, the `key-up` action is sent to the controller", function() { expect(3); var event; textField = TextField['default'].create({ 'key-up': 'doSomething', targetObject: StubController.create({ send: function(actionName, value, evt) { equal(actionName, 'doSomething', "text field sent correct action name"); equal(value, 'bar', 'value was received'); equal(evt, event, 'event was received as param'); } }) }); append(); run['default'](function() { event = jQuery['default'].Event("keyup"); event.keyCode = event.which = 65; textField.$().val('bar'); textField.$().trigger(event); }); }); QUnit.test('should not reset cursor position when text field receives keyUp event', function() { view = TextField['default'].create({ value: 'Broseidon, King of the Brocean' }); appendView(view); setCaretPosition(view.$(), 5); run['default'](function() { view.trigger('keyUp', {}); }); equal(caretPosition(view.$()), 5, 'The keyUp event should not result in the cursor being reset due to the bind-attr observers'); }); }); enifed('ember-views/tests/views/text_field_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views'); test('ember-views/tests/views/text_field_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/text_field_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/text_field_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views'); test('ember-views/tests/views/text_field_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/text_field_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/actions_test', ['ember-metal/run_loop', 'ember-metal/mixin', 'ember-runtime/controllers/controller', 'ember-runtime/system/object', 'ember-views/views/view'], function (run, mixin, Controller, EmberObject, View) { 'use strict'; var view; QUnit.module("View action handling", { teardown: function() { run['default'](function() { if (view) { view.destroy(); } }); } }); QUnit.test("Action can be handled by a function on actions object", function() { expect(1); view = View['default'].extend({ actions: { poke: function() { ok(true, 'poked'); } } }).create(); view.send("poke"); }); QUnit.test("A handled action can be bubbled to the target for continued processing", function() { expect(2); view = View['default'].extend({ actions: { poke: function() { ok(true, 'poked 1'); return true; } }, target: Controller['default'].extend({ actions: { poke: function() { ok(true, 'poked 2'); } } }).create() }).create(); view.send("poke"); }); QUnit.test("Action can be handled by a superclass' actions object", function() { expect(4); var SuperView = View['default'].extend({ actions: { foo: function() { ok(true, 'foo'); }, bar: function(msg) { equal(msg, "HELLO"); } } }); var BarViewMixin = mixin.Mixin.create({ actions: { bar: function(msg) { equal(msg, "HELLO"); this._super(msg); } } }); var IndexView = SuperView.extend(BarViewMixin, { actions: { baz: function() { ok(true, 'baz'); } } }); view = IndexView.create(); view.send("foo"); view.send("bar", "HELLO"); view.send("baz"); }); QUnit.test("Actions cannot be provided at create time", function() { expectAssertion(function() { view = View['default'].create({ actions: { foo: function() { ok(true, 'foo'); } } }); }); // but should be OK on an object that doesn't mix in Ember.ActionHandler EmberObject['default'].create({ actions: ['foo'] }); }); }); enifed('ember-views/tests/views/view/actions_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/actions_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/actions_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/actions_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/actions_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/actions_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/append_to_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, run, jQuery, EmberView, ContainerView) { 'use strict'; var View, view, willDestroyCalled, childView; QUnit.module("EmberView - append() and appendTo()", { setup: function() { View = EmberView['default'].extend({}); }, teardown: function() { run['default'](function() { if (!view.isDestroyed) { view.destroy(); } }); } }); QUnit.test("should be added to the specified element when calling appendTo()", function() { jQuery['default']("#qunit-fixture").html(''); view = View.create(); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.appendTo('#menu'); }); var viewElem = jQuery['default']('#menu').children(); ok(viewElem.length > 0, "creates and appends the view's element"); }); QUnit.test("should be added to the document body when calling append()", function() { view = View.create({ render: function(buffer) { buffer.push("foo bar baz"); } }); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); var viewElem = jQuery['default'](document.body).find(':contains("foo bar baz")'); ok(viewElem.length > 0, "creates and appends the view's element"); }); QUnit.test("raises an assert when a target does not exist in the DOM", function() { view = View.create(); expectAssertion(function() { run['default'](function() { view.appendTo('does-not-exist-in-dom'); }); }); }); QUnit.test("append calls willInsertElement and didInsertElement callbacks", function() { var willInsertElementCalled = false; var willInsertElementCalledInChild = false; var didInsertElementCalled = false; var ViewWithCallback = View.extend({ willInsertElement: function() { willInsertElementCalled = true; }, didInsertElement: function() { didInsertElementCalled = true; }, render: function(buffer) { this.appendChild(EmberView['default'].create({ willInsertElement: function() { willInsertElementCalledInChild = true; } })); } }); view = ViewWithCallback.create(); run['default'](function() { view.append(); }); ok(willInsertElementCalled, "willInsertElement called"); ok(willInsertElementCalledInChild, "willInsertElement called in child"); ok(didInsertElementCalled, "didInsertElement called"); }); QUnit.test("remove removes an element from the DOM", function() { willDestroyCalled = 0; view = View.create({ willDestroyElement: function() { willDestroyCalled++; } }); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); ok(jQuery['default']("#" + property_get.get(view, 'elementId')).length === 1, "precond - element was inserted"); run['default'](function() { view.remove(); }); ok(jQuery['default']("#" + property_get.get(view, 'elementId')).length === 0, "remove removes an element from the DOM"); ok(EmberView['default'].views[property_get.get(view, 'elementId')] === undefined, "remove does not remove the view from the view hash"); ok(!property_get.get(view, 'element'), "remove nulls out the element"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once"); }); QUnit.test("destroy more forcibly removes the view", function() { willDestroyCalled = 0; view = View.create({ willDestroyElement: function() { willDestroyCalled++; } }); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); ok(jQuery['default']("#" + property_get.get(view, 'elementId')).length === 1, "precond - element was inserted"); run['default'](function() { view.destroy(); }); ok(jQuery['default']("#" + property_get.get(view, 'elementId')).length === 0, "destroy removes an element from the DOM"); ok(EmberView['default'].views[property_get.get(view, 'elementId')] === undefined, "destroy removes a view from the global views hash"); equal(property_get.get(view, 'isDestroyed'), true, "the view is marked as destroyed"); ok(!property_get.get(view, 'element'), "the view no longer has an element"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once"); }); QUnit.module("EmberView - append() and appendTo() in a view hierarchy", { setup: function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); View = ContainerView['default'].extend({ childViews: ['child'], child: EmberView['default'].extend({ elementId: 'child' }) }); }, teardown: function() { run['default'](function() { if (!view.isDestroyed) { view.destroy(); } }); } }); QUnit.test("should be added to the specified element when calling appendTo()", function() { jQuery['default']("#qunit-fixture").html(''); view = View.create(); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.appendTo('#menu'); }); var viewElem = jQuery['default']('#menu #child'); ok(viewElem.length > 0, "creates and appends the view's element"); }); QUnit.test("should be added to the document body when calling append()", function() { jQuery['default']("#qunit-fixture").html(''); view = View.create(); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); var viewElem = jQuery['default']('#child'); ok(viewElem.length > 0, "creates and appends the view's element"); }); QUnit.module("EmberView - removing views in a view hierarchy", { setup: function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); willDestroyCalled = 0; view = ContainerView['default'].create({ childViews: ['child'], child: EmberView['default'].create({ willDestroyElement: function() { willDestroyCalled++; } }) }); childView = property_get.get(view, 'child'); }, teardown: function() { run['default'](function() { if (!view.isDestroyed) { view.destroy(); } }); } }); QUnit.test("remove removes child elements from the DOM", function() { ok(!property_get.get(childView, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); ok(jQuery['default']("#" + property_get.get(childView, 'elementId')).length === 1, "precond - element was inserted"); // remove parent view run['default'](function() { view.remove(); }); ok(jQuery['default']("#" + property_get.get(childView, 'elementId')).length === 0, "remove removes child elements the DOM"); ok(EmberView['default'].views[property_get.get(childView, 'elementId')] === undefined, "remove does not remove child views from the view hash"); ok(!property_get.get(childView, 'element'), "remove nulls out child elements"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once"); }); QUnit.test("destroy more forcibly removes child views", function() { ok(!property_get.get(childView, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); ok(jQuery['default']("#" + property_get.get(childView, 'elementId')).length === 1, "precond - child element was inserted"); willDestroyCalled = 0; run['default'](function() { view.destroy(); }); ok(jQuery['default']("#" + property_get.get(childView, 'elementId')).length === 0, "destroy removes child elements from the DOM"); ok(EmberView['default'].views[property_get.get(childView, 'elementId')] === undefined, "destroy removes a child views from the global views hash"); equal(property_get.get(childView, 'isDestroyed'), true, "child views are marked as destroyed"); ok(!property_get.get(childView, 'element'), "child views no longer have an element"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once on children"); }); QUnit.test("destroy removes a child view from its parent", function() { ok(!property_get.get(childView, 'element'), "precond - should not have an element"); run['default'](function() { view.append(); }); ok(jQuery['default']("#" + property_get.get(childView, 'elementId')).length === 1, "precond - child element was inserted"); run['default'](function() { childView.destroy(); }); ok(property_get.get(view, 'childViews.length') === 0, "Destroyed child views should be removed from their parent"); }); }); enifed('ember-views/tests/views/view/append_to_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/append_to_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/append_to_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/append_to_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/append_to_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/append_to_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/attribute_bindings_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-metal/observer', 'ember-metal/property_events', 'ember-htmlbars/utils/string', 'ember-views/views/view'], function (Ember, run, observer, property_events, string, EmberView) { 'use strict'; var originalLookup = Ember['default'].lookup; var lookup, view; var appendView = function() { run['default'](function() { view.appendTo('#qunit-fixture'); }); }; QUnit.module("EmberView - Attribute Bindings", { setup: function() { Ember['default'].lookup = lookup = {}; }, teardown: function() { if (view) { run['default'](function() { view.destroy(); }); view = null; } Ember['default'].lookup = originalLookup; } }); QUnit.test("should render attribute bindings", function() { view = EmberView['default'].create({ attributeBindings: ['type', 'destroyed', 'exists', 'nothing', 'notDefined', 'notNumber', 'explosions'], type: 'submit', exists: true, nothing: null, notDefined: undefined }); run['default'](function() { view.createElement(); }); equal(view.$().attr('type'), 'submit', "updates type attribute"); ok(view.$().attr('exists'), "adds exists attribute when true"); ok(!view.$().attr('nothing'), "removes nothing attribute when null"); equal(view.$().attr('notDefined'), undefined, "removes notDefined attribute when undefined"); }); QUnit.test("should normalize case for attribute bindings", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['disAbled'], disAbled: true }); run['default'](function() { view.createElement(); }); ok(view.$().prop('disabled'), "sets property with correct case"); }); QUnit.test("should render attribute bindings on input", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['type', 'isDisabled:disabled'], type: 'submit', isDisabled: true }); run['default'](function() { view.createElement(); }); equal(view.$().attr('type'), 'submit', "updates type attribute"); ok(view.$().prop('disabled'), "supports customizing attribute name for Boolean values"); }); QUnit.test("should update attribute bindings", function() { view = EmberView['default'].create({ attributeBindings: ['type', 'color:data-color', 'exploded', 'collapsed', 'times'], type: 'reset', color: 'red', exploded: 'bang', collapsed: null, times: 15 }); run['default'](function() { view.createElement(); }); equal(view.$().attr('type'), 'reset', "adds type attribute"); equal(view.$().attr('data-color'), 'red', "attr value set with ternary"); equal(view.$().attr('exploded'), 'bang', "adds exploded attribute when it has a value"); ok(!view.$().attr('collapsed'), "does not add null attribute"); equal(view.$().attr('times'), '15', 'sets an integer to an attribute'); run['default'](function() { view.set('type', 'submit'); view.set('color', 'blue'); view.set('exploded', null); view.set('collapsed', 'swish'); view.set('times', 16); }); equal(view.$().attr('type'), 'submit', "adds type attribute"); equal(view.$().attr('data-color'), 'blue', "attr value set with ternary"); ok(!view.$().attr('exploded'), "removed exploded attribute when it is null"); ok(view.$().attr('collapsed'), "swish", "adds an attribute when it has a value"); equal(view.$().attr('times'), '16', 'updates an integer attribute'); }); QUnit.test("should update attribute bindings on input (boolean)", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['disabled'], disabled: true }); run['default'](function() { view.createElement(); }); ok(view.$().prop('disabled'), "adds disabled property when true"); run['default'](function() { view.set('disabled', false); }); ok(!view.$().prop('disabled'), "updates disabled property when false"); }); QUnit.test("should update attribute bindings on input (raw number prop)", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['size'], size: 20 }); run['default'](function() { view.createElement(); }); equal(view.$().prop('size'), 20, "adds size property"); run['default'](function() { view.set('size', 10); }); equal(view.$().prop('size'), 10, "updates size property"); }); QUnit.test("should update attribute bindings on input (name)", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['name'], name: 'bloody-awful' }); run['default'](function() { view.createElement(); }); equal(view.$().prop('name'), 'bloody-awful', "adds name property"); run['default'](function() { view.set('name', 'simply-grand'); }); equal(view.$().prop('name'), 'simply-grand', "updates name property"); }); QUnit.test("should update attribute bindings with micro syntax", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['isDisabled:disabled'], type: 'reset', isDisabled: true }); run['default'](function() { view.createElement(); }); ok(view.$().prop('disabled'), "adds disabled property when true"); run['default'](function() { view.set('isDisabled', false); }); ok(!view.$().prop('disabled'), "updates disabled property when false"); }); QUnit.test("should allow namespaced attributes in micro syntax", function () { view = EmberView['default'].create({ attributeBindings: ['xlinkHref:xlink:href'], xlinkHref: '/foo.png' }); run['default'](function() { view.createElement(); }); equal(view.$().attr('xlink:href'), '/foo.png', "namespaced attribute is set"); run['default'](function () { view.set('xlinkHref', '/bar.png'); }); equal(view.$().attr('xlink:href'), '/bar.png', "namespaced attribute is updated"); }); QUnit.test("should update attribute bindings on svg", function() { view = EmberView['default'].create({ attributeBindings: ['viewBox'], viewBox: null }); run['default'](function() { view.createElement(); }); equal(view.$().attr('viewBox'), null, "viewBox can be null"); run['default'](function() { view.set('viewBox', '0 0 100 100'); }); equal(view.$().attr('viewBox'), '0 0 100 100', "viewBox can be updated"); }); // This comes into play when using the {{#each}} helper. If the // passed array item is a String, it will be converted into a // String object instead of a normal string. QUnit.test("should allow binding to String objects", function() { view = EmberView['default'].create({ attributeBindings: ['foo'], // JSHint doesn't like `new String` so we'll create it the same way it gets created in practice foo: (function() { return this; }).call("bar") }); run['default'](function() { view.createElement(); }); equal(view.$().attr('foo'), 'bar', "should convert String object to bare string"); run['default'](function() { view.set('foo', null); }); ok(!view.$().attr('foo'), "removes foo attribute when null"); }); QUnit.test("should teardown observers on rerender", function() { view = EmberView['default'].create({ attributeBindings: ['foo'], classNameBindings: ['foo'], foo: 'bar' }); appendView(); equal(observer.observersFor(view, 'foo').length, 2, 'observer count after render is two'); run['default'](function() { view.rerender(); }); equal(observer.observersFor(view, 'foo').length, 2, 'observer count after rerender remains two'); }); QUnit.test("handles attribute bindings for properties", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['checked'], checked: null }); appendView(); equal(!!view.$().prop('checked'), false, 'precond - is not checked'); run['default'](function() { view.set('checked', true); }); equal(view.$().prop('checked'), true, 'changes to checked'); run['default'](function() { view.set('checked', false); }); equal(!!view.$().prop('checked'), false, 'changes to unchecked'); }); QUnit.test("handles `undefined` value for properties", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['value'], value: "test" }); appendView(); equal(view.$().prop('value'), "test", "value is defined"); run['default'](function() { view.set('value', undefined); }); equal(view.$().prop('value'), '', "value is blank"); }); QUnit.test("handles null value for attributes on text fields", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['value'] }); appendView(); view.$().attr('value', 'test'); equal(view.$().attr('value'), "test", "value is defined"); run['default'](function() { view.set('value', null); }); equal(!!view.$().prop('value'), false, "value is not defined"); }); QUnit.test("handles a 0 value attribute on text fields", function() { view = EmberView['default'].create({ tagName: 'input', attributeBindings: ['value'] }); appendView(); view.$().attr('value', 'test'); equal(view.$().attr('value'), "test", "value is defined"); run['default'](function() { view.set('value', 0); }); strictEqual(view.$().prop('value'), "0", "value should be 0"); }); QUnit.test("attributeBindings should not fail if view has been removed", function() { run['default'](function() { view = EmberView['default'].create({ attributeBindings: ['checked'], checked: true }); }); run['default'](function() { view.createElement(); }); var error; try { run['default'](function() { property_events.changeProperties(function() { view.set('checked', false); view.remove(); }); }); } catch(e) { error = e; } ok(!error, error); }); QUnit.test("attributeBindings should not fail if view has been destroyed", function() { run['default'](function() { view = EmberView['default'].create({ attributeBindings: ['checked'], checked: true }); }); run['default'](function() { view.createElement(); }); var error; try { run['default'](function() { property_events.changeProperties(function() { view.set('checked', false); view.destroy(); }); }); } catch(e) { error = e; } ok(!error, error); }); QUnit.test("asserts if an attributeBinding is setup on class", function() { view = EmberView['default'].create({ attributeBindings: ['class'] }); expectAssertion(function() { appendView(); }, 'You cannot use class as an attributeBinding, use classNameBindings instead.'); }); QUnit.test("blacklists href bindings based on protocol", function() { /* jshint scripturl:true */ view = EmberView['default'].create({ tagName: 'a', attributeBindings: ['href'], href: "javascript:alert('foo')" }); appendView(); equal(view.$().attr('href'), "unsafe:javascript:alert('foo')", "value property sanitized"); run['default'](function() { view.set('href', new string.SafeString(view.get('href'))); }); equal(view.$().attr('href'), "javascript:alert('foo')", "value is not defined"); }); QUnit.test("attributeBindings should be overridable", function() { var ParentView = EmberView['default'].extend({ attributeBindings: ['href'], href: "an href" }); var ChildView = ParentView.extend({ attributeBindings: ['newHref:href'], newHref: "a new href" }); view = ChildView.create(); appendView(); equal(view.$().attr('href'), "a new href", "expect value from subclass attribute binding"); }); }); enifed('ember-views/tests/views/view/attribute_bindings_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/attribute_bindings_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/attribute_bindings_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/attribute_bindings_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/attribute_bindings_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/attribute_bindings_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/child_views_test', ['ember-metal/run_loop', 'ember-views/views/view'], function (run, EmberView) { 'use strict'; var parentView, childView; QUnit.module('tests/views/view/child_views_tests.js', { setup: function() { parentView = EmberView['default'].create({ render: function(buffer) { buffer.push('Em'); this.appendChild(childView); } }); childView = EmberView['default'].create({ template: function() { return 'ber'; } }); }, teardown: function() { run['default'](function() { parentView.destroy(); childView.destroy(); }); } }); // no parent element, buffer, no element // parent element // no parent element, no buffer, no element QUnit.test("should render an inserted child view when the child is inserted before a DOM element is created", function() { run['default'](function() { parentView.append(); }); equal(parentView.$().text(), 'Ember', 'renders the child view after the parent view'); }); QUnit.test("should not duplicate childViews when rerendering", function() { var Inner = EmberView['default'].extend({ template: function() { return ''; } }); var Inner2 = EmberView['default'].extend({ template: function() { return ''; } }); var Middle = EmberView['default'].extend({ render: function(buffer) { this.appendChild(Inner); this.appendChild(Inner2); } }); var outer = EmberView['default'].create({ render: function(buffer) { this.middle = this.appendChild(Middle); } }); run['default'](function() { outer.append(); }); equal(outer.get('middle.childViews.length'), 2, 'precond middle has 2 child views rendered to buffer'); run['default'](function() { outer.middle.rerender(); }); equal(outer.get('middle.childViews.length'), 2, 'middle has 2 child views rendered to buffer'); run['default'](function() { outer.destroy(); }); }); }); enifed('ember-views/tests/views/view/child_views_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/child_views_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/child_views_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/child_views_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/child_views_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/child_views_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/class_name_bindings_test', ['ember-metal/property_set', 'ember-metal/run_loop', 'ember-metal/property_events', 'ember-metal/watching', 'ember-runtime/system/object', 'ember-views/views/view'], function (property_set, run, property_events, watching, EmberObject, EmberView) { 'use strict'; var view; QUnit.module("EmberView - Class Name Bindings", { teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("should apply bound class names to the element", function() { view = EmberView['default'].create({ classNameBindings: ['priority', 'isUrgent', 'isClassified:classified', 'canIgnore', 'messages.count', 'messages.resent:is-resent', 'isNumber:is-number', 'isFalsy::is-falsy', 'isTruthy::is-not-truthy', 'isEnabled:enabled:disabled'], priority: 'high', isUrgent: true, isClassified: true, canIgnore: false, isNumber: 5, isFalsy: 0, isTruthy: 'abc', isEnabled: true, messages: { count: 'five-messages', resent: true } }); run['default'](function() { view.createElement(); }); ok(view.$().hasClass('high'), "adds string values as class name"); ok(view.$().hasClass('is-urgent'), "adds true Boolean values by dasherizing"); ok(view.$().hasClass('classified'), "supports customizing class name for Boolean values"); ok(view.$().hasClass('five-messages'), "supports paths in bindings"); ok(view.$().hasClass('is-resent'), "supports customing class name for paths"); ok(view.$().hasClass('is-number'), "supports colon syntax with truthy properties"); ok(view.$().hasClass('is-falsy'), "supports colon syntax with falsy properties"); ok(!view.$().hasClass('abc'), "does not add values as classes when falsy classes have been specified"); ok(!view.$().hasClass('is-not-truthy'), "does not add falsy classes when values are truthy"); ok(!view.$().hasClass('can-ignore'), "does not add false Boolean values as class"); ok(view.$().hasClass('enabled'), "supports customizing class name for Boolean values with negation"); ok(!view.$().hasClass('disabled'), "does not add class name for negated binding"); }); QUnit.test("should add, remove, or change class names if changed after element is created", function() { view = EmberView['default'].create({ classNameBindings: ['priority', 'isUrgent', 'isClassified:classified', 'canIgnore', 'messages.count', 'messages.resent:is-resent', 'isEnabled:enabled:disabled'], priority: 'high', isUrgent: true, isClassified: true, canIgnore: false, isEnabled: true, messages: EmberObject['default'].create({ count: 'five-messages', resent: false }) }); run['default'](function() { view.createElement(); property_set.set(view, 'priority', 'orange'); property_set.set(view, 'isUrgent', false); property_set.set(view, 'canIgnore', true); property_set.set(view, 'isEnabled', false); property_set.set(view, 'messages.count', 'six-messages'); property_set.set(view, 'messages.resent', true); }); ok(view.$().hasClass('orange'), "updates string values"); ok(!view.$().hasClass('high'), "removes old string value"); ok(!view.$().hasClass('is-urgent', "removes dasherized class when changed from true to false")); ok(view.$().hasClass('can-ignore'), "adds dasherized class when changed from false to true"); ok(view.$().hasClass('six-messages'), "adds new value when path changes"); ok(!view.$().hasClass('five-messages'), "removes old value when path changes"); ok(view.$().hasClass('is-resent'), "adds customized class name when path changes"); ok(!view.$().hasClass('enabled'), "updates class name for negated binding"); ok(view.$().hasClass('disabled'), "adds negated class name for negated binding"); }); QUnit.test(":: class name syntax works with an empty true class", function() { view = EmberView['default'].create({ isEnabled: false, classNameBindings: ['isEnabled::not-enabled'] }); run['default'](function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view not-enabled', "false class is rendered when property is false"); run['default'](function() { view.set('isEnabled', true); }); equal(view.$().attr('class'), 'ember-view', "no class is added when property is true and the class is empty"); }); QUnit.test("classNames should not be duplicated on rerender", function() { run['default'](function() { view = EmberView['default'].create({ classNameBindings: ['priority'], priority: 'high' }); }); run['default'](function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view high'); run['default'](function() { view.rerender(); }); equal(view.$().attr('class'), 'ember-view high'); }); QUnit.test("classNameBindings should work when the binding property is updated and the view has been removed of the DOM", function() { run['default'](function() { view = EmberView['default'].create({ classNameBindings: ['priority'], priority: 'high' }); }); run['default'](function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view high'); run['default'](function() { view.remove(); }); view.set('priority', 'low'); run['default'](function() { view.append(); }); equal(view.$().attr('class'), 'ember-view low'); }); QUnit.test("classNames removed by a classNameBindings observer should not re-appear on rerender", function() { view = EmberView['default'].create({ classNameBindings: ['isUrgent'], isUrgent: true }); run['default'](function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view is-urgent'); run['default'](function() { view.set('isUrgent', false); }); equal(view.$().attr('class'), 'ember-view'); run['default'](function() { view.rerender(); }); equal(view.$().attr('class'), 'ember-view'); }); QUnit.test("classNameBindings lifecycle test", function() { run['default'](function() { view = EmberView['default'].create({ classNameBindings: ['priority'], priority: 'high' }); }); equal(watching.isWatching(view, 'priority'), false); run['default'](function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view high'); equal(watching.isWatching(view, 'priority'), true); run['default'](function() { view.remove(); view.set('priority', 'low'); }); equal(watching.isWatching(view, 'priority'), false); }); QUnit.test("classNameBindings should not fail if view has been removed", function() { run['default'](function() { view = EmberView['default'].create({ classNameBindings: ['priority'], priority: 'high' }); }); run['default'](function() { view.createElement(); }); var error; try { run['default'](function() { property_events.changeProperties(function() { view.set('priority', 'low'); view.remove(); }); }); } catch(e) { error = e; } ok(!error, error); }); QUnit.test("classNameBindings should not fail if view has been destroyed", function() { run['default'](function() { view = EmberView['default'].create({ classNameBindings: ['priority'], priority: 'high' }); }); run['default'](function() { view.createElement(); }); var error; try { run['default'](function() { property_events.changeProperties(function() { view.set('priority', 'low'); view.destroy(); }); }); } catch(e) { error = e; } ok(!error, error); }); QUnit.test("Providing a binding with a space in it asserts", function() { view = EmberView['default'].create({ classNameBindings: 'i:think:i am:so:clever' }); expectAssertion(function() { view.createElement(); }, /classNameBindings must not have spaces in them/i); }); }); enifed('ember-views/tests/views/view/class_name_bindings_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/class_name_bindings_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/class_name_bindings_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/class_name_bindings_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/class_name_bindings_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/class_name_bindings_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/context_test', ['ember-metal/run_loop', 'ember-views/views/view', 'ember-views/views/container_view'], function (run, EmberView, ContainerView) { 'use strict'; QUnit.module("EmberView - context property"); QUnit.test("setting a controller on an inner view should change it context", function() { var App = {}; var a = { name: 'a' }; var b = { name: 'b' }; var innerView = EmberView['default'].create(); var middleView = ContainerView['default'].create(); var outerView = App.outerView = ContainerView['default'].create({ controller: a }); run['default'](function() { outerView.appendTo('#qunit-fixture'); }); run['default'](function () { outerView.set('currentView', middleView); }); run['default'](function () { innerView.set('controller', b); middleView.set('currentView', innerView); }); // assert equal(outerView.get('context'), a, 'outer context correct'); equal(middleView.get('context'), a, 'middle context correct'); equal(innerView.get('context'), b, 'inner context correct'); run['default'](function() { innerView.destroy(); middleView.destroy(); outerView.destroy(); }); }); }); enifed('ember-views/tests/views/view/context_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/context_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/context_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/context_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/context_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/context_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/controller_test', ['ember-metal/run_loop', 'ember-views/views/container_view'], function (run, ContainerView) { 'use strict'; QUnit.module("Ember.View - controller property"); QUnit.test("controller property should be inherited from nearest ancestor with controller", function() { var grandparent = ContainerView['default'].create(); var parent = ContainerView['default'].create(); var child = ContainerView['default'].create(); var grandchild = ContainerView['default'].create(); var grandparentController = {}; var parentController = {}; run['default'](function() { grandparent.set('controller', grandparentController); parent.set('controller', parentController); grandparent.pushObject(parent); parent.pushObject(child); }); strictEqual(grandparent.get('controller'), grandparentController); strictEqual(parent.get('controller'), parentController); strictEqual(child.get('controller'), parentController); strictEqual(grandchild.get('controller'), null); run['default'](function() { child.pushObject(grandchild); }); strictEqual(grandchild.get('controller'), parentController); var newController = {}; run['default'](function() { parent.set('controller', newController); }); strictEqual(parent.get('controller'), newController); strictEqual(child.get('controller'), newController); strictEqual(grandchild.get('controller'), newController); run['default'](function() { grandparent.destroy(); parent.destroy(); child.destroy(); grandchild.destroy(); }); }); }); enifed('ember-views/tests/views/view/controller_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/controller_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/controller_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/controller_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/controller_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/controller_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/create_child_view_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/views/view'], function (property_get, run, EmberView) { 'use strict'; var view, myViewClass, newView, container; QUnit.module("EmberView#createChildView", { setup: function() { container = { }; view = EmberView['default'].create({ container: container }); myViewClass = EmberView['default'].extend({ isMyView: true, foo: 'bar' }); }, teardown: function() { run['default'](function() { view.destroy(); if (newView) { newView.destroy(); } }); } }); QUnit.test("should create view from class with any passed attributes", function() { var attrs = { foo: "baz" }; newView = view.createChildView(myViewClass, attrs); equal(newView.container, container, 'expects to share container with parent'); ok(property_get.get(newView, 'isMyView'), 'newView is instance of myView'); equal(property_get.get(newView, 'foo'), 'baz', 'view did get custom attributes'); ok(!attrs.parentView, "the original attributes hash was not mutated"); }); QUnit.test("should set newView.parentView to receiver", function() { newView = view.createChildView(myViewClass); equal(newView.container, container, 'expects to share container with parent'); equal(property_get.get(newView, 'parentView'), view, 'newView.parentView == view'); }); QUnit.test("should create property on parentView to a childView instance if provided a viewName", function() { var attrs = { viewName: "someChildView" }; newView = view.createChildView(myViewClass, attrs); equal(newView.container, container, 'expects to share container with parent'); equal(property_get.get(view, 'someChildView'), newView); }); QUnit.test("should update a view instances attributes, including the _parentView and container properties", function() { var attrs = { foo: "baz" }; var myView = myViewClass.create(); newView = view.createChildView(myView, attrs); equal(newView.container, container, 'expects to share container with parent'); equal(newView._parentView, view, 'expects to have the correct parent'); equal(property_get.get(newView, 'foo'), 'baz', 'view did get custom attributes'); deepEqual(newView, myView); }); QUnit.test("should create from string via container lookup", function() { var ChildViewClass = EmberView['default'].extend(); var fullName = 'view:bro'; view.container.lookupFactory = function(viewName) { equal(fullName, viewName); return ChildViewClass.extend({ container: container }); }; newView = view.createChildView('bro'); equal(newView.container, container, 'expects to share container with parent'); equal(newView._parentView, view, 'expects to have the correct parent'); }); QUnit.test("should assert when trying to create childView from string, but no such view is registered", function() { view.container.lookupFactory = function() {}; expectAssertion(function() { view.createChildView('bro'); }); }); }); enifed('ember-views/tests/views/view/create_child_view_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/create_child_view_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/create_child_view_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/create_child_view_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/create_child_view_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/create_child_view_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/create_element_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/views/container_view', 'ember-metal-views/tests/test_helpers'], function (property_get, run, EmberView, ContainerView, test_helpers) { 'use strict'; var view; QUnit.module("Ember.View#createElement", { teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("returns the receiver", function() { var ret; view = EmberView['default'].create(); run['default'](function() { ret = view.createElement(); }); equal(ret, view, 'returns receiver'); }); QUnit.test('should assert if `tagName` is an empty string and `classNameBindings` are specified', function() { expect(1); view = EmberView['default'].create({ tagName: '', foo: true, classNameBindings: ['foo:is-foo:is-bar'] }); expectAssertion(function() { run['default'](function() { view.createElement(); }); }, /You cannot use `classNameBindings` on a tag-less view/); }); QUnit.test("calls render and turns resultant string into element", function() { view = EmberView['default'].create({ tagName: 'span', render: function(buffer) { buffer.push("foo"); } }); equal(property_get.get(view, 'element'), null, 'precondition - has no element'); run['default'](function() { view.createElement(); }); var elem = property_get.get(view, 'element'); ok(elem, 'has element now'); equal(elem.innerHTML, 'foo', 'has innerHTML from context'); equal(elem.tagName.toString().toLowerCase(), 'span', 'has tagName from view'); }); QUnit.test("calls render and parses the buffer string in the right context", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].create({ tagName: 'table', childViews: [EmberView['default'].create({ tagName: '', render: function(buffer) { // Emulate a metamorph buffer.push("snorfblax"); } })] }); equal(property_get.get(view, 'element'), null, 'precondition - has no element'); run['default'](function() { view.createElement(); }); var elem = property_get.get(view, 'element'); ok(elem, 'has element now'); equal(elem.tagName.toString().toLowerCase(), 'table', 'has tagName from view'); equal(elem.childNodes[0].tagName, 'SCRIPT', 'script tag first'); equal(elem.childNodes[1].tagName, 'TR', 'tr tag second'); test_helpers.equalHTML(elem.childNodes, 'snorfblax', 'has innerHTML from context'); }); QUnit.test("does not wrap many tr children in tbody elements", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].create({ tagName: 'table', childViews: [ EmberView['default'].create({ tagName: '', render: function(buffer) { // Emulate a metamorph buffer.push("snorfblax"); } }), EmberView['default'].create({ tagName: '', render: function(buffer) { // Emulate a metamorph buffer.push("snorfblax"); } }) ] }); equal(property_get.get(view, 'element'), null, 'precondition - has no element'); run['default'](function() { view.createElement(); }); var elem = property_get.get(view, 'element'); ok(elem, 'has element now'); test_helpers.equalHTML(elem.childNodes, 'snorfblaxsnorfblax', 'has innerHTML from context'); equal(elem.tagName.toString().toLowerCase(), 'table', 'has tagName from view'); }); QUnit.test("generated element include HTML from child views as well", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].create({ childViews: [EmberView['default'].create({ elementId: "foo" })] }); run['default'](function() { view.createElement(); }); ok(view.$('#foo').length, 'has element with child elementId'); }); }); enifed('ember-views/tests/views/view/create_element_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/create_element_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/create_element_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/create_element_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/create_element_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/create_element_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/destroy_element_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, run, EmberView, ContainerView) { 'use strict'; var view; QUnit.module("EmberView#destroyElement", { teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("if it has no element, does nothing", function() { var callCount = 0; view = EmberView['default'].create({ willDestroyElement: function() { callCount++; } }); ok(!property_get.get(view, 'element'), 'precond - does NOT have element'); run['default'](function() { view.destroyElement(); }); equal(callCount, 0, 'did not invoke callback'); }); QUnit.test("if it has a element, calls willDestroyElement on receiver and child views then deletes the element", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); var parentCount = 0; var childCount = 0; view = ContainerView['default'].create({ willDestroyElement: function() { parentCount++; }, childViews: [ContainerView['default'].extend({ // no willDestroyElement here... make sure no errors are thrown childViews: [EmberView['default'].extend({ willDestroyElement: function() { childCount++; } })] })] }); run['default'](function() { view.createElement(); }); ok(property_get.get(view, 'element'), 'precond - view has element'); run['default'](function() { view.destroyElement(); }); equal(parentCount, 1, 'invoked destroy element on the parent'); equal(childCount, 1, 'invoked destroy element on the child'); ok(!property_get.get(view, 'element'), 'view no longer has element'); ok(!property_get.get(property_get.get(view, 'childViews').objectAt(0), 'element'), 'child no longer has an element'); }); QUnit.test("returns receiver", function() { var ret; view = EmberView['default'].create(); run['default'](function() { view.createElement(); ret = view.destroyElement(); }); equal(ret, view, 'returns receiver'); }); QUnit.test("removes element from parentNode if in DOM", function() { view = EmberView['default'].create(); run['default'](function() { view.append(); }); var parent = view.$().parent(); ok(property_get.get(view, 'element'), 'precond - has element'); run['default'](function() { view.destroyElement(); }); equal(view.$(), undefined, 'view has no selector'); ok(!parent.find('#'+view.get('elementId')).length, 'element no longer in parent node'); }); }); enifed('ember-views/tests/views/view/destroy_element_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/destroy_element_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/destroy_element_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/destroy_element_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/destroy_element_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/destroy_element_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/destroy_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/views/view'], function (property_get, run, EmberView) { 'use strict'; QUnit.module("Ember.View#destroy"); QUnit.test("should teardown viewName on parentView when childView is destroyed", function() { var viewName = "someChildView"; var parentView = EmberView['default'].create(); var childView = parentView.createChildView(EmberView['default'], { viewName: viewName }); equal(property_get.get(parentView, viewName), childView, "Precond - child view was registered on parent"); run['default'](function() { childView.destroy(); }); equal(property_get.get(parentView, viewName), null, "viewName reference was removed on parent"); run['default'](function() { parentView.destroy(); }); }); }); enifed('ember-views/tests/views/view/destroy_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/destroy_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/destroy_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/destroy_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/destroy_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/destroy_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/element_test', ['ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, property_set, run, EmberView, ContainerView) { 'use strict'; var parentView, view; QUnit.module("Ember.View#element", { teardown: function() { run['default'](function() { if (parentView) { parentView.destroy(); } view.destroy(); }); } }); QUnit.test("returns null if the view has no element and no parent view", function() { view = EmberView['default'].create(); equal(property_get.get(view, 'parentView'), null, 'precond - has no parentView'); equal(property_get.get(view, 'element'), null, 'has no element'); }); QUnit.test("returns null if the view has no element and parent view has no element", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); parentView = ContainerView['default'].create({ childViews: [EmberView['default'].extend()] }); view = property_get.get(parentView, 'childViews').objectAt(0); equal(property_get.get(view, 'parentView'), parentView, 'precond - has parent view'); equal(property_get.get(parentView, 'element'), null, 'parentView has no element'); equal(property_get.get(view, 'element'), null, ' has no element'); }); QUnit.test("returns element if you set the value", function() { view = EmberView['default'].create(); equal(property_get.get(view, 'element'), null, 'precond- has no element'); var dom = document.createElement('div'); property_set.set(view, 'element', dom); equal(property_get.get(view, 'element'), dom, 'now has set element'); }); }); enifed('ember-views/tests/views/view/element_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/element_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/element_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/element_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/element_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/element_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/evented_test', ['ember-metal/run_loop', 'ember-runtime/system/object', 'ember-views/views/view'], function (run, EmberObject, EmberView) { 'use strict'; var view; QUnit.module("EmberView evented helpers", { teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("fire should call method sharing event name if it exists on the view", function() { var eventFired = false; view = EmberView['default'].create({ fireMyEvent: function() { this.trigger('myEvent'); }, myEvent: function() { eventFired = true; } }); run['default'](function() { view.fireMyEvent(); }); equal(eventFired, true, "fired the view method sharing the event name"); }); QUnit.test("fire does not require a view method with the same name", function() { var eventFired = false; view = EmberView['default'].create({ fireMyEvent: function() { this.trigger('myEvent'); } }); var listenObject = EmberObject['default'].create({ onMyEvent: function() { eventFired = true; } }); view.on('myEvent', listenObject, 'onMyEvent'); run['default'](function() { view.fireMyEvent(); }); equal(eventFired, true, "fired the event without a view method sharing its name"); run['default'](function() { listenObject.destroy(); }); }); }); enifed('ember-views/tests/views/view/evented_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/evented_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/evented_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/evented_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/evented_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/evented_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/init_test', ['ember-metal/core', 'ember-metal/property_get', 'ember-metal/run_loop', 'ember-metal/computed', 'ember-views/views/view'], function (Ember, property_get, run, computed, EmberView) { 'use strict'; var originalLookup = Ember['default'].lookup; var lookup, view; QUnit.module("EmberView.create", { setup: function() { Ember['default'].lookup = lookup = {}; }, teardown: function() { run['default'](function() { view.destroy(); }); Ember['default'].lookup = originalLookup; } }); QUnit.test("registers view in the global views hash using layerId for event targeted", function() { view = EmberView['default'].create(); run['default'](function() { view.appendTo('#qunit-fixture'); }); equal(EmberView['default'].views[property_get.get(view, 'elementId')], view, 'registers view'); }); QUnit.module("EmberView.createWithMixins"); QUnit.test("should warn if a computed property is used for classNames", function() { expectAssertion(function() { EmberView['default'].createWithMixins({ elementId: 'test', classNames: computed.computed(function() { return ['className']; })["volatile"]() }); }, /Only arrays of static class strings.*For dynamic classes/i); }); QUnit.test("should warn if a non-array is used for classNameBindings", function() { expectAssertion(function() { EmberView['default'].createWithMixins({ elementId: 'test', classNameBindings: computed.computed(function() { return ['className']; })["volatile"]() }); }, /Only arrays are allowed/i); }); QUnit.test("creates a renderer if one is not provided", function() { var childView; view = EmberView['default'].create({ render: function(buffer) { buffer.push('Em'); this.appendChild(childView); } }); childView = EmberView['default'].create({ template: function() { return 'ber'; } }); run['default'](function() { view.append(); }); run['default'](function() { ok(property_get.get(view, 'renderer'), "view created without container receives a renderer"); strictEqual(property_get.get(view, 'renderer'), property_get.get(childView, 'renderer'), "parent and child share a renderer"); }); run['default'](function() { view.destroy(); childView.destroy(); }); }); }); enifed('ember-views/tests/views/view/init_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/init_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/init_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/init_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/init_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/init_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/inject_test', ['ember-runtime/system/service', 'ember-runtime/system/container', 'ember-runtime/inject', 'ember-views/views/view'], function (Service, system__container, inject, View) { 'use strict'; QUnit.module('EmberView - injected properties'); QUnit.test("services can be injected into views", function() { var registry = new system__container.Registry(); var container = registry.container(); registry.register('view:application', View['default'].extend({ profilerService: inject['default'].service('profiler') })); registry.register('service:profiler', Service['default'].extend()); var appView = container.lookup('view:application'); var profilerService = container.lookup('service:profiler'); equal(profilerService, appView.get('profilerService'), "service.profiler is injected"); }); }); enifed('ember-views/tests/views/view/inject_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/inject_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/inject_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/inject_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/inject_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/inject_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/is_visible_test', ['ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, property_set, run, EmberView, ContainerView) { 'use strict'; var View, view, parentBecameVisible, childBecameVisible, grandchildBecameVisible; var parentBecameHidden, childBecameHidden, grandchildBecameHidden; QUnit.module("EmberView#isVisible", { teardown: function() { if (view) { run['default'](function() { view.destroy(); }); } } }); QUnit.test("should hide views when isVisible is false", function() { view = EmberView['default'].create({ isVisible: false }); run['default'](function() { view.append(); }); ok(view.$().is(':hidden'), "the view is hidden"); run['default'](function() { property_set.set(view, 'isVisible', true); }); ok(view.$().is(':visible'), "the view is visible"); run['default'](function() { view.remove(); }); }); QUnit.test("should hide element if isVisible is false before element is created", function() { view = EmberView['default'].create({ isVisible: false }); ok(!property_get.get(view, 'isVisible'), "precond - view is not visible"); property_set.set(view, 'template', function() { return "foo"; }); run['default'](function() { view.append(); }); ok(view.$().is(':hidden'), "should be hidden"); run['default'](function() { view.remove(); }); run['default'](function() { property_set.set(view, 'isVisible', true); }); run['default'](function() { view.append(); }); ok(view.$().is(':visible'), "view should be visible"); run['default'](function() { view.remove(); }); }); QUnit.module("EmberView#isVisible with Container", { setup: function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); parentBecameVisible=0; childBecameVisible=0; grandchildBecameVisible=0; parentBecameHidden=0; childBecameHidden=0; grandchildBecameHidden=0; View = ContainerView['default'].extend({ childViews: ['child'], becameVisible: function() { parentBecameVisible++; }, becameHidden: function() { parentBecameHidden++; }, child: ContainerView['default'].extend({ childViews: ['grandchild'], becameVisible: function() { childBecameVisible++; }, becameHidden: function() { childBecameHidden++; }, grandchild: EmberView['default'].extend({ template: function() { return "seems weird bro"; }, becameVisible: function() { grandchildBecameVisible++; }, becameHidden: function() { grandchildBecameHidden++; } }) }) }); }, teardown: function() { if (view) { run['default'](function() { view.destroy(); }); } } }); QUnit.test("view should be notified after isVisible is set to false and the element has been hidden", function() { run['default'](function() { view = View.create({ isVisible: false }); view.append(); }); ok(view.$().is(':hidden'), "precond - view is hidden when appended"); run['default'](function() { view.set('isVisible', true); }); ok(view.$().is(':visible'), "precond - view is now visible"); equal(parentBecameVisible, 1); equal(childBecameVisible, 1); equal(grandchildBecameVisible, 1); }); QUnit.test("view should be notified after isVisible is set to false and the element has been hidden", function() { view = View.create({ isVisible: true }); var childView = view.get('childViews').objectAt(0); run['default'](function() { view.append(); }); ok(view.$().is(':visible'), "precond - view is visible when appended"); run['default'](function() { childView.set('isVisible', false); }); ok(childView.$().is(':hidden'), "precond - view is now hidden"); equal(childBecameHidden, 1); equal(grandchildBecameHidden, 1); }); QUnit.test("view should be notified after isVisible is set to true and the element has been shown", function() { view = View.create({ isVisible: false }); run['default'](function() { view.append(); }); ok(view.$().is(':hidden'), "precond - view is hidden when appended"); run['default'](function() { view.set('isVisible', true); }); ok(view.$().is(':visible'), "precond - view is now visible"); equal(parentBecameVisible, 1); equal(childBecameVisible, 1); equal(grandchildBecameVisible, 1); }); QUnit.test("if a view descends from a hidden view, making isVisible true should not trigger becameVisible", function() { view = View.create({ isVisible: true }); var childView = view.get('childViews').objectAt(0); run['default'](function() { view.append(); }); ok(view.$().is(':visible'), "precond - view is visible when appended"); run['default'](function() { childView.set('isVisible', false); }); run['default'](function() { view.set('isVisible', false); }); childBecameVisible = 0; grandchildBecameVisible = 0; run['default'](function() { childView.set('isVisible', true); }); equal(childBecameVisible, 0, "the child did not become visible"); equal(grandchildBecameVisible, 0, "the grandchild did not become visible"); }); QUnit.test("if a child view becomes visible while its parent is hidden, if its parent later becomes visible, it receives a becameVisible callback", function() { view = View.create({ isVisible: false }); var childView = view.get('childViews').objectAt(0); run['default'](function() { view.append(); }); ok(view.$().is(':hidden'), "precond - view is hidden when appended"); run['default'](function() { childView.set('isVisible', true); }); equal(childBecameVisible, 0, "child did not become visible since parent is hidden"); equal(grandchildBecameVisible, 0, "grandchild did not become visible since parent is hidden"); run['default'](function() { view.set('isVisible', true); }); equal(parentBecameVisible, 1); equal(childBecameVisible, 1); equal(grandchildBecameVisible, 1); }); }); enifed('ember-views/tests/views/view/is_visible_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/is_visible_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/is_visible_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/is_visible_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/is_visible_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/is_visible_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/jquery_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/views/view'], function (property_get, run, EmberView) { 'use strict'; var view; QUnit.module("EmberView#$", { setup: function() { view = EmberView['default'].extend({ render: function(context, firstTime) { context.push(''); } }).create(); run['default'](function() { view.append(); }); }, teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("returns undefined if no element", function() { var view = EmberView['default'].create(); ok(!property_get.get(view, 'element'), 'precond - should have no element'); equal(view.$(), undefined, 'should return undefined'); equal(view.$('span'), undefined, 'should undefined if filter passed'); run['default'](function() { view.destroy(); }); }); QUnit.test("returns jQuery object selecting element if provided", function() { ok(property_get.get(view, 'element'), 'precond - should have element'); var jquery = view.$(); equal(jquery.length, 1, 'view.$() should have one element'); equal(jquery[0], property_get.get(view, 'element'), 'element should be element'); }); QUnit.test("returns jQuery object selecting element inside element if provided", function() { ok(property_get.get(view, 'element'), 'precond - should have element'); var jquery = view.$('span'); equal(jquery.length, 1, 'view.$() should have one element'); equal(jquery[0].parentNode, property_get.get(view, 'element'), 'element should be in element'); }); QUnit.test("returns empty jQuery object if filter passed that does not match item in parent", function() { ok(property_get.get(view, 'element'), 'precond - should have element'); var jquery = view.$('body'); // would normally work if not scoped to view equal(jquery.length, 0, 'view.$(body) should have no elements'); }); }); enifed('ember-views/tests/views/view/jquery_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/jquery_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/jquery_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/jquery_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/jquery_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/jquery_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/layout_test', ['container/registry', 'ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/views/view'], function (Registry, property_get, run, EmberView) { 'use strict'; var registry, container, view; QUnit.module("EmberView - Layout Functionality", { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('template', { instantiate: false }); }, teardown: function() { run['default'](function() { view.destroy(); container.destroy(); }); registry = container = view = null; } }); QUnit.test("Layout views return throw if their layout cannot be found", function() { view = EmberView['default'].create({ layoutName: 'cantBeFound', container: { lookup: function() { } } }); expectAssertion(function() { property_get.get(view, 'layout'); }, /cantBeFound/); }); QUnit.test("should call the function of the associated layout", function() { var templateCalled = 0; var layoutCalled = 0; registry.register('template:template', function() { templateCalled++; }); registry.register('template:layout', function() { layoutCalled++; }); view = EmberView['default'].create({ container: container, layoutName: 'layout', templateName: 'template' }); run['default'](function() { view.createElement(); }); equal(templateCalled, 0, "template is not called when layout is present"); equal(layoutCalled, 1, "layout is called when layout is present"); }); QUnit.test("should call the function of the associated template with itself as the context", function() { registry.register('template:testTemplate', function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; }); view = EmberView['default'].create({ container: container, layoutName: 'testTemplate', context: { personName: "Tom DAAAALE" } }); run['default'](function() { view.createElement(); }); equal("template was called for Tom DAAAALE", view.$('#twas-called').text(), "the named template was called with the view as the data source"); }); QUnit.test("should fall back to defaultTemplate if neither template nor templateName are provided", function() { var View; View = EmberView['default'].extend({ defaultLayout: function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; } }); view = View.create({ context: { personName: "Tom DAAAALE" } }); run['default'](function() { view.createElement(); }); equal("template was called for Tom DAAAALE", view.$('#twas-called').text(), "the named template was called with the view as the data source"); }); QUnit.test("should not use defaultLayout if layout is provided", function() { var View; View = EmberView['default'].extend({ layout: function() { return "foo"; }, defaultLayout: function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; } }); view = View.create(); run['default'](function() { view.createElement(); }); equal("foo", view.$().text(), "default layout was not printed"); }); QUnit.test("the template property is available to the layout template", function() { view = EmberView['default'].create({ template: function(context, options) { options.data.buffer.push(" derp"); }, layout: function(context, options) { options.data.buffer.push("Herp"); property_get.get(options.data.view, 'template')(context, options); } }); run['default'](function() { view.createElement(); }); equal("Herp derp", view.$().text(), "the layout has access to the template"); }); }); enifed('ember-views/tests/views/view/layout_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/layout_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/layout_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/layout_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/layout_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/layout_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/nearest_of_type_test', ['ember-metal/run_loop', 'ember-metal/mixin', 'ember-views/views/view'], function (run, mixin, View) { 'use strict'; var parentView, view; QUnit.module("View#nearest*", { teardown: function() { run['default'](function() { if (parentView) { parentView.destroy(); } if (view) { view.destroy(); } }); } }); (function() { var Mixin = mixin.Mixin.create({}); var Parent = View['default'].extend(Mixin, { render: function(buffer) { this.appendChild(View['default'].create()); } }); QUnit.test("nearestOfType should find the closest view by view class", function() { var child; run['default'](function() { parentView = Parent.create(); parentView.appendTo('#qunit-fixture'); }); child = parentView.get('childViews')[0]; equal(child.nearestOfType(Parent), parentView, "finds closest view in the hierarchy by class"); }); QUnit.test("nearestOfType should find the closest view by mixin", function() { var child; run['default'](function() { parentView = Parent.create(); parentView.appendTo('#qunit-fixture'); }); child = parentView.get('childViews')[0]; equal(child.nearestOfType(Mixin), parentView, "finds closest view in the hierarchy by class"); }); QUnit.test("nearestWithProperty should search immediate parent", function() { var childView; view = View['default'].create({ myProp: true, render: function(buffer) { this.appendChild(View['default'].create()); } }); run['default'](function() { view.appendTo('#qunit-fixture'); }); childView = view.get('childViews')[0]; equal(childView.nearestWithProperty('myProp'), view); }); QUnit.test("nearestChildOf should be deprecated", function() { var child; run['default'](function() { parentView = Parent.create(); parentView.appendTo('#qunit-fixture'); }); child = parentView.get('childViews')[0]; expectDeprecation(function() { child.nearestChildOf(Parent); }, 'nearestChildOf has been deprecated.'); }); }()); }); enifed('ember-views/tests/views/view/nearest_of_type_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/nearest_of_type_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/nearest_of_type_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/nearest_of_type_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/nearest_of_type_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/nearest_of_type_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/nested_view_ordering_test', ['container/registry', 'ember-metal/run_loop', 'ember-views/views/view', 'ember-template-compiler/system/compile'], function (Registry, run, EmberView, compile) { 'use strict'; var registry, container, view; QUnit.module("EmberView - Nested View Ordering", { setup: function() { registry = new Registry['default'](); container = registry.container(); }, teardown: function() { run['default'](function() { if (view) { view.destroy(); } container.destroy(); }); registry = container = view = null; } }); QUnit.test("should call didInsertElement on child views before parent", function() { var insertedLast; view = EmberView['default'].create({ didInsertElement: function() { insertedLast = "outer"; }, container: container, template: compile['default']("{{view \"inner\"}}") }); registry.register("view:inner", EmberView['default'].extend({ didInsertElement: function() { insertedLast = "inner"; } })); run['default'](function() { view.append(); }); equal(insertedLast, "outer", "didInsertElement called on outer view after inner view"); }); }); enifed('ember-views/tests/views/view/nested_view_ordering_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/nested_view_ordering_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/nested_view_ordering_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/nested_view_ordering_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/nested_view_ordering_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/nested_view_ordering_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/remove_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-metal/enumerable_utils', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, run, enumerable_utils, jQuery, View, ContainerView) { 'use strict'; var parentView, child; QUnit.module("View#removeChild", { setup: function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); parentView = ContainerView['default'].create({ childViews: [View['default']] }); child = property_get.get(parentView, 'childViews').objectAt(0); }, teardown: function() { run['default'](function() { parentView.destroy(); child.destroy(); }); } }); QUnit.test("returns receiver", function() { equal(parentView.removeChild(child), parentView, 'receiver'); }); QUnit.test("removes child from parent.childViews array", function() { ok(enumerable_utils.indexOf(property_get.get(parentView, 'childViews'), child)>=0, 'precond - has child in childViews array before remove'); parentView.removeChild(child); ok(enumerable_utils.indexOf(property_get.get(parentView, 'childViews'), child)<0, 'removed child'); }); QUnit.test("sets parentView property to null", function() { ok(property_get.get(child, 'parentView'), 'precond - has parentView'); parentView.removeChild(child); ok(!property_get.get(child, 'parentView'), 'parentView is now null'); }); // ....................................................... // removeAllChildren() // var view, childViews; QUnit.module("View#removeAllChildren", { setup: function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].create({ childViews: [View['default'], View['default'], View['default']] }); childViews = view.get('childViews'); }, teardown: function() { run['default'](function() { childViews.forEach(function(v) { v.destroy(); }); view.destroy(); }); } }); QUnit.test("removes all child views", function() { equal(property_get.get(view, 'childViews.length'), 3, 'precond - has child views'); view.removeAllChildren(); equal(property_get.get(view, 'childViews.length'), 0, 'removed all children'); }); QUnit.test("returns receiver", function() { equal(view.removeAllChildren(), view, 'receiver'); }); // ....................................................... // removeFromParent() // QUnit.module("View#removeFromParent", { teardown: function() { run['default'](function() { if (parentView) { parentView.destroy(); } if (child) { child.destroy(); } if (view) { view.destroy(); } }); } }); QUnit.test("removes view from parent view", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); parentView = ContainerView['default'].create({ childViews: [View['default']] }); child = property_get.get(parentView, 'childViews').objectAt(0); ok(property_get.get(child, 'parentView'), 'precond - has parentView'); run['default'](function() { parentView.createElement(); }); ok(parentView.$('div').length, "precond - has a child DOM element"); run['default'](function() { child.removeFromParent(); }); ok(!property_get.get(child, 'parentView'), 'no longer has parentView'); ok(enumerable_utils.indexOf(property_get.get(parentView, 'childViews'), child)<0, 'no longer in parent childViews'); equal(parentView.$('div').length, 0, "removes DOM element from parent"); }); QUnit.test("returns receiver", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); parentView = ContainerView['default'].create({ childViews: [View['default']] }); child = property_get.get(parentView, 'childViews').objectAt(0); var removed = run['default'](function() { return child.removeFromParent(); }); equal(removed, child, 'receiver'); }); QUnit.test("does nothing if not in parentView", function() { child = View['default'].create(); // monkey patch for testing... ok(!property_get.get(child, 'parentView'), 'precond - has no parent'); child.removeFromParent(); run['default'](function() { child.destroy(); }); }); QUnit.test("the DOM element is gone after doing append and remove in two separate runloops", function() { view = View['default'].create(); run['default'](function() { view.append(); }); run['default'](function() { view.remove(); }); var viewElem = jQuery['default']('#'+property_get.get(view, 'elementId')); ok(viewElem.length === 0, "view's element doesn't exist in DOM"); }); QUnit.test("the DOM element is gone after doing append and remove in a single runloop", function() { view = View['default'].create(); run['default'](function() { view.append(); view.remove(); }); var viewElem = jQuery['default']('#'+property_get.get(view, 'elementId')); ok(viewElem.length === 0, "view's element doesn't exist in DOM"); }); }); enifed('ember-views/tests/views/view/remove_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/remove_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/remove_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/remove_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/remove_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/remove_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/render_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-views/views/container_view', 'ember-metal/computed', 'ember-template-compiler/system/compile'], function (property_get, run, jQuery, EmberView, ContainerView, computed, compile) { 'use strict'; var view; // ....................................................... // render() // QUnit.module("EmberView#render", { teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("default implementation does not render child views", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); var rendered = 0; var parentRendered = 0; view = ContainerView['default'].createWithMixins({ childViews: ["child"], render: function(buffer) { parentRendered++; this._super(buffer); }, child: EmberView['default'].createWithMixins({ render: function(buffer) { rendered++; this._super(buffer); } }) }); run['default'](function() { view.createElement(); }); equal(rendered, 1, 'rendered the child once'); equal(parentRendered, 1); equal(view.$('div').length, 1); }); QUnit.test("should invoke renderChildViews if layer is destroyed then re-rendered", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); var rendered = 0; var parentRendered = 0; view = ContainerView['default'].createWithMixins({ childViews: ["child"], render: function(buffer) { parentRendered++; this._super(buffer); }, child: EmberView['default'].createWithMixins({ render: function(buffer) { rendered++; this._super(buffer); } }) }); run['default'](function() { view.append(); }); equal(rendered, 1, 'rendered the child once'); equal(parentRendered, 1); equal(view.$('div').length, 1); run['default'](function() { view.rerender(); }); equal(rendered, 2, 'rendered the child twice'); equal(parentRendered, 2); equal(view.$('div').length, 1); run['default'](function() { view.destroy(); }); }); QUnit.test("should render child views with a different tagName", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].create({ childViews: ["child"], child: EmberView['default'].create({ tagName: 'aside' }) }); run['default'](function() { view.createElement(); }); equal(view.$('aside').length, 1); }); QUnit.test("should add ember-view to views", function() { view = EmberView['default'].create(); run['default'](function() { view.createElement(); }); ok(view.$().hasClass('ember-view'), "the view has ember-view"); }); QUnit.test("should allow tagName to be a computed property [DEPRECATED]", function() { view = EmberView['default'].extend({ tagName: computed.computed(function() { return 'span'; }) }).create(); expectDeprecation(function() { run['default'](function() { view.createElement(); }); }, /using a computed property to define tagName will not be permitted/); equal(view.element.tagName, 'SPAN', "the view has was created with the correct element"); run['default'](function() { view.set('tagName', 'div'); }); equal(view.element.tagName, 'SPAN', "the tagName cannot be changed after initial render"); }); QUnit.test("should allow hX tags as tagName", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].create({ childViews: ["child"], child: EmberView['default'].create({ tagName: 'h3' }) }); run['default'](function() { view.createElement(); }); ok(view.$('h3').length, "does not render the h3 tag correctly"); }); QUnit.test("should not add role attribute unless one is specified", function() { view = EmberView['default'].create(); run['default'](function() { view.createElement(); }); ok(view.$().attr('role') === undefined, "does not have a role attribute"); }); QUnit.test("should re-render if the context is changed", function() { view = EmberView['default'].create({ elementId: 'template-context-test', context: { foo: "bar" }, render: function(buffer) { var value = property_get.get(property_get.get(this, 'context'), 'foo'); buffer.push(value); } }); run['default'](function() { view.appendTo('#qunit-fixture'); }); equal(jQuery['default']('#qunit-fixture #template-context-test').text(), "bar", "precond - renders the view with the initial value"); run['default'](function() { view.set('context', { foo: "bang baz" }); }); equal(jQuery['default']('#qunit-fixture #template-context-test').text(), "bang baz", "re-renders the view with the updated context"); }); QUnit.test("renders contained view with omitted start tag and parent view context", function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); view = ContainerView['default'].createWithMixins({ tagName: 'table', childViews: ["row"], row: EmberView['default'].createWithMixins({ tagName: 'tr' }) }); run['default'](view, view.append); equal(view.element.tagName, 'TABLE', 'container view is table'); equal(view.element.childNodes[0].tagName, 'TR', 'inner view is tr'); run['default'](view, view.rerender); equal(view.element.tagName, 'TABLE', 'container view is table'); equal(view.element.childNodes[0].tagName, 'TR', 'inner view is tr'); }); QUnit.test("renders a contained view with omitted start tag and tagless parent view context", function() { view = EmberView['default'].createWithMixins({ tagName: 'table', template: compile['default']("{{view view.pivot}}"), pivot: EmberView['default'].extend({ tagName: '', template: compile['default']("{{view view.row}}"), row: EmberView['default'].extend({ tagName: 'tr' }) }) }); run['default'](view, view.append); equal(view.element.tagName, 'TABLE', 'container view is table'); ok(view.$('tr').length, 'inner view is tr'); run['default'](view, view.rerender); equal(view.element.tagName, 'TABLE', 'container view is table'); ok(view.$('tr').length, 'inner view is tr'); }); }); enifed('ember-views/tests/views/view/render_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/render_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/render_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/render_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/render_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/render_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/replace_in_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/view', 'ember-views/views/container_view'], function (property_get, run, jQuery, EmberView, ContainerView) { 'use strict'; var View, view; QUnit.module("EmberView - replaceIn()", { setup: function() { View = EmberView['default'].extend({}); }, teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("should be added to the specified element when calling replaceIn()", function() { jQuery['default']("#qunit-fixture").html(''); view = View.create(); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.replaceIn('#menu'); }); var viewElem = jQuery['default']('#menu').children(); ok(viewElem.length > 0, "creates and replaces the view's element"); }); QUnit.test("raises an assert when a target does not exist in the DOM", function() { view = View.create(); expectAssertion(function() { run['default'](function() { view.replaceIn('made-up-target'); }); }); }); QUnit.test("should remove previous elements when calling replaceIn()", function() { jQuery['default']("#qunit-fixture").html(''); var viewElem = jQuery['default']('#menu').children(); view = View.create(); ok(viewElem.length === 1, "should have one element"); run['default'](function() { view.replaceIn('#menu'); }); ok(viewElem.length === 1, "should have one element"); }); QUnit.test("should move the view to the inDOM state after replacing", function() { jQuery['default']("#qunit-fixture").html(''); view = View.create(); run['default'](function() { view.replaceIn('#menu'); }); equal(view.currentState, view._states.inDOM, "the view is in the inDOM state"); }); QUnit.module("EmberView - replaceIn() in a view hierarchy", { setup: function() { expectDeprecation("Setting `childViews` on a Container is deprecated."); View = ContainerView['default'].extend({ childViews: ['child'], child: EmberView['default'].extend({ elementId: 'child' }) }); }, teardown: function() { run['default'](function() { view.destroy(); }); } }); QUnit.test("should be added to the specified element when calling replaceIn()", function() { jQuery['default']("#qunit-fixture").html(''); view = View.create(); ok(!property_get.get(view, 'element'), "precond - should not have an element"); run['default'](function() { view.replaceIn('#menu'); }); var viewElem = jQuery['default']('#menu #child'); ok(viewElem.length > 0, "creates and replaces the view's element"); }); }); enifed('ember-views/tests/views/view/replace_in_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/replace_in_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/replace_in_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/replace_in_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/replace_in_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/replace_in_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/state_deprecation_test', ['ember-metal/platform/define_property', 'ember-metal/run_loop', 'ember-views/views/view'], function (define_property, run, EmberView) { 'use strict'; var view; QUnit.module("views/view/state_deprecation", { teardown: function() { if (view) { run['default'](view, 'destroy'); } } }); if (define_property.hasPropertyAccessors) { QUnit.test("view.state should be an alias of view._state with a deprecation", function() { expect(2); view = EmberView['default'].create(); expectDeprecation(function() { equal(view._state, view.state, '_state and state are aliased'); }, 'Usage of `state` is deprecated, use `_state` instead.'); }); QUnit.test("view.states should be an alias of view._states with a deprecation", function() { expect(2); view = EmberView['default'].create(); expectDeprecation(function() { equal(view._states, view.states, '_states and states are aliased'); }, 'Usage of `states` is deprecated, use `_states` instead.'); }); } QUnit.test("no deprecation is printed if view.state or view._state is not looked up", function() { expect(2); expectNoDeprecation(); var view = EmberView['default'].create(); ok(view, 'view was created'); }); }); enifed('ember-views/tests/views/view/state_deprecation_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/state_deprecation_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/state_deprecation_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/state_deprecation_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/state_deprecation_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/state_deprecation_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/stream_test', ['ember-metal/run_loop', 'ember-views/views/view'], function (run, EmberView) { 'use strict'; var view; QUnit.module("ember-views: streams", { teardown: function() { if (view) { run['default'](view, 'destroy'); } } }); QUnit.test("can return a stream that is notified of changes", function() { expect(2); view = EmberView['default'].create({ controller: { name: 'Robert' } }); var stream = view.getStream('name'); equal(stream.value(), 'Robert', 'initial value is correct'); stream.subscribe(function() { equal(stream.value(), 'Max', 'value is updated'); }); run['default'](view, 'set', 'controller.name', 'Max'); }); QUnit.test("a single stream is used for the same path", function() { expect(2); var stream1, stream2; view = EmberView['default'].create({ controller: { name: 'Robert' } }); stream1 = view.getStream('name'); stream2 = view.getStream('name'); equal(stream1, stream2, 'streams for the same path should be the same object'); stream1 = view.getStream(''); stream2 = view.getStream('this'); equal(stream1, stream2, 'streams "" and "this" should be the same object'); }); QUnit.test("the stream returned is labeled with the requested path", function() { expect(2); var stream; view = EmberView['default'].create({ controller: { name: 'Robert' }, foo: 'bar' }); stream = view.getStream('name'); equal(stream._label, 'name', 'stream is labeled'); stream = view.getStream('view.foo'); equal(stream._label, 'view.foo', 'stream is labeled'); }); }); enifed('ember-views/tests/views/view/stream_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/stream_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/stream_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/stream_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/stream_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/stream_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/template_test', ['container/registry', 'ember-metal/property_get', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-views/views/view'], function (Registry, property_get, run, EmberObject, EmberView) { 'use strict'; var registry, container, view; QUnit.module("EmberView - Template Functionality", { setup: function() { registry = new Registry['default'](); container = registry.container(); registry.optionsForType('template', { instantiate: false }); }, teardown: function() { run['default'](function() { if (view) { view.destroy(); } container.destroy(); registry = container = view = null; }); } }); QUnit.test("Template views return throw if their template cannot be found", function() { view = EmberView['default'].create({ templateName: 'cantBeFound', container: { lookup: function() { } } }); expectAssertion(function() { property_get.get(view, 'template'); }, /cantBeFound/); }); if (typeof Handlebars === "object") { QUnit.test("should allow standard Handlebars template usage", function() { view = EmberView['default'].create({ context: { name: "Erik" }, template: Handlebars.compile("Hello, {{name}}") }); run['default'](function() { view.createElement(); }); equal(view.$().text(), "Hello, Erik"); }); } QUnit.test("should call the function of the associated template", function() { registry.register('template:testTemplate', function() { return "

    template was called

    "; }); view = EmberView['default'].create({ container: container, templateName: 'testTemplate' }); run['default'](function() { view.createElement(); }); ok(view.$('#twas-called').length, "the named template was called"); }); QUnit.test("should call the function of the associated template with itself as the context", function() { registry.register('template:testTemplate', function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; }); view = EmberView['default'].create({ container: container, templateName: 'testTemplate', context: { personName: "Tom DAAAALE" } }); run['default'](function() { view.createElement(); }); equal("template was called for Tom DAAAALE", view.$('#twas-called').text(), "the named template was called with the view as the data source"); }); QUnit.test("should fall back to defaultTemplate if neither template nor templateName are provided", function() { var View; View = EmberView['default'].extend({ defaultTemplate: function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; } }); view = View.create({ context: { personName: "Tom DAAAALE" } }); run['default'](function() { view.createElement(); }); equal("template was called for Tom DAAAALE", view.$('#twas-called').text(), "the named template was called with the view as the data source"); }); QUnit.test("should not use defaultTemplate if template is provided", function() { var View; View = EmberView['default'].extend({ template: function() { return "foo"; }, defaultTemplate: function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; } }); view = View.create(); run['default'](function() { view.createElement(); }); equal("foo", view.$().text(), "default template was not printed"); }); QUnit.test("should not use defaultTemplate if template is provided", function() { var View; registry.register('template:foobar', function() { return 'foo'; }); View = EmberView['default'].extend({ container: container, templateName: 'foobar', defaultTemplate: function(dataSource) { return "

    template was called for " + property_get.get(dataSource, 'personName') + "

    "; } }); view = View.create(); run['default'](function() { view.createElement(); }); equal("foo", view.$().text(), "default template was not printed"); }); QUnit.test("should render an empty element if no template is specified", function() { view = EmberView['default'].create(); run['default'](function() { view.createElement(); }); equal(view.$().html(), '', "view div should be empty"); }); QUnit.test("should provide a controller to the template if a controller is specified on the view", function() { expect(7); var Controller1 = EmberObject['default'].extend({ toString: function() { return "Controller1"; } }); var Controller2 = EmberObject['default'].extend({ toString: function() { return "Controller2"; } }); var controller1 = Controller1.create(); var controller2 = Controller2.create(); var optionsDataKeywordsControllerForView; var optionsDataKeywordsControllerForChildView; var contextForView; var contextForControllerlessView; view = EmberView['default'].create({ controller: controller1, template: function(buffer, options) { optionsDataKeywordsControllerForView = options.data.view._keywords.controller.value(); } }); run['default'](function() { view.appendTo('#qunit-fixture'); }); strictEqual(optionsDataKeywordsControllerForView, controller1, "passes the controller in the data"); run['default'](function() { view.destroy(); }); var parentView = EmberView['default'].create({ controller: controller1, template: function(buffer, options) { options.data.view.appendChild(EmberView['default'].create({ controller: controller2, template: function(context, options) { contextForView = context; optionsDataKeywordsControllerForChildView = options.data.view._keywords.controller.value(); } })); optionsDataKeywordsControllerForView = options.data.view._keywords.controller.value(); } }); run['default'](function() { parentView.appendTo('#qunit-fixture'); }); strictEqual(optionsDataKeywordsControllerForView, controller1, "passes the controller in the data"); strictEqual(optionsDataKeywordsControllerForChildView, controller2, "passes the child view's controller in the data"); run['default'](function() { parentView.destroy(); }); var parentViewWithControllerlessChild = EmberView['default'].create({ controller: controller1, template: function(buffer, options) { options.data.view.appendChild(EmberView['default'].create({ template: function(context, options) { contextForControllerlessView = context; optionsDataKeywordsControllerForChildView = options.data.view._keywords.controller.value(); } })); optionsDataKeywordsControllerForView = options.data.view._keywords.controller.value(); } }); run['default'](function() { parentViewWithControllerlessChild.appendTo('#qunit-fixture'); }); strictEqual(optionsDataKeywordsControllerForView, controller1, "passes the original controller in the data"); strictEqual(optionsDataKeywordsControllerForChildView, controller1, "passes the controller in the data to child views"); strictEqual(contextForView, controller2, "passes the controller in as the main context of the parent view"); strictEqual(contextForControllerlessView, controller1, "passes the controller in as the main context of the child view"); run['default'](function() { parentView.destroy(); parentViewWithControllerlessChild.destroy(); }); }); QUnit.test("should throw an assertion if no container has been set", function() { expect(1); var View; View = EmberView['default'].extend({ templateName: 'foobar' }); throws(function() { view = View.create(); run['default'](function() { view.createElement(); }); }, /Container was not found when looking up a views template./); }); }); enifed('ember-views/tests/views/view/template_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/template_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/template_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/template_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/template_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/template_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/transition_to_deprecation_test', ['ember-views/views/view', 'ember-metal/run_loop'], function (EmberView, run) { 'use strict'; var view; QUnit.module('views/view/transition_to_deprecation', { setup: function() { view = EmberView['default'].create(); }, teardown: function() { run['default'](view, 'destroy'); } }); QUnit.test('deprecates when calling transitionTo', function() { expect(1); view = EmberView['default'].create(); expectDeprecation(function() { view.transitionTo('preRender'); }, ''); }); QUnit.test("doesn't deprecate when calling _transitionTo", function() { expect(1); view = EmberView['default'].create(); view._transitionTo('preRender'); ok(true); }); }); enifed('ember-views/tests/views/view/transition_to_deprecation_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/transition_to_deprecation_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/transition_to_deprecation_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/transition_to_deprecation_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/transition_to_deprecation_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/transition_to_deprecation_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/view_lifecycle_test', ['ember-metal/core', 'ember-metal/run_loop', 'ember-runtime/system/object', 'ember-views/system/jquery', 'ember-views/views/view'], function (Ember, run, EmberObject, jQuery, EmberView) { 'use strict'; var originalLookup = Ember['default'].lookup; var lookup, view; QUnit.module("views/view/view_lifecycle_test - pre-render", { setup: function() { Ember['default'].lookup = lookup = {}; }, teardown: function() { if (view) { run['default'](function() { view.destroy(); }); } Ember['default'].lookup = originalLookup; } }); function tmpl(str) { return function(context, options) { options.data.buffer.push(str); }; } QUnit.test("should create and append a DOM element after bindings have synced", function() { var ViewTest; lookup.ViewTest = ViewTest = {}; run['default'](function() { ViewTest.fakeController = EmberObject['default'].create({ fakeThing: 'controllerPropertyValue' }); view = EmberView['default'].createWithMixins({ fooBinding: 'ViewTest.fakeController.fakeThing', render: function(buffer) { buffer.push(this.get('foo')); } }); ok(!view.get('element'), "precond - does not have an element before appending"); view.append(); }); equal(view.$().text(), 'controllerPropertyValue', "renders and appends after bindings have synced"); }); QUnit.test("should throw an exception if trying to append a child before rendering has begun", function() { run['default'](function() { view = EmberView['default'].create(); }); throws(function() { view.appendChild(EmberView['default'], {}); }, null, "throws an error when calling appendChild()"); }); QUnit.test("should not affect rendering if rerender is called before initial render happens", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl("Rerender me!") }); view.rerender(); view.append(); }); equal(view.$().text(), "Rerender me!", "renders correctly if rerender is called first"); }); QUnit.test("should not affect rendering if destroyElement is called before initial render happens", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl("Don't destroy me!") }); view.destroyElement(); view.append(); }); equal(view.$().text(), "Don't destroy me!", "renders correctly if destroyElement is called first"); }); QUnit.module("views/view/view_lifecycle_test - in render", { setup: function() { }, teardown: function() { if (view) { run['default'](function() { view.destroy(); }); } } }); QUnit.test("appendChild should work inside a template", function() { run['default'](function() { view = EmberView['default'].create({ template: function(context, options) { var buffer = options.data.buffer; buffer.push("

    Hi!

    "); options.data.view.appendChild(EmberView['default'], { template: tmpl("Inception reached") }); buffer.push(""); } }); view.appendTo("#qunit-fixture"); }); ok(view.$('h1').length === 1 && view.$('div').length === 2, "The appended child is visible"); }); QUnit.test("rerender should throw inside a template", function() { throws(function() { run['default'](function() { var renderCount = 0; view = EmberView['default'].create({ template: function(context, options) { var view = options.data.view; var child1 = view.appendChild(EmberView['default'], { template: function(context, options) { renderCount++; options.data.buffer.push(String(renderCount)); } }); view.appendChild(EmberView['default'], { template: function(context, options) { options.data.buffer.push("Inside child2"); child1.rerender(); } }); } }); view.appendTo("#qunit-fixture"); }); }, /Something you did caused a view to re-render after it rendered but before it was inserted into the DOM./); }); QUnit.module("views/view/view_lifecycle_test - hasElement", { teardown: function() { if (view) { run['default'](function() { view.destroy(); }); } } }); QUnit.test("createElement puts the view into the hasElement state", function() { view = EmberView['default'].create({ render: function(buffer) { buffer.push('hello'); } }); run['default'](function() { view.createElement(); }); equal(view.currentState, view._states.hasElement, "the view is in the hasElement state"); }); QUnit.test("trigger rerender on a view in the hasElement state doesn't change its state to inDOM", function() { view = EmberView['default'].create({ render: function(buffer) { buffer.push('hello'); } }); run['default'](function() { view.createElement(); view.rerender(); }); equal(view.currentState, view._states.hasElement, "the view is still in the hasElement state"); }); QUnit.module("views/view/view_lifecycle_test - in DOM", { teardown: function() { if (view) { run['default'](function() { view.destroy(); }); } } }); QUnit.test("should throw an exception when calling appendChild when DOM element exists", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl("Wait for the kick") }); view.append(); }); throws(function() { view.appendChild(EmberView['default'], { template: tmpl("Ah ah ah! You didn't say the magic word!") }); }, null, "throws an exception when calling appendChild after element is created"); }); QUnit.test("should replace DOM representation if rerender() is called after element is created", function() { run['default'](function() { view = EmberView['default'].create({ template: function(context, options) { var buffer = options.data.buffer; var value = context.get('shape'); buffer.push("Do not taunt happy fun "+value); }, context: EmberObject['default'].create({ shape: 'sphere' }) }); view.append(); }); equal(view.$().text(), "Do not taunt happy fun sphere", "precond - creates DOM element"); view.set('context.shape', 'ball'); run['default'](function() { view.rerender(); }); equal(view.$().text(), "Do not taunt happy fun ball", "rerenders DOM element when rerender() is called"); }); QUnit.test("should destroy DOM representation when destroyElement is called", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl("Don't fear the reaper") }); view.append(); }); ok(view.get('element'), "precond - generates a DOM element"); run['default'](function() { view.destroyElement(); }); ok(!view.get('element'), "destroys view when destroyElement() is called"); }); QUnit.test("should destroy DOM representation when destroy is called", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl("
    Don't fear the reaper
    ") }); view.append(); }); ok(view.get('element'), "precond - generates a DOM element"); run['default'](function() { view.destroy(); }); ok(jQuery['default']('#warning').length === 0, "destroys element when destroy() is called"); }); QUnit.test("should throw an exception if trying to append an element that is already in DOM", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl('Broseidon, King of the Brocean') }); view.append(); }); ok(view.get('element'), "precond - creates DOM element"); throws(function() { run['default'](function() { view.append(); }); }, null, "raises an exception on second append"); }); QUnit.module("views/view/view_lifecycle_test - destroyed"); QUnit.test("should throw an exception when calling appendChild after view is destroyed", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl("Wait for the kick") }); view.append(); }); run['default'](function() { view.destroy(); }); throws(function() { view.appendChild(EmberView['default'], { template: tmpl("Ah ah ah! You didn't say the magic word!") }); }, null, "throws an exception when calling appendChild"); }); QUnit.test("should throw an exception when rerender is called after view is destroyed", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl('foo') }); view.append(); }); run['default'](function() { view.destroy(); }); throws(function() { view.rerender(); }, null, "throws an exception when calling rerender"); }); QUnit.test("should throw an exception when destroyElement is called after view is destroyed", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl('foo') }); view.append(); }); run['default'](function() { view.destroy(); }); throws(function() { view.destroyElement(); }, null, "throws an exception when calling destroyElement"); }); QUnit.test("trigger rerender on a view in the inDOM state keeps its state as inDOM", function() { run['default'](function() { view = EmberView['default'].create({ template: tmpl('foo') }); view.append(); }); run['default'](function() { view.rerender(); }); equal(view.currentState, view._states.inDOM, "the view is still in the inDOM state"); run['default'](function() { view.destroy(); }); }); }); enifed('ember-views/tests/views/view/view_lifecycle_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/view_lifecycle_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/view_lifecycle_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/view_lifecycle_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/view_lifecycle_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/view_lifecycle_test.js should pass jshint.'); }); }); enifed('ember-views/tests/views/view/virtual_views_test', ['ember-metal/property_get', 'ember-metal/run_loop', 'ember-views/system/jquery', 'ember-views/views/view'], function (property_get, run, jQuery, EmberView) { 'use strict'; var rootView, childView; QUnit.module("virtual views", { teardown: function() { run['default'](function() { rootView.destroy(); childView.destroy(); }); } }); QUnit.test("a virtual view does not appear as a view's parentView", function() { rootView = EmberView['default'].create({ elementId: 'root-view', render: function(buffer) { buffer.push("

    Hi

    "); this.appendChild(virtualView); } }); var virtualView = EmberView['default'].create({ isVirtual: true, tagName: '', render: function(buffer) { buffer.push("

    Virtual

    "); this.appendChild(childView); } }); childView = EmberView['default'].create({ render: function(buffer) { buffer.push("

    Bye!

    "); } }); run['default'](function() { jQuery['default']("#qunit-fixture").empty(); rootView.appendTo("#qunit-fixture"); }); equal(jQuery['default']("#root-view > h2").length, 1, "nodes with '' tagName do not create wrappers"); equal(property_get.get(childView, 'parentView'), rootView); var children = property_get.get(rootView, 'childViews'); equal(property_get.get(children, 'length'), 1, "there is one child element"); equal(children.objectAt(0), childView, "the child element skips through the virtual view"); }); QUnit.test("when a virtual view's child views change, the parent's childViews should reflect", function() { rootView = EmberView['default'].create({ elementId: 'root-view', render: function(buffer) { buffer.push("

    Hi

    "); this.appendChild(virtualView); } }); var virtualView = EmberView['default'].create({ isVirtual: true, tagName: '', render: function(buffer) { buffer.push("

    Virtual

    "); this.appendChild(childView); } }); childView = EmberView['default'].create({ render: function(buffer) { buffer.push("

    Bye!

    "); } }); run['default'](function() { jQuery['default']("#qunit-fixture").empty(); rootView.appendTo("#qunit-fixture"); }); equal(virtualView.get('childViews.length'), 1, "has childView - precond"); equal(rootView.get('childViews.length'), 1, "has childView - precond"); run['default'](function() { childView.removeFromParent(); }); equal(virtualView.get('childViews.length'), 0, "has no childView"); equal(rootView.get('childViews.length'), 0, "has no childView"); }); }); enifed('ember-views/tests/views/view/virtual_views_test.jscs-test', function () { 'use strict'; module('JSCS - ember-views/tests/views/view'); test('ember-views/tests/views/view/virtual_views_test.js should pass jscs', function() { ok(true, 'ember-views/tests/views/view/virtual_views_test.js should pass jscs.'); }); }); enifed('ember-views/tests/views/view/virtual_views_test.jshint', function () { 'use strict'; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/virtual_views_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/virtual_views_test.js should pass jshint.'); }); }); enifed('ember-views/views/bound_component_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/bound_component_view.js should pass jscs', function() { ok(true, 'ember-views/views/bound_component_view.js should pass jscs.'); }); }); enifed('ember-views/views/bound_component_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/bound_component_view.js should pass jshint', function() { ok(true, 'ember-views/views/bound_component_view.js should pass jshint.'); }); }); enifed('ember-views/views/bound_if_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/bound_if_view.js should pass jscs', function() { ok(true, 'ember-views/views/bound_if_view.js should pass jscs.'); }); }); enifed('ember-views/views/bound_if_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/bound_if_view.js should pass jshint', function() { ok(true, 'ember-views/views/bound_if_view.js should pass jshint.'); }); }); enifed('ember-views/views/bound_partial_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/bound_partial_view.js should pass jscs', function() { ok(true, 'ember-views/views/bound_partial_view.js should pass jscs.'); }); }); enifed('ember-views/views/bound_partial_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/bound_partial_view.js should pass jshint', function() { ok(true, 'ember-views/views/bound_partial_view.js should pass jshint.'); }); }); enifed('ember-views/views/checkbox.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/checkbox.js should pass jscs', function() { ok(true, 'ember-views/views/checkbox.js should pass jscs.'); }); }); enifed('ember-views/views/checkbox.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/checkbox.js should pass jshint', function() { ok(true, 'ember-views/views/checkbox.js should pass jshint.'); }); }); enifed('ember-views/views/collection_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/collection_view.js should pass jscs', function() { ok(true, 'ember-views/views/collection_view.js should pass jscs.'); }); }); enifed('ember-views/views/collection_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/collection_view.js should pass jshint', function() { ok(true, 'ember-views/views/collection_view.js should pass jshint.'); }); }); enifed('ember-views/views/component.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/component.js should pass jscs', function() { ok(true, 'ember-views/views/component.js should pass jscs.'); }); }); enifed('ember-views/views/component.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/component.js should pass jshint', function() { ok(true, 'ember-views/views/component.js should pass jshint.'); }); }); enifed('ember-views/views/container_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/container_view.js should pass jscs', function() { ok(true, 'ember-views/views/container_view.js should pass jscs.'); }); }); enifed('ember-views/views/container_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/container_view.js should pass jshint', function() { ok(true, 'ember-views/views/container_view.js should pass jshint.'); }); }); enifed('ember-views/views/core_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/core_view.js should pass jscs', function() { ok(true, 'ember-views/views/core_view.js should pass jscs.'); }); }); enifed('ember-views/views/core_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/core_view.js should pass jshint', function() { ok(true, 'ember-views/views/core_view.js should pass jshint.'); }); }); enifed('ember-views/views/each.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/each.js should pass jscs', function() { ok(true, 'ember-views/views/each.js should pass jscs.'); }); }); enifed('ember-views/views/each.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/each.js should pass jshint', function() { ok(true, 'ember-views/views/each.js should pass jshint.'); }); }); enifed('ember-views/views/metamorph_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/metamorph_view.js should pass jscs', function() { ok(true, 'ember-views/views/metamorph_view.js should pass jscs.'); }); }); enifed('ember-views/views/metamorph_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/metamorph_view.js should pass jshint', function() { ok(true, 'ember-views/views/metamorph_view.js should pass jshint.'); }); }); enifed('ember-views/views/select.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/select.js should pass jscs', function() { ok(true, 'ember-views/views/select.js should pass jscs.'); }); }); enifed('ember-views/views/select.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/select.js should pass jshint', function() { ok(true, 'ember-views/views/select.js should pass jshint.'); }); }); enifed('ember-views/views/simple_bound_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/simple_bound_view.js should pass jscs', function() { ok(true, 'ember-views/views/simple_bound_view.js should pass jscs.'); }); }); enifed('ember-views/views/simple_bound_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/simple_bound_view.js should pass jshint', function() { ok(true, 'ember-views/views/simple_bound_view.js should pass jshint.'); }); }); enifed('ember-views/views/states.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/states.js should pass jscs', function() { ok(true, 'ember-views/views/states.js should pass jscs.'); }); }); enifed('ember-views/views/states.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/states.js should pass jshint', function() { ok(true, 'ember-views/views/states.js should pass jshint.'); }); }); enifed('ember-views/views/states/default.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views/states'); test('ember-views/views/states/default.js should pass jscs', function() { ok(true, 'ember-views/views/states/default.js should pass jscs.'); }); }); enifed('ember-views/views/states/default.jshint', function () { 'use strict'; module('JSHint - ember-views/views/states'); test('ember-views/views/states/default.js should pass jshint', function() { ok(true, 'ember-views/views/states/default.js should pass jshint.'); }); }); enifed('ember-views/views/states/destroying.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views/states'); test('ember-views/views/states/destroying.js should pass jscs', function() { ok(true, 'ember-views/views/states/destroying.js should pass jscs.'); }); }); enifed('ember-views/views/states/destroying.jshint', function () { 'use strict'; module('JSHint - ember-views/views/states'); test('ember-views/views/states/destroying.js should pass jshint', function() { ok(true, 'ember-views/views/states/destroying.js should pass jshint.'); }); }); enifed('ember-views/views/states/has_element.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views/states'); test('ember-views/views/states/has_element.js should pass jscs', function() { ok(true, 'ember-views/views/states/has_element.js should pass jscs.'); }); }); enifed('ember-views/views/states/has_element.jshint', function () { 'use strict'; module('JSHint - ember-views/views/states'); test('ember-views/views/states/has_element.js should pass jshint', function() { ok(true, 'ember-views/views/states/has_element.js should pass jshint.'); }); }); enifed('ember-views/views/states/in_buffer.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views/states'); test('ember-views/views/states/in_buffer.js should pass jscs', function() { ok(true, 'ember-views/views/states/in_buffer.js should pass jscs.'); }); }); enifed('ember-views/views/states/in_buffer.jshint', function () { 'use strict'; module('JSHint - ember-views/views/states'); test('ember-views/views/states/in_buffer.js should pass jshint', function() { ok(true, 'ember-views/views/states/in_buffer.js should pass jshint.'); }); }); enifed('ember-views/views/states/in_dom.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views/states'); test('ember-views/views/states/in_dom.js should pass jscs', function() { ok(true, 'ember-views/views/states/in_dom.js should pass jscs.'); }); }); enifed('ember-views/views/states/in_dom.jshint', function () { 'use strict'; module('JSHint - ember-views/views/states'); test('ember-views/views/states/in_dom.js should pass jshint', function() { ok(true, 'ember-views/views/states/in_dom.js should pass jshint.'); }); }); enifed('ember-views/views/states/pre_render.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views/states'); test('ember-views/views/states/pre_render.js should pass jscs', function() { ok(true, 'ember-views/views/states/pre_render.js should pass jscs.'); }); }); enifed('ember-views/views/states/pre_render.jshint', function () { 'use strict'; module('JSHint - ember-views/views/states'); test('ember-views/views/states/pre_render.js should pass jshint', function() { ok(true, 'ember-views/views/states/pre_render.js should pass jshint.'); }); }); enifed('ember-views/views/text_area.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/text_area.js should pass jscs', function() { ok(true, 'ember-views/views/text_area.js should pass jscs.'); }); }); enifed('ember-views/views/text_area.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/text_area.js should pass jshint', function() { ok(true, 'ember-views/views/text_area.js should pass jshint.'); }); }); enifed('ember-views/views/text_field.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/text_field.js should pass jscs', function() { ok(true, 'ember-views/views/text_field.js should pass jscs.'); }); }); enifed('ember-views/views/text_field.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/text_field.js should pass jshint', function() { ok(true, 'ember-views/views/text_field.js should pass jshint.'); }); }); enifed('ember-views/views/view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/view.js should pass jscs', function() { ok(true, 'ember-views/views/view.js should pass jscs.'); }); }); enifed('ember-views/views/view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/view.js should pass jshint', function() { ok(true, 'ember-views/views/view.js should pass jshint.'); }); }); enifed('ember-views/views/with_view.jscs-test', function () { 'use strict'; module('JSCS - ember-views/views'); test('ember-views/views/with_view.js should pass jscs', function() { ok(true, 'ember-views/views/with_view.js should pass jscs.'); }); }); enifed('ember-views/views/with_view.jshint', function () { 'use strict'; module('JSHint - ember-views/views'); test('ember-views/views/with_view.js should pass jshint', function() { ok(true, 'ember-views/views/with_view.js should pass jshint.'); }); }); enifed('ember.jscs-test', function () { 'use strict'; module('JSCS - .'); test('ember.js should pass jscs', function() { ok(true, 'ember.js should pass jscs.'); }); }); enifed('ember.jshint', function () { 'use strict'; module('JSHint - .'); test('ember.js should pass jshint', function() { ok(true, 'ember.js should pass jshint.'); }); }); enifed('ember/tests/application_lifecycle', ['ember'], function () { 'use strict'; var App, container, router; QUnit.module("Application Lifecycle", { setup: function() { Ember.run(function() { App = Ember.Application.create({ rootElement: '#qunit-fixture' }); App.Router = App.Router.extend({ location: 'none' }); App.deferReadiness(); container = App.__container__; }); }, teardown: function() { router = null; Ember.run(App, 'destroy'); } }); function handleURL(path) { router = container.lookup('router:main'); return Ember.run(function() { return router.handleURL(path).then(function(value) { ok(true, 'url: `' + path + '` was handled'); return value; }, function(reason) { ok(false, reason); throw reason; }); }); } QUnit.test("Resetting the application allows controller properties to be set when a route deactivates", function() { App.Router.map(function() { this.route('home', { path: '/' }); }); App.HomeRoute = Ember.Route.extend({ setupController: function() { this.controllerFor('home').set('selectedMenuItem', 'home'); }, deactivate: function() { this.controllerFor('home').set('selectedMenuItem', null); } }); App.ApplicationRoute = Ember.Route.extend({ setupController: function() { this.controllerFor('application').set('selectedMenuItem', 'home'); }, deactivate: function() { this.controllerFor('application').set('selectedMenuItem', null); } }); container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); handleURL('/'); equal(Ember.controllerFor(container, 'home').get('selectedMenuItem'), 'home'); equal(Ember.controllerFor(container, 'application').get('selectedMenuItem'), 'home'); App.reset(); equal(Ember.controllerFor(container, 'home').get('selectedMenuItem'), null); equal(Ember.controllerFor(container, 'application').get('selectedMenuItem'), null); }); QUnit.test("Destroying the application resets the router before the container is destroyed", function() { App.Router.map(function() { this.route('home', { path: '/' }); }); App.HomeRoute = Ember.Route.extend({ setupController: function() { this.controllerFor('home').set('selectedMenuItem', 'home'); }, deactivate: function() { this.controllerFor('home').set('selectedMenuItem', null); } }); App.ApplicationRoute = Ember.Route.extend({ setupController: function() { this.controllerFor('application').set('selectedMenuItem', 'home'); }, deactivate: function() { this.controllerFor('application').set('selectedMenuItem', null); } }); container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); handleURL('/'); equal(Ember.controllerFor(container, 'home').get('selectedMenuItem'), 'home'); equal(Ember.controllerFor(container, 'application').get('selectedMenuItem'), 'home'); Ember.run(App, 'destroy'); equal(Ember.controllerFor(container, 'home').get('selectedMenuItem'), null); equal(Ember.controllerFor(container, 'application').get('selectedMenuItem'), null); }); }); enifed('ember/tests/application_lifecycle.jscs-test', function () { 'use strict'; module('JSCS - ember/tests'); test('ember/tests/application_lifecycle.js should pass jscs', function() { ok(true, 'ember/tests/application_lifecycle.js should pass jscs.'); }); }); enifed('ember/tests/application_lifecycle.jshint', function () { 'use strict'; module('JSHint - ember/tests'); test('ember/tests/application_lifecycle.js should pass jshint', function() { ok(true, 'ember/tests/application_lifecycle.js should pass jshint.'); }); }); enifed('ember/tests/component_registration_test', ['ember', 'ember-template-compiler/system/compile', 'ember-htmlbars/helpers'], function (__dep0__, compile, helpers) { 'use strict'; var App, registry, container; var originalHelpers; function prepare() { Ember.TEMPLATES["components/expand-it"] = compile['default']("

    hello {{yield}}

    "); Ember.TEMPLATES.application = compile['default']("Hello world {{#expand-it}}world{{/expand-it}}"); originalHelpers = Ember.A(Ember.keys(helpers['default'])); } function cleanup() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; cleanupHandlebarsHelpers(); }); } function cleanupHandlebarsHelpers() { var currentHelpers = Ember.A(Ember.keys(helpers['default'])); currentHelpers.forEach(function(name) { if (!originalHelpers.contains(name)) { delete helpers['default'][name]; } }); } QUnit.module("Application Lifecycle - Component Registration", { setup: prepare, teardown: cleanup }); function boot(callback) { Ember.run(function() { App = Ember.Application.create({ name: 'App', rootElement: '#qunit-fixture' }); App.deferReadiness(); App.Router = Ember.Router.extend({ location: 'none' }); registry = App.registry; container = App.__container__; if (callback) { callback(); } }); var router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); Ember.run(function() { router.handleURL('/'); }); } QUnit.test("The helper becomes the body of the component", function() { boot(); equal(Ember.$('div.ember-view > div.ember-view', '#qunit-fixture').text(), "hello world", "The component is composed correctly"); }); QUnit.test("If a component is registered, it is used", function() { boot(function() { registry.register('component:expand-it', Ember.Component.extend({ classNames: 'testing123' })); }); equal(Ember.$('div.testing123', '#qunit-fixture').text(), "hello world", "The component is composed correctly"); }); QUnit.test("Late-registered components can be rendered with custom `template` property (DEPRECATED)", function() { Ember.TEMPLATES.application = compile['default']("
    there goes {{my-hero}}
    "); expectDeprecation(/Do not specify template on a Component/); boot(function() { registry.register('component:my-hero', Ember.Component.extend({ classNames: 'testing123', template: function() { return "watch him as he GOES"; } })); }); equal(Ember.$('#wrapper').text(), "there goes watch him as he GOES", "The component is composed correctly"); ok(!helpers['default']['my-hero'], "Component wasn't saved to global helpers hash"); }); QUnit.test("Late-registered components can be rendered with template registered on the container", function() { Ember.TEMPLATES.application = compile['default']("
    hello world {{sally-rutherford}}-{{#sally-rutherford}}!!!{{/sally-rutherford}}
    "); boot(function() { registry.register('template:components/sally-rutherford', compile['default']("funkytowny{{yield}}")); registry.register('component:sally-rutherford', Ember.Component); }); equal(Ember.$('#wrapper').text(), "hello world funkytowny-funkytowny!!!", "The component is composed correctly"); ok(!helpers['default']['sally-rutherford'], "Component wasn't saved to global helpers hash"); }); QUnit.test("Late-registered components can be rendered with ONLY the template registered on the container", function() { Ember.TEMPLATES.application = compile['default']("
    hello world {{borf-snorlax}}-{{#borf-snorlax}}!!!{{/borf-snorlax}}
    "); boot(function() { registry.register('template:components/borf-snorlax', compile['default']("goodfreakingTIMES{{yield}}")); }); equal(Ember.$('#wrapper').text(), "hello world goodfreakingTIMES-goodfreakingTIMES!!!", "The component is composed correctly"); ok(!helpers['default']['borf-snorlax'], "Component wasn't saved to global helpers hash"); }); QUnit.test("Component-like invocations are treated as bound paths if neither template nor component are registered on the container", function() { Ember.TEMPLATES.application = compile['default']("
    {{user-name}} hello {{api-key}} world
    "); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'user-name': 'machty' })); }); equal(Ember.$('#wrapper').text(), "machty hello world", "The component is composed correctly"); }); QUnit.test("Assigning templateName to a component should setup the template as a layout (DEPRECATED)", function() { expect(2); Ember.TEMPLATES.application = compile['default']("
    {{#my-component}}{{text}}{{/my-component}}
    "); Ember.TEMPLATES['foo-bar-baz'] = compile['default']("{{text}}-{{yield}}"); expectDeprecation(/Do not specify templateName on a Component/); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); registry.register('component:my-component', Ember.Component.extend({ text: 'inner', templateName: 'foo-bar-baz' })); }); equal(Ember.$('#wrapper').text(), "inner-outer", "The component is composed correctly"); }); QUnit.test("Assigning templateName and layoutName should use the templates specified", function() { expect(1); Ember.TEMPLATES.application = compile['default']("
    {{my-component}}
    "); Ember.TEMPLATES['foo'] = compile['default']("{{text}}"); Ember.TEMPLATES['bar'] = compile['default']("{{text}}-{{yield}}"); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); registry.register('component:my-component', Ember.Component.extend({ text: 'inner', layoutName: 'bar', templateName: 'foo' })); }); equal(Ember.$('#wrapper').text(), "inner-outer", "The component is composed correctly"); }); if (!Ember.FEATURES.isEnabled('ember-htmlbars')) { // jscs:disable validateIndentation // ember-htmlbars doesn't throw an exception when a helper is not found QUnit.test('Using name of component that does not exist', function () { Ember.TEMPLATES.application = compile['default']("
    {{#no-good}} {{/no-good}}
    "); throws(function () { boot(); }, /Could not find component or helper named 'no-good'/); }); // jscs:enable validateIndentation } QUnit.module("Application Lifecycle - Component Context", { setup: prepare, teardown: cleanup }); QUnit.test("Components with a block should have the proper content when a template is provided", function() { Ember.TEMPLATES.application = compile['default']("
    {{#my-component}}{{text}}{{/my-component}}
    "); Ember.TEMPLATES['components/my-component'] = compile['default']("{{text}}-{{yield}}"); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); registry.register('component:my-component', Ember.Component.extend({ text: 'inner' })); }); equal(Ember.$('#wrapper').text(), "inner-outer", "The component is composed correctly"); }); QUnit.test("Components with a block should yield the proper content without a template provided", function() { Ember.TEMPLATES.application = compile['default']("
    {{#my-component}}{{text}}{{/my-component}}
    "); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); registry.register('component:my-component', Ember.Component.extend({ text: 'inner' })); }); equal(Ember.$('#wrapper').text(), "outer", "The component is composed correctly"); }); QUnit.test("Components without a block should have the proper content when a template is provided", function() { Ember.TEMPLATES.application = compile['default']("
    {{my-component}}
    "); Ember.TEMPLATES['components/my-component'] = compile['default']("{{text}}"); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); registry.register('component:my-component', Ember.Component.extend({ text: 'inner' })); }); equal(Ember.$('#wrapper').text(), "inner", "The component is composed correctly"); }); QUnit.test("Components without a block should have the proper content", function() { Ember.TEMPLATES.application = compile['default']("
    {{my-component}}
    "); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); registry.register('component:my-component', Ember.Component.extend({ didInsertElement: function() { this.$().html('Some text inserted by jQuery'); } })); }); equal(Ember.$('#wrapper').text(), "Some text inserted by jQuery", "The component is composed correctly"); }); QUnit.test("properties of a component without a template should not collide with internal structures", function() { Ember.TEMPLATES.application = compile['default']("
    {{my-component data=foo}}
    "); boot(function() { registry.register('controller:application', Ember.Controller.extend({ 'text': 'outer', 'foo': 'Some text inserted by jQuery' })); registry.register('component:my-component', Ember.Component.extend({ didInsertElement: function() { this.$().html(this.get('data')); } })); }); equal(Ember.$('#wrapper').text(), "Some text inserted by jQuery", "The component is composed correctly"); }); QUnit.test("Components trigger actions in the parents context when called from within a block", function() { Ember.TEMPLATES.application = compile['default']("
    {{#my-component}}Fizzbuzz{{/my-component}}
    "); boot(function() { registry.register('controller:application', Ember.Controller.extend({ actions: { fizzbuzz: function() { ok(true, 'action triggered on parent'); } } })); registry.register('component:my-component', Ember.Component.extend()); }); Ember.run(function() { Ember.$('#fizzbuzz', "#wrapper").click(); }); }); QUnit.test("Components trigger actions in the components context when called from within its template", function() { Ember.TEMPLATES.application = compile['default']("
    {{#my-component}}{{text}}{{/my-component}}
    "); Ember.TEMPLATES['components/my-component'] = compile['default']("Fizzbuzz"); boot(function() { registry.register('controller:application', Ember.Controller.extend({ actions: { fizzbuzz: function() { ok(false, 'action triggered on the wrong context'); } } })); registry.register('component:my-component', Ember.Component.extend({ actions: { fizzbuzz: function() { ok(true, 'action triggered on component'); } } })); }); Ember.$('#fizzbuzz', "#wrapper").click(); }); }); enifed('ember/tests/component_registration_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests'); test('ember/tests/component_registration_test.js should pass jscs', function() { ok(true, 'ember/tests/component_registration_test.js should pass jscs.'); }); }); enifed('ember/tests/component_registration_test.jshint', function () { 'use strict'; module('JSHint - ember/tests'); test('ember/tests/component_registration_test.js should pass jshint', function() { ok(true, 'ember/tests/component_registration_test.js should pass jshint.'); }); }); enifed('ember/tests/global-api-test', ['ember'], function () { 'use strict'; QUnit.module("Global API Tests"); function confirmExport(property) { QUnit.test('confirm ' + property + ' is exported', function() { ok(Ember.get(window, property) + ' is exported propertly'); }); } confirmExport('Ember.DefaultResolver'); confirmExport('Ember.generateController'); }); enifed('ember/tests/global-api-test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests'); test('ember/tests/global-api-test.js should pass jscs', function() { ok(true, 'ember/tests/global-api-test.js should pass jscs.'); }); }); enifed('ember/tests/global-api-test.jshint', function () { 'use strict'; module('JSHint - ember/tests'); test('ember/tests/global-api-test.js should pass jshint', function() { ok(true, 'ember/tests/global-api-test.js should pass jshint.'); }); }); enifed('ember/tests/helpers/helper_registration_test', ['ember', 'ember-htmlbars/compat'], function (__dep0__, EmberHandlebars) { 'use strict'; var compile, helpers, makeBoundHelper; compile = EmberHandlebars['default'].compile; helpers = EmberHandlebars['default'].helpers; makeBoundHelper = EmberHandlebars['default'].makeBoundHelper; var App, registry, container; function reverseHelper(value) { return arguments.length > 1 ? value.split('').reverse().join('') : "--"; } QUnit.module("Application Lifecycle - Helper Registration", { teardown: function() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; }); } }); var boot = function(callback) { Ember.run(function() { App = Ember.Application.create({ name: 'App', rootElement: '#qunit-fixture' }); App.deferReadiness(); App.Router = Ember.Router.extend({ location: 'none' }); registry = App.registry; container = App.__container__; if (callback) { callback(); } }); var router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); Ember.run(function() { router.handleURL('/'); }); }; QUnit.test("Unbound dashed helpers registered on the container can be late-invoked", function() { Ember.TEMPLATES.application = compile("
    {{x-borf}} {{x-borf YES}}
    "); boot(function() { registry.register('helper:x-borf', function(val) { return arguments.length > 1 ? val : "BORF"; }); }); equal(Ember.$('#wrapper').text(), "BORF YES", "The helper was invoked from the container"); ok(!helpers['x-borf'], "Container-registered helper doesn't wind up on global helpers hash"); }); // need to make `makeBoundHelper` for HTMLBars QUnit.test("Bound helpers registered on the container can be late-invoked", function() { Ember.TEMPLATES.application = compile("
    {{x-reverse}} {{x-reverse foo}}
    "); boot(function() { registry.register('controller:application', Ember.Controller.extend({ foo: "alex" })); registry.register('helper:x-reverse', makeBoundHelper(reverseHelper)); }); equal(Ember.$('#wrapper').text(), "-- xela", "The bound helper was invoked from the container"); ok(!helpers['x-reverse'], "Container-registered helper doesn't wind up on global helpers hash"); }); if (!Ember.FEATURES.isEnabled('ember-htmlbars')) { // jscs:disable validateIndentation // we have unit tests for this in ember-htmlbars/tests/system/lookup-helper // and we are not going to recreate the handlebars helperMissing concept QUnit.test("Undashed helpers registered on the container can not (presently) be invoked", function() { var realHelperMissing = helpers.helperMissing; helpers.helperMissing = function() { return "NOHALPER"; }; // Note: the reason we're not allowing undashed helpers is to avoid // a possible perf hit in hot code paths, i.e. _triageMustache. // We only presently perform container lookups if prop.indexOf('-') >= 0 Ember.TEMPLATES.application = compile("
    {{omg}}|{{omg 'GRRR'}}|{{yorp}}|{{yorp 'ahh'}}
    "); boot(function() { registry.register('helper:omg', function() { return "OMG"; }); registry.register('helper:yorp', makeBoundHelper(function() { return "YORP"; })); }); equal(Ember.$('#wrapper').text(), "|NOHALPER||NOHALPER", "The undashed helper was invoked from the container"); helpers.helperMissing = realHelperMissing; }); // jscs:enable validateIndentation } }); enifed('ember/tests/helpers/helper_registration_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests/helpers'); test('ember/tests/helpers/helper_registration_test.js should pass jscs', function() { ok(true, 'ember/tests/helpers/helper_registration_test.js should pass jscs.'); }); }); enifed('ember/tests/helpers/helper_registration_test.jshint', function () { 'use strict'; module('JSHint - ember/tests/helpers'); test('ember/tests/helpers/helper_registration_test.js should pass jshint', function() { ok(true, 'ember/tests/helpers/helper_registration_test.js should pass jshint.'); }); }); enifed('ember/tests/helpers/link_to_test', ['ember', 'ember-runtime/controllers/object_controller', 'ember-htmlbars/compat'], function (__dep0__, object_controller, EmberHandlebars) { 'use strict'; var compile = EmberHandlebars['default'].compile; var Router, App, AppView, router, registry, container; var set = Ember.set; function bootApplication() { router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); } // IE includes the host name function normalizeUrl(url) { return url.replace(/https?:\/\/[^\/]+/, ''); } function shouldNotBeActive(selector) { checkActive(selector, false); } function shouldBeActive(selector) { checkActive(selector, true); } function checkActive(selector, active) { var classList = Ember.$(selector, '#qunit-fixture')[0].className; equal(classList.indexOf('active') > -1, active, selector + " active should be " + active.toString()); } var updateCount, replaceCount; function sharedSetup() { App = Ember.Application.create({ name: "App", rootElement: '#qunit-fixture' }); App.deferReadiness(); updateCount = replaceCount = 0; App.Router.reopen({ location: Ember.NoneLocation.createWithMixins({ setURL: function(path) { updateCount++; set(this, 'path', path); }, replaceURL: function(path) { replaceCount++; set(this, 'path', path); } }) }); Router = App.Router; registry = App.registry; container = App.__container__; } function sharedTeardown() { Ember.run(function() { App.destroy(); }); Ember.TEMPLATES = {}; } QUnit.module("The {{link-to}} helper", { setup: function() { Ember.run(function() { sharedSetup(); Ember.TEMPLATES.app = compile("{{outlet}}"); Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'about' id='about-link'}}About{{/link-to}}{{#link-to 'index' id='self-link'}}Self{{/link-to}}"); Ember.TEMPLATES.about = compile("

    About

    {{#link-to 'index' id='home-link'}}Home{{/link-to}}{{#link-to 'about' id='self-link'}}Self{{/link-to}}"); Ember.TEMPLATES.item = compile("

    Item

    {{model.name}}

    {{#link-to 'index' id='home-link'}}Home{{/link-to}}"); AppView = Ember.View.extend({ templateName: 'app' }); registry.register('view:app', AppView); registry.unregister('router:main'); registry.register('router:main', Router); }); }, teardown: sharedTeardown }); QUnit.test("The {{link-to}} helper moves into the named route", function() { Router.map(function(match) { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(Ember.$('h3:contains(Home)', '#qunit-fixture').length, 1, "The home template was rendered"); equal(Ember.$('#self-link.active', '#qunit-fixture').length, 1, "The self-link was rendered with active class"); equal(Ember.$('#about-link:not(.active)', '#qunit-fixture').length, 1, "The other link was rendered without active class"); Ember.run(function() { Ember.$('#about-link', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(About)', '#qunit-fixture').length, 1, "The about template was rendered"); equal(Ember.$('#self-link.active', '#qunit-fixture').length, 1, "The self-link was rendered with active class"); equal(Ember.$('#home-link:not(.active)', '#qunit-fixture').length, 1, "The other link was rendered without active class"); }); QUnit.test("The {{link-to}} helper supports URL replacement", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'about' id='about-link' replace=true}}About{{/link-to}}"); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(updateCount, 0, 'precond: setURL has not been called'); equal(replaceCount, 0, 'precond: replaceURL has not been called'); Ember.run(function() { Ember.$('#about-link', '#qunit-fixture').click(); }); equal(updateCount, 0, 'setURL should not be called'); equal(replaceCount, 1, 'replaceURL should be called once'); }); QUnit.test("the {{link-to}} helper doesn't add an href when the tagName isn't 'a'", function() { Ember.TEMPLATES.index = compile("{{#link-to 'about' id='about-link' tagName='div'}}About{{/link-to}}"); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(Ember.$('#about-link').attr('href'), undefined, "there is no href attribute"); }); QUnit.test("the {{link-to}} applies a 'disabled' class when disabled", function () { Ember.TEMPLATES.index = compile('{{#link-to "about" id="about-link" disabledWhen="shouldDisable"}}About{{/link-to}}'); App.IndexController = Ember.Controller.extend({ shouldDisable: true }); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(Ember.$('#about-link.disabled', '#qunit-fixture').length, 1, "The link is disabled when its disabledWhen is true"); }); QUnit.test("the {{link-to}} doesn't apply a 'disabled' class if disabledWhen is not provided", function () { Ember.TEMPLATES.index = compile('{{#link-to "about" id="about-link"}}About{{/link-to}}'); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); ok(!Ember.$('#about-link', '#qunit-fixture').hasClass("disabled"), "The link is not disabled if disabledWhen not provided"); }); QUnit.test("the {{link-to}} helper supports a custom disabledClass", function () { Ember.TEMPLATES.index = compile('{{#link-to "about" id="about-link" disabledWhen="shouldDisable" disabledClass="do-not-want"}}About{{/link-to}}'); App.IndexController = Ember.Controller.extend({ shouldDisable: true }); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(Ember.$('#about-link.do-not-want', '#qunit-fixture').length, 1, "The link can apply a custom disabled class"); }); QUnit.test("the {{link-to}} helper does not respond to clicks when disabled", function () { Ember.TEMPLATES.index = compile('{{#link-to "about" id="about-link" disabledWhen="shouldDisable"}}About{{/link-to}}'); App.IndexController = Ember.Controller.extend({ shouldDisable: true }); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); Ember.run(function() { Ember.$('#about-link', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(About)', '#qunit-fixture').length, 0, "Transitioning did not occur"); }); QUnit.test("The {{link-to}} helper supports a custom activeClass", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'about' id='about-link'}}About{{/link-to}}{{#link-to 'index' id='self-link' activeClass='zomg-active'}}Self{{/link-to}}"); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(Ember.$('h3:contains(Home)', '#qunit-fixture').length, 1, "The home template was rendered"); equal(Ember.$('#self-link.zomg-active', '#qunit-fixture').length, 1, "The self-link was rendered with active class"); equal(Ember.$('#about-link:not(.active)', '#qunit-fixture').length, 1, "The other link was rendered without active class"); }); QUnit.test("The {{link-to}} helper supports leaving off .index for nested routes", function() { Router.map(function() { this.resource("about", function() { this.route("item"); }); }); Ember.TEMPLATES.about = compile("

    About

    {{outlet}}"); Ember.TEMPLATES['about/index'] = compile("
    Index
    "); Ember.TEMPLATES['about/item'] = compile("
    {{#link-to 'about'}}About{{/link-to}}
    "); bootApplication(); Ember.run(router, 'handleURL', '/about/item'); equal(normalizeUrl(Ember.$('#item a', '#qunit-fixture').attr('href')), '/about'); }); QUnit.test("The {{link-to}} helper supports currentWhen (DEPRECATED)", function() { expectDeprecation('Using currentWhen with {{link-to}} is deprecated in favor of `current-when`.'); Router.map(function(match) { this.resource("index", { path: "/" }, function() { this.route("about"); }); this.route("item"); }); Ember.TEMPLATES.index = compile("

    Home

    {{outlet}}"); Ember.TEMPLATES['index/about'] = compile("{{#link-to 'item' id='other-link' currentWhen='index'}}ITEM{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); equal(Ember.$('#other-link.active', '#qunit-fixture').length, 1, "The link is active since current-when is a parent route"); }); QUnit.test("The {{link-to}} helper supports custom, nested, current-when", function() { Router.map(function(match) { this.resource("index", { path: "/" }, function() { this.route("about"); }); this.route("item"); }); Ember.TEMPLATES.index = compile("

    Home

    {{outlet}}"); Ember.TEMPLATES['index/about'] = compile("{{#link-to 'item' id='other-link' current-when='index'}}ITEM{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); equal(Ember.$('#other-link.active', '#qunit-fixture').length, 1, "The link is active since current-when is a parent route"); }); QUnit.test("The {{link-to}} helper does not disregard current-when when it is given explicitly for a resource", function() { Router.map(function(match) { this.resource("index", { path: "/" }, function() { this.route("about"); }); this.resource("items", function() { this.route('item'); }); }); Ember.TEMPLATES.index = compile("

    Home

    {{outlet}}"); Ember.TEMPLATES['index/about'] = compile("{{#link-to 'items' id='other-link' current-when='index'}}ITEM{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); equal(Ember.$('#other-link.active', '#qunit-fixture').length, 1, "The link is active when current-when is given for explicitly for a resource"); }); QUnit.test("The {{link-to}} helper supports multiple current-when routes", function() { Router.map(function(match) { this.resource("index", { path: "/" }, function() { this.route("about"); }); this.route("item"); this.route("foo"); }); Ember.TEMPLATES.index = compile("

    Home

    {{outlet}}"); Ember.TEMPLATES['index/about'] = compile("{{#link-to 'item' id='link1' current-when='item index'}}ITEM{{/link-to}}"); Ember.TEMPLATES['item'] = compile("{{#link-to 'item' id='link2' current-when='item index'}}ITEM{{/link-to}}"); Ember.TEMPLATES['foo'] = compile("{{#link-to 'item' id='link3' current-when='item index'}}ITEM{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); equal(Ember.$('#link1.active', '#qunit-fixture').length, 1, "The link is active since current-when contains the parent route"); Ember.run(function() { router.handleURL("/item"); }); equal(Ember.$('#link2.active', '#qunit-fixture').length, 1, "The link is active since you are on the active route"); Ember.run(function() { router.handleURL("/foo"); }); equal(Ember.$('#link3.active', '#qunit-fixture').length, 0, "The link is not active since current-when does not contain the active route"); }); QUnit.test("The {{link-to}} helper defaults to bubbling", function() { Ember.TEMPLATES.about = compile("
    {{#link-to 'about.contact' id='about-contact'}}About{{/link-to}}
    {{outlet}}"); Ember.TEMPLATES['about/contact'] = compile("

    Contact

    "); Router.map(function() { this.resource("about", function() { this.route("contact"); }); }); var hidden = 0; App.AboutRoute = Ember.Route.extend({ actions: { hide: function() { hidden++; } } }); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); Ember.run(function() { Ember.$('#about-contact', '#qunit-fixture').click(); }); equal(Ember.$("#contact", "#qunit-fixture").text(), "Contact", "precond - the link worked"); equal(hidden, 1, "The link bubbles"); }); QUnit.test("The {{link-to}} helper supports bubbles=false", function() { Ember.TEMPLATES.about = compile("
    {{#link-to 'about.contact' id='about-contact' bubbles=false}}About{{/link-to}}
    {{outlet}}"); Ember.TEMPLATES['about/contact'] = compile("

    Contact

    "); Router.map(function() { this.resource("about", function() { this.route("contact"); }); }); var hidden = 0; App.AboutRoute = Ember.Route.extend({ actions: { hide: function() { hidden++; } } }); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); Ember.run(function() { Ember.$('#about-contact', '#qunit-fixture').click(); }); equal(Ember.$("#contact", "#qunit-fixture").text(), "Contact", "precond - the link worked"); equal(hidden, 0, "The link didn't bubble"); }); QUnit.test("The {{link-to}} helper moves into the named route with context", function() { Router.map(function(match) { this.route("about"); this.resource("item", { path: "/item/:id" }); }); Ember.TEMPLATES.about = compile("

    List

      {{#each person in model}}
    • {{#link-to 'item' person}}{{person.name}}{{/link-to}}
    • {{/each}}
    {{#link-to 'index' id='home-link'}}Home{{/link-to}}"); App.AboutRoute = Ember.Route.extend({ model: function() { return Ember.A([ { id: "yehuda", name: "Yehuda Katz" }, { id: "tom", name: "Tom Dale" }, { id: "erik", name: "Erik Brynroflsson" } ]); } }); App.ItemRoute = Ember.Route.extend({ serialize: function(object) { return { id: object.id }; } }); bootApplication(); Ember.run(function() { router.handleURL("/about"); }); equal(Ember.$('h3:contains(List)', '#qunit-fixture').length, 1, "The home template was rendered"); equal(normalizeUrl(Ember.$('#home-link').attr('href')), '/', "The home link points back at /"); Ember.run(function() { Ember.$('li a:contains(Yehuda)', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(Item)', '#qunit-fixture').length, 1, "The item template was rendered"); equal(Ember.$('p', '#qunit-fixture').text(), "Yehuda Katz", "The name is correct"); Ember.run(function() { Ember.$('#home-link').click(); }); Ember.run(function() { Ember.$('#about-link').click(); }); equal(normalizeUrl(Ember.$('li a:contains(Yehuda)').attr('href')), "/item/yehuda"); equal(normalizeUrl(Ember.$('li a:contains(Tom)').attr('href')), "/item/tom"); equal(normalizeUrl(Ember.$('li a:contains(Erik)').attr('href')), "/item/erik"); Ember.run(function() { Ember.$('li a:contains(Erik)', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(Item)', '#qunit-fixture').length, 1, "The item template was rendered"); equal(Ember.$('p', '#qunit-fixture').text(), "Erik Brynroflsson", "The name is correct"); }); QUnit.test("The {{link-to}} helper binds some anchor html tag common attributes", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'index' id='self-link' title='title-attr' rel='rel-attr' tabindex='-1'}}Self{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/"); }); var link = Ember.$('#self-link', '#qunit-fixture'); equal(link.attr('title'), 'title-attr', "The self-link contains title attribute"); equal(link.attr('rel'), 'rel-attr', "The self-link contains rel attribute"); equal(link.attr('tabindex'), '-1', "The self-link contains tabindex attribute"); }); QUnit.test("The {{link-to}} helper supports `target` attribute", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'index' id='self-link' target='_blank'}}Self{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/"); }); var link = Ember.$('#self-link', '#qunit-fixture'); equal(link.attr('target'), '_blank', "The self-link contains `target` attribute"); }); QUnit.test("The {{link-to}} helper does not call preventDefault if `target` attribute is provided", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'index' id='self-link' target='_blank'}}Self{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/"); }); var event = Ember.$.Event("click"); Ember.$('#self-link', '#qunit-fixture').trigger(event); equal(event.isDefaultPrevented(), false, "should not preventDefault when target attribute is specified"); }); QUnit.test("The {{link-to}} helper should preventDefault when `target = _self`", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#link-to 'index' id='self-link' target='_self'}}Self{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/"); }); var event = Ember.$.Event("click"); Ember.$('#self-link', '#qunit-fixture').trigger(event); equal(event.isDefaultPrevented(), true, "should preventDefault when target attribute is `_self`"); }); QUnit.test("The {{link-to}} helper should not transition if target is not equal to _self or empty", function() { Ember.TEMPLATES.index = compile("{{#link-to 'about' id='about-link' replace=true target='_blank'}}About{{/link-to}}"); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); Ember.run(function() { Ember.$('#about-link', '#qunit-fixture').click(); }); notEqual(container.lookup('controller:application').get('currentRouteName'), 'about', 'link-to should not transition if target is not equal to _self or empty'); }); QUnit.test("The {{link-to}} helper accepts string/numeric arguments", function() { Router.map(function() { this.route('filter', { path: '/filters/:filter' }); this.route('post', { path: '/post/:post_id' }); this.route('repo', { path: '/repo/:owner/:name' }); }); App.FilterController = Ember.Controller.extend({ filter: "unpopular", repo: Ember.Object.create({ owner: 'ember', name: 'ember.js' }), post_id: 123 }); Ember.TEMPLATES.filter = compile('

    {{filter}}

    {{#link-to "filter" "unpopular" id="link"}}Unpopular{{/link-to}}{{#link-to "filter" filter id="path-link"}}Unpopular{{/link-to}}{{#link-to "post" post_id id="post-path-link"}}Post{{/link-to}}{{#link-to "post" 123 id="post-number-link"}}Post{{/link-to}}{{#link-to "repo" repo id="repo-object-link"}}Repo{{/link-to}}'); Ember.TEMPLATES.index = compile(' '); bootApplication(); Ember.run(function() { router.handleURL("/filters/popular"); }); equal(normalizeUrl(Ember.$('#link', '#qunit-fixture').attr('href')), "/filters/unpopular"); equal(normalizeUrl(Ember.$('#path-link', '#qunit-fixture').attr('href')), "/filters/unpopular"); equal(normalizeUrl(Ember.$('#post-path-link', '#qunit-fixture').attr('href')), "/post/123"); equal(normalizeUrl(Ember.$('#post-number-link', '#qunit-fixture').attr('href')), "/post/123"); equal(normalizeUrl(Ember.$('#repo-object-link', '#qunit-fixture').attr('href')), "/repo/ember/ember.js"); }); QUnit.test("Issue 4201 - Shorthand for route.index shouldn't throw errors about context arguments", function() { expect(2); Router.map(function() { this.resource('lobby', function() { this.route('index', { path: ':lobby_id' }); this.route('list'); }); }); App.LobbyIndexRoute = Ember.Route.extend({ model: function(params) { equal(params.lobby_id, 'foobar'); return params.lobby_id; } }); Ember.TEMPLATES['lobby/index'] = compile("{{#link-to 'lobby' 'foobar' id='lobby-link'}}Lobby{{/link-to}}"); Ember.TEMPLATES.index = compile(""); Ember.TEMPLATES['lobby/list'] = compile("{{#link-to 'lobby' 'foobar' id='lobby-link'}}Lobby{{/link-to}}"); bootApplication(); Ember.run(router, 'handleURL', '/lobby/list'); Ember.run(Ember.$('#lobby-link'), 'click'); shouldBeActive('#lobby-link'); }); QUnit.test("The {{link-to}} helper unwraps controllers", function() { expect(4); Router.map(function() { this.route('filter', { path: '/filters/:filter' }); }); var indexObject = { filter: 'popular' }; App.FilterRoute = Ember.Route.extend({ model: function(params) { return indexObject; }, serialize: function(passedObject) { equal(passedObject, indexObject, "The unwrapped object is passed"); return { filter: 'popular' }; } }); App.IndexRoute = Ember.Route.extend({ model: function() { return indexObject; } }); Ember.TEMPLATES.filter = compile('

    {{model.filter}}

    '); Ember.TEMPLATES.index = compile('{{#link-to "filter" this id="link"}}Filter{{/link-to}}'); bootApplication(); Ember.run(function() { router.handleURL("/"); }); Ember.$('#link', '#qunit-fixture').trigger('click'); }); QUnit.test("The {{link-to}} helper doesn't change view context", function() { App.IndexView = Ember.View.extend({ elementId: 'index', name: 'test', isTrue: true }); Ember.TEMPLATES.index = compile("{{view.name}}-{{#link-to 'index' id='self-link'}}Link: {{view.name}}-{{#if view.isTrue}}{{view.name}}{{/if}}{{/link-to}}"); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(Ember.$('#index', '#qunit-fixture').text(), 'test-Link: test-test', "accesses correct view"); }); QUnit.test("Quoteless route param performs property lookup", function() { Ember.TEMPLATES.index = compile("{{#link-to 'index' id='string-link'}}string{{/link-to}}{{#link-to foo id='path-link'}}path{{/link-to}}{{#link-to view.foo id='view-link'}}{{view.foo}}{{/link-to}}"); function assertEquality(href) { equal(normalizeUrl(Ember.$('#string-link', '#qunit-fixture').attr('href')), '/'); equal(normalizeUrl(Ember.$('#path-link', '#qunit-fixture').attr('href')), href); equal(normalizeUrl(Ember.$('#view-link', '#qunit-fixture').attr('href')), href); } App.IndexView = Ember.View.extend({ foo: 'index', elementId: 'index-view' }); App.IndexController = Ember.Controller.extend({ foo: 'index' }); App.Router.map(function() { this.route('about'); }); bootApplication(); Ember.run(router, 'handleURL', '/'); assertEquality('/'); var controller = container.lookup('controller:index'); var view = Ember.View.views['index-view']; Ember.run(function() { controller.set('foo', 'about'); view.set('foo', 'about'); }); assertEquality('/about'); }); QUnit.test("link-to with null/undefined dynamic parameters are put in a loading state", function() { expect(19); var oldWarn = Ember.Logger.warn; var warnCalled = false; Ember.Logger.warn = function() { warnCalled = true; }; Ember.TEMPLATES.index = compile("{{#link-to destinationRoute routeContext loadingClass='i-am-loading' id='context-link'}}string{{/link-to}}{{#link-to secondRoute loadingClass='i-am-loading' id='static-link'}}string{{/link-to}}"); var thing = Ember.Object.create({ id: 123 }); App.IndexController = Ember.Controller.extend({ destinationRoute: null, routeContext: null }); App.AboutRoute = Ember.Route.extend({ activate: function() { ok(true, "About was entered"); } }); App.Router.map(function() { this.route('thing', { path: '/thing/:thing_id' }); this.route('about'); }); bootApplication(); Ember.run(router, 'handleURL', '/'); function assertLinkStatus($link, url) { if (url) { equal(normalizeUrl($link.attr('href')), url, "loaded link-to has expected href"); ok(!$link.hasClass('i-am-loading'), "loaded linkView has no loadingClass"); } else { equal(normalizeUrl($link.attr('href')), '#', "unloaded link-to has href='#'"); ok($link.hasClass('i-am-loading'), "loading linkView has loadingClass"); } } var $contextLink = Ember.$('#context-link', '#qunit-fixture'); var $staticLink = Ember.$('#static-link', '#qunit-fixture'); var controller = container.lookup('controller:index'); assertLinkStatus($contextLink); assertLinkStatus($staticLink); Ember.run(function() { warnCalled = false; $contextLink.click(); ok(warnCalled, "Logger.warn was called from clicking loading link"); }); // Set the destinationRoute (context is still null). Ember.run(controller, 'set', 'destinationRoute', 'thing'); assertLinkStatus($contextLink); // Set the routeContext to an id Ember.run(controller, 'set', 'routeContext', '456'); assertLinkStatus($contextLink, '/thing/456'); // Test that 0 isn't interpreted as falsy. Ember.run(controller, 'set', 'routeContext', 0); assertLinkStatus($contextLink, '/thing/0'); // Set the routeContext to an object Ember.run(controller, 'set', 'routeContext', thing); assertLinkStatus($contextLink, '/thing/123'); // Set the destinationRoute back to null. Ember.run(controller, 'set', 'destinationRoute', null); assertLinkStatus($contextLink); Ember.run(function() { warnCalled = false; $staticLink.click(); ok(warnCalled, "Logger.warn was called from clicking loading link"); }); Ember.run(controller, 'set', 'secondRoute', 'about'); assertLinkStatus($staticLink, '/about'); // Click the now-active link Ember.run($staticLink, 'click'); Ember.Logger.warn = oldWarn; }); QUnit.test("The {{link-to}} helper refreshes href element when one of params changes", function() { Router.map(function() { this.route('post', { path: '/posts/:post_id' }); }); var post = Ember.Object.create({ id: '1' }); var secondPost = Ember.Object.create({ id: '2' }); Ember.TEMPLATES.index = compile('{{#link-to "post" post id="post"}}post{{/link-to}}'); App.IndexController = Ember.Controller.extend(); var indexController = container.lookup('controller:index'); Ember.run(function() { indexController.set('post', post); }); bootApplication(); Ember.run(function() { router.handleURL("/"); }); equal(normalizeUrl(Ember.$('#post', '#qunit-fixture').attr('href')), '/posts/1', 'precond - Link has rendered href attr properly'); Ember.run(function() { indexController.set('post', secondPost); }); equal(Ember.$('#post', '#qunit-fixture').attr('href'), '/posts/2', 'href attr was updated after one of the params had been changed'); Ember.run(function() { indexController.set('post', null); }); equal(Ember.$('#post', '#qunit-fixture').attr('href'), '#', 'href attr becomes # when one of the arguments in nullified'); }); QUnit.test("The {{link-to}} helper's bound parameter functionality works as expected in conjunction with an ObjectProxy/Controller", function() { expectDeprecation(object_controller.objectControllerDeprecation); Router.map(function() { this.route('post', { path: '/posts/:post_id' }); }); var post = Ember.Object.create({ id: '1' }); var secondPost = Ember.Object.create({ id: '2' }); Ember.TEMPLATES = { index: compile(' '), post: compile('{{#link-to "post" this id="self-link"}}selflink{{/link-to}}') }; App.PostController = Ember.ObjectController.extend(); var postController = container.lookup('controller:post'); bootApplication(); Ember.run(router, 'transitionTo', 'post', post); var $link = Ember.$('#self-link', '#qunit-fixture'); equal(normalizeUrl($link.attr('href')), '/posts/1', 'self link renders post 1'); Ember.run(postController, 'set', 'model', secondPost); equal(normalizeUrl($link.attr('href')), '/posts/2', 'self link updated to post 2'); }); QUnit.test("{{linkTo}} is aliased", function() { Ember.TEMPLATES.index = compile("

    Home

    {{#linkTo 'about' id='about-link' replace=true}}About{{/linkTo}}"); Router.map(function() { this.route("about"); }); expectDeprecation(function() { bootApplication(); }, "The 'linkTo' view helper is deprecated in favor of 'link-to'"); Ember.run(function() { router.handleURL("/"); }); Ember.run(function() { Ember.$('#about-link', '#qunit-fixture').click(); }); equal(container.lookup('controller:application').get('currentRouteName'), 'about', 'linkTo worked properly'); }); QUnit.test("The {{link-to}} helper is active when a resource is active", function() { Router.map(function() { this.resource("about", function() { this.route("item"); }); }); Ember.TEMPLATES.about = compile("
    {{#link-to 'about' id='about-link'}}About{{/link-to}} {{#link-to 'about.item' id='item-link'}}Item{{/link-to}} {{outlet}}
    "); Ember.TEMPLATES['about/item'] = compile(" "); Ember.TEMPLATES['about/index'] = compile(" "); bootApplication(); Ember.run(router, 'handleURL', '/about'); equal(Ember.$('#about-link.active', '#qunit-fixture').length, 1, "The about resource link is active"); equal(Ember.$('#item-link.active', '#qunit-fixture').length, 0, "The item route link is inactive"); Ember.run(router, 'handleURL', '/about/item'); equal(Ember.$('#about-link.active', '#qunit-fixture').length, 1, "The about resource link is active"); equal(Ember.$('#item-link.active', '#qunit-fixture').length, 1, "The item route link is active"); }); QUnit.test("The {{link-to}} helper works in an #each'd array of string route names", function() { Router.map(function() { this.route('foo'); this.route('bar'); this.route('rar'); }); App.IndexController = Ember.Controller.extend({ routeNames: Ember.A(['foo', 'bar', 'rar']), route1: 'bar', route2: 'foo' }); Ember.TEMPLATES = { index: compile('{{#each routeName in routeNames}}{{#link-to routeName}}{{routeName}}{{/link-to}}{{/each}}{{#each routeNames}}{{#link-to this}}{{this}}{{/link-to}}{{/each}}{{#link-to route1}}a{{/link-to}}{{#link-to route2}}b{{/link-to}}') }; expectDeprecation(function() { bootApplication(); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead.'); function linksEqual($links, expected) { equal($links.length, expected.length, "Has correct number of links"); var idx; for (idx = 0; idx < $links.length; idx++) { var href = Ember.$($links[idx]).attr('href'); // Old IE includes the whole hostname as well equal(href.slice(-expected[idx].length), expected[idx], "Expected link to be '"+expected[idx]+"', but was '"+href+"'"); } } linksEqual(Ember.$('a', '#qunit-fixture'), ["/foo", "/bar", "/rar", "/foo", "/bar", "/rar", "/bar", "/foo"]); var indexController = container.lookup('controller:index'); Ember.run(indexController, 'set', 'route1', 'rar'); linksEqual(Ember.$('a', '#qunit-fixture'), ["/foo", "/bar", "/rar", "/foo", "/bar", "/rar", "/rar", "/foo"]); Ember.run(indexController.routeNames, 'shiftObject'); linksEqual(Ember.$('a', '#qunit-fixture'), ["/bar", "/rar", "/bar", "/rar", "/rar", "/foo"]); }); QUnit.test("The non-block form {{link-to}} helper moves into the named route", function() { expect(3); Router.map(function(match) { this.route("contact"); }); Ember.TEMPLATES.index = compile("

    Home

    {{link-to 'Contact us' 'contact' id='contact-link'}}{{#link-to 'index' id='self-link'}}Self{{/link-to}}"); Ember.TEMPLATES.contact = compile("

    Contact

    {{link-to 'Home' 'index' id='home-link'}}{{link-to 'Self' 'contact' id='self-link'}}"); bootApplication(); Ember.run(function() { Ember.$('#contact-link', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(Contact)', '#qunit-fixture').length, 1, "The contact template was rendered"); equal(Ember.$('#self-link.active', '#qunit-fixture').length, 1, "The self-link was rendered with active class"); equal(Ember.$('#home-link:not(.active)', '#qunit-fixture').length, 1, "The other link was rendered without active class"); }); QUnit.test("The non-block form {{link-to}} helper updates the link text when it is a binding", function() { expect(8); Router.map(function(match) { this.route("contact"); }); App.IndexController = Ember.Controller.extend({ contactName: 'Jane' }); Ember.TEMPLATES.index = compile("

    Home

    {{link-to contactName 'contact' id='contact-link'}}{{#link-to 'index' id='self-link'}}Self{{/link-to}}"); Ember.TEMPLATES.contact = compile("

    Contact

    {{link-to 'Home' 'index' id='home-link'}}{{link-to 'Self' 'contact' id='self-link'}}"); bootApplication(); Ember.run(function() { router.handleURL("/"); }); var controller = container.lookup('controller:index'); equal(Ember.$('#contact-link:contains(Jane)', '#qunit-fixture').length, 1, "The link title is correctly resolved"); Ember.run(function() { controller.set('contactName', 'Joe'); }); equal(Ember.$('#contact-link:contains(Joe)', '#qunit-fixture').length, 1, "The link title is correctly updated when the bound property changes"); Ember.run(function() { controller.set('contactName', 'Robert'); }); equal(Ember.$('#contact-link:contains(Robert)', '#qunit-fixture').length, 1, "The link title is correctly updated when the bound property changes a second time"); Ember.run(function() { Ember.$('#contact-link', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(Contact)', '#qunit-fixture').length, 1, "The contact template was rendered"); equal(Ember.$('#self-link.active', '#qunit-fixture').length, 1, "The self-link was rendered with active class"); equal(Ember.$('#home-link:not(.active)', '#qunit-fixture').length, 1, "The other link was rendered without active class"); Ember.run(function() { Ember.$('#home-link', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(Home)', '#qunit-fixture').length, 1, "The index template was rendered"); equal(Ember.$('#contact-link:contains(Robert)', '#qunit-fixture').length, 1, "The link title is correctly updated when the route changes"); }); QUnit.test("The non-block form {{link-to}} helper moves into the named route with context", function() { expect(5); Router.map(function(match) { this.route("item", { path: "/item/:id" }); }); App.IndexRoute = Ember.Route.extend({ model: function() { return Ember.A([ { id: "yehuda", name: "Yehuda Katz" }, { id: "tom", name: "Tom Dale" }, { id: "erik", name: "Erik Brynroflsson" } ]); } }); App.ItemRoute = Ember.Route.extend({ serialize: function(object) { return { id: object.id }; } }); Ember.TEMPLATES.index = compile("

    Home

      {{#each person in controller}}
    • {{link-to person.name 'item' person}}
    • {{/each}}
    "); Ember.TEMPLATES.item = compile("

    Item

    {{model.name}}

    {{#link-to 'index' id='home-link'}}Home{{/link-to}}"); bootApplication(); Ember.run(function() { Ember.$('li a:contains(Yehuda)', '#qunit-fixture').click(); }); equal(Ember.$('h3:contains(Item)', '#qunit-fixture').length, 1, "The item template was rendered"); equal(Ember.$('p', '#qunit-fixture').text(), "Yehuda Katz", "The name is correct"); Ember.run(function() { Ember.$('#home-link').click(); }); equal(normalizeUrl(Ember.$('li a:contains(Yehuda)').attr('href')), "/item/yehuda"); equal(normalizeUrl(Ember.$('li a:contains(Tom)').attr('href')), "/item/tom"); equal(normalizeUrl(Ember.$('li a:contains(Erik)').attr('href')), "/item/erik"); }); QUnit.test("The non-block form {{link-to}} performs property lookup", function() { Ember.TEMPLATES.index = compile("{{link-to 'string' 'index' id='string-link'}}{{link-to path foo id='path-link'}}{{link-to view.foo view.foo id='view-link'}}"); function assertEquality(href) { equal(normalizeUrl(Ember.$('#string-link', '#qunit-fixture').attr('href')), '/'); equal(normalizeUrl(Ember.$('#path-link', '#qunit-fixture').attr('href')), href); equal(normalizeUrl(Ember.$('#view-link', '#qunit-fixture').attr('href')), href); } App.IndexView = Ember.View.extend({ foo: 'index', elementId: 'index-view' }); App.IndexController = Ember.Controller.extend({ foo: 'index' }); App.Router.map(function() { this.route('about'); }); bootApplication(); Ember.run(router, 'handleURL', '/'); assertEquality('/'); var controller = container.lookup('controller:index'); var view = Ember.View.views['index-view']; Ember.run(function() { controller.set('foo', 'about'); view.set('foo', 'about'); }); assertEquality('/about'); }); QUnit.test("The non-block form {{link-to}} protects against XSS", function() { Ember.TEMPLATES.application = compile("{{link-to display 'index' id='link'}}"); App.ApplicationController = Ember.Controller.extend({ display: 'blahzorz' }); bootApplication(); Ember.run(router, 'handleURL', '/'); var controller = container.lookup('controller:application'); equal(Ember.$('#link', '#qunit-fixture').text(), 'blahzorz'); Ember.run(function() { controller.set('display', 'BLAMMO'); }); equal(Ember.$('#link', '#qunit-fixture').text(), 'BLAMMO'); equal(Ember.$('b', '#qunit-fixture').length, 0); }); QUnit.test("the {{link-to}} helper calls preventDefault", function() { Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(router, 'handleURL', '/'); var event = Ember.$.Event("click"); Ember.$('#about-link', '#qunit-fixture').trigger(event); equal(event.isDefaultPrevented(), true, "should preventDefault"); }); QUnit.test("the {{link-to}} helper does not call preventDefault if `preventDefault=false` is passed as an option", function() { Ember.TEMPLATES.index = compile("{{#link-to 'about' id='about-link' preventDefault=false}}About{{/link-to}}"); Router.map(function() { this.route("about"); }); bootApplication(); Ember.run(router, 'handleURL', '/'); var event = Ember.$.Event("click"); Ember.$('#about-link', '#qunit-fixture').trigger(event); equal(event.isDefaultPrevented(), false, "should not preventDefault"); }); QUnit.test("the {{link-to}} helper does not throw an error if its route has exited", function() { expect(0); Ember.TEMPLATES.application = compile("{{#link-to 'index' id='home-link'}}Home{{/link-to}}{{#link-to 'post' defaultPost id='default-post-link'}}Default Post{{/link-to}}{{#if currentPost}}{{#link-to 'post' id='post-link'}}Post{{/link-to}}{{/if}}"); App.ApplicationController = Ember.Controller.extend({ needs: ['post'], currentPost: Ember.computed.alias('controllers.post.model') }); App.PostController = Ember.Controller.extend({ model: { id: 1 } }); Router.map(function() { this.route("post", { path: 'post/:post_id' }); }); bootApplication(); Ember.run(router, 'handleURL', '/'); Ember.run(function() { Ember.$('#default-post-link', '#qunit-fixture').click(); }); Ember.run(function() { Ember.$('#home-link', '#qunit-fixture').click(); }); }); QUnit.test("{{link-to}} active property respects changing parent route context", function() { Ember.TEMPLATES.application = compile( "{{link-to 'OMG' 'things' 'omg' id='omg-link'}} " + "{{link-to 'LOL' 'things' 'lol' id='lol-link'}} "); Router.map(function() { this.resource('things', { path: '/things/:name' }, function() { this.route('other'); }); }); bootApplication(); Ember.run(router, 'handleURL', '/things/omg'); shouldBeActive('#omg-link'); shouldNotBeActive('#lol-link'); Ember.run(router, 'handleURL', '/things/omg/other'); shouldBeActive('#omg-link'); shouldNotBeActive('#lol-link'); }); QUnit.test("{{link-to}} populates href with default query param values even without query-params object", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: '123' }); Ember.TEMPLATES.index = compile("{{#link-to 'index' id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/", "link has right href"); }); QUnit.test("{{link-to}} populates href with default query param values with empty query-params object", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: '123' }); Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params) id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/", "link has right href"); }); QUnit.test("{{link-to}} populates href with supplied query param values", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: '123' }); Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params foo='456') id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/?foo=456", "link has right href"); }); QUnit.test("{{link-to}} populates href with partially supplied query param values", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo', 'bar'], foo: '123', bar: 'yes' }); Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params foo='456') id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/?foo=456", "link has right href"); }); QUnit.test("{{link-to}} populates href with partially supplied query param values, but omits if value is default value", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo', 'bar'], foo: '123', bar: 'yes' }); Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params foo='123') id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/", "link has right href"); }); QUnit.test("{{link-to}} populates href with fully supplied query param values", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo', 'bar'], foo: '123', bar: 'yes' }); Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params foo='456' bar='NAW') id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/?bar=NAW&foo=456", "link has right href"); }); QUnit.module("The {{link-to}} helper: invoking with query params", { setup: function() { Ember.run(function() { sharedSetup(); App.IndexController = Ember.Controller.extend({ queryParams: ['foo', 'bar', 'abool'], foo: '123', bar: 'abc', boundThing: "OMG", abool: true }); App.AboutController = Ember.Controller.extend({ queryParams: ['baz', 'bat'], baz: 'alex', bat: 'borf' }); registry.unregister('router:main'); registry.register('router:main', Router); }); }, teardown: sharedTeardown }); QUnit.test("doesn't update controller QP properties on current route when invoked", function() { Ember.TEMPLATES.index = compile("{{#link-to 'index' id='the-link'}}Index{{/link-to}}"); bootApplication(); Ember.run(Ember.$('#the-link'), 'click'); var indexController = container.lookup('controller:index'); deepEqual(indexController.getProperties('foo', 'bar'), { foo: '123', bar: 'abc' }, "controller QP properties not"); }); QUnit.test("doesn't update controller QP properties on current route when invoked (empty query-params obj)", function() { Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params) id='the-link'}}Index{{/link-to}}"); bootApplication(); Ember.run(Ember.$('#the-link'), 'click'); var indexController = container.lookup('controller:index'); deepEqual(indexController.getProperties('foo', 'bar'), { foo: '123', bar: 'abc' }, "controller QP properties not"); }); QUnit.test("link-to with no params throws", function() { Ember.TEMPLATES.index = compile("{{#link-to id='the-link'}}Index{{/link-to}}"); expectAssertion(function() { bootApplication(); }, /one or more/); }); QUnit.test("doesn't update controller QP properties on current route when invoked (empty query-params obj, inferred route)", function() { Ember.TEMPLATES.index = compile("{{#link-to (query-params) id='the-link'}}Index{{/link-to}}"); bootApplication(); Ember.run(Ember.$('#the-link'), 'click'); var indexController = container.lookup('controller:index'); deepEqual(indexController.getProperties('foo', 'bar'), { foo: '123', bar: 'abc' }, "controller QP properties not"); }); QUnit.test("updates controller QP properties on current route when invoked", function() { Ember.TEMPLATES.index = compile("{{#link-to 'index' (query-params foo='456') id='the-link'}}Index{{/link-to}}"); bootApplication(); Ember.run(Ember.$('#the-link'), 'click'); var indexController = container.lookup('controller:index'); deepEqual(indexController.getProperties('foo', 'bar'), { foo: '456', bar: 'abc' }, "controller QP properties updated"); }); QUnit.test("updates controller QP properties on current route when invoked (inferred route)", function() { Ember.TEMPLATES.index = compile("{{#link-to (query-params foo='456') id='the-link'}}Index{{/link-to}}"); bootApplication(); Ember.run(Ember.$('#the-link'), 'click'); var indexController = container.lookup('controller:index'); deepEqual(indexController.getProperties('foo', 'bar'), { foo: '456', bar: 'abc' }, "controller QP properties updated"); }); QUnit.test("updates controller QP properties on other route after transitioning to that route", function() { Router.map(function() { this.route('about'); }); Ember.TEMPLATES.index = compile("{{#link-to 'about' (query-params baz='lol') id='the-link'}}About{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), '/about?baz=lol'); Ember.run(Ember.$('#the-link'), 'click'); var aboutController = container.lookup('controller:about'); deepEqual(aboutController.getProperties('baz', 'bat'), { baz: 'lol', bat: 'borf' }, "about controller QP properties updated"); equal(container.lookup('controller:application').get('currentPath'), "about"); }); QUnit.test("supplied QP properties can be bound", function() { var indexController = container.lookup('controller:index'); Ember.TEMPLATES.index = compile("{{#link-to (query-params foo=boundThing) id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), '/?foo=OMG'); Ember.run(indexController, 'set', 'boundThing', "ASL"); equal(Ember.$('#the-link').attr('href'), '/?foo=ASL'); }); QUnit.test("supplied QP properties can be bound (booleans)", function() { var indexController = container.lookup('controller:index'); Ember.TEMPLATES.index = compile("{{#link-to (query-params abool=boundThing) id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), '/?abool=OMG'); Ember.run(indexController, 'set', 'boundThing', false); equal(Ember.$('#the-link').attr('href'), '/?abool=false'); Ember.run(Ember.$('#the-link'), 'click'); deepEqual(indexController.getProperties('foo', 'bar', 'abool'), { foo: '123', bar: 'abc', abool: false }); }); QUnit.test("href updates when unsupplied controller QP props change", function() { var indexController = container.lookup('controller:index'); Ember.TEMPLATES.index = compile("{{#link-to (query-params foo='lol') id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), '/?foo=lol'); Ember.run(indexController, 'set', 'bar', 'BORF'); equal(Ember.$('#the-link').attr('href'), '/?bar=BORF&foo=lol'); Ember.run(indexController, 'set', 'foo', 'YEAH'); equal(Ember.$('#the-link').attr('href'), '/?bar=BORF&foo=lol'); }); QUnit.test("The {{link-to}} applies activeClass when query params are not changed", function() { Ember.TEMPLATES.index = compile( "{{#link-to (query-params foo='cat') id='cat-link'}}Index{{/link-to}} " + "{{#link-to (query-params foo='dog') id='dog-link'}}Index{{/link-to}} " + "{{#link-to 'index' id='change-nothing'}}Index{{/link-to}}" ); Ember.TEMPLATES.search = compile( "{{#link-to (query-params search='same') id='same-search'}}Index{{/link-to}} " + "{{#link-to (query-params search='change') id='change-search'}}Index{{/link-to}} " + "{{#link-to (query-params search='same' archive=true) id='same-search-add-archive'}}Index{{/link-to}} " + "{{#link-to (query-params archive=true) id='only-add-archive'}}Index{{/link-to}} " + "{{#link-to (query-params search='same' archive=true) id='both-same'}}Index{{/link-to}} " + "{{#link-to (query-params search='different' archive=true) id='change-one'}}Index{{/link-to}} " + "{{#link-to (query-params search='different' archive=false) id='remove-one'}}Index{{/link-to}} " + "{{outlet}}" ); Ember.TEMPLATES['search/results'] = compile( "{{#link-to (query-params sort='title') id='same-sort-child-only'}}Index{{/link-to}} " + "{{#link-to (query-params search='same') id='same-search-parent-only'}}Index{{/link-to}} " + "{{#link-to (query-params search='change') id='change-search-parent-only'}}Index{{/link-to}} " + "{{#link-to (query-params search='same' sort='title') id='same-search-same-sort-child-and-parent'}}Index{{/link-to}} " + "{{#link-to (query-params search='same' sort='author') id='same-search-different-sort-child-and-parent'}}Index{{/link-to}} " + "{{#link-to (query-params search='change' sort='title') id='change-search-same-sort-child-and-parent'}}Index{{/link-to}} " + "{{#link-to (query-params foo='dog') id='dog-link'}}Index{{/link-to}} " ); Router.map(function() { this.resource("search", function() { this.route("results"); }); }); App.SearchController = Ember.Controller.extend({ queryParams: ['search', 'archive'], search: '', archive: false }); App.SearchResultsController = Ember.Controller.extend({ queryParams: ['sort', 'showDetails'], sort: 'title', showDetails: true }); bootApplication(); //Basic tests shouldNotBeActive('#cat-link'); shouldNotBeActive('#dog-link'); Ember.run(router, 'handleURL', '/?foo=cat'); shouldBeActive('#cat-link'); shouldNotBeActive('#dog-link'); Ember.run(router, 'handleURL', '/?foo=dog'); shouldBeActive('#dog-link'); shouldNotBeActive('#cat-link'); shouldBeActive('#change-nothing'); //Multiple params Ember.run(function() { router.handleURL("/search?search=same"); }); shouldBeActive('#same-search'); shouldNotBeActive('#change-search'); shouldNotBeActive('#same-search-add-archive'); shouldNotBeActive('#only-add-archive'); shouldNotBeActive('#remove-one'); Ember.run(function() { router.handleURL("/search?search=same&archive=true"); }); shouldBeActive('#both-same'); shouldNotBeActive('#change-one'); //Nested Controllers Ember.run(function() { // Note: this is kind of a strange case; sort's default value is 'title', // so this URL shouldn't have been generated in the first place, but // we should also be able to gracefully handle these cases. router.handleURL("/search/results?search=same&sort=title&showDetails=true"); }); //shouldBeActive('#same-sort-child-only'); shouldBeActive('#same-search-parent-only'); shouldNotBeActive('#change-search-parent-only'); shouldBeActive('#same-search-same-sort-child-and-parent'); shouldNotBeActive('#same-search-different-sort-child-and-parent'); shouldNotBeActive('#change-search-same-sort-child-and-parent'); }); QUnit.test("The {{link-to}} applies active class when query-param is number", function() { Ember.TEMPLATES.index = compile( "{{#link-to (query-params page=pageNumber) id='page-link'}}Index{{/link-to}} "); App.IndexController = Ember.Controller.extend({ queryParams: ['page'], page: 1, pageNumber: 5 }); bootApplication(); shouldNotBeActive('#page-link'); Ember.run(router, 'handleURL', '/?page=5'); shouldBeActive('#page-link'); }); QUnit.test("The {{link-to}} applies active class when query-param is array", function() { Ember.TEMPLATES.index = compile( "{{#link-to (query-params pages=pagesArray) id='array-link'}}Index{{/link-to}} " + "{{#link-to (query-params pages=biggerArray) id='bigger-link'}}Index{{/link-to}} " + "{{#link-to (query-params pages=emptyArray) id='empty-link'}}Index{{/link-to}} " ); App.IndexController = Ember.Controller.extend({ queryParams: ['pages'], pages: [], pagesArray: [1,2], biggerArray: [1,2,3], emptyArray: [] }); bootApplication(); shouldNotBeActive('#array-link'); Ember.run(router, 'handleURL', '/?pages=%5B1%2C2%5D'); shouldBeActive('#array-link'); shouldNotBeActive('#bigger-link'); shouldNotBeActive('#empty-link'); Ember.run(router, 'handleURL', '/?pages=%5B2%2C1%5D'); shouldNotBeActive('#array-link'); shouldNotBeActive('#bigger-link'); shouldNotBeActive('#empty-link'); Ember.run(router, 'handleURL', '/?pages=%5B1%2C2%2C3%5D'); shouldBeActive('#bigger-link'); shouldNotBeActive('#array-link'); shouldNotBeActive('#empty-link'); }); QUnit.test("The {{link-to}} helper applies active class to parent route", function() { App.Router.map(function() { this.resource('parent', function() { this.route('child'); }); }); Ember.TEMPLATES.application = compile( "{{#link-to 'parent' id='parent-link'}}Parent{{/link-to}} " + "{{#link-to 'parent.child' id='parent-child-link'}}Child{{/link-to}} " + "{{#link-to 'parent' (query-params foo=cat) id='parent-link-qp'}}Parent{{/link-to}} " + "{{outlet}}" ); App.ParentChildController = Ember.Controller.extend({ queryParams: ['foo'], foo: 'bar' }); bootApplication(); shouldNotBeActive('#parent-link'); shouldNotBeActive('#parent-child-link'); shouldNotBeActive('#parent-link-qp'); Ember.run(router, 'handleURL', '/parent/child?foo=dog'); shouldBeActive('#parent-link'); shouldNotBeActive('#parent-link-qp'); }); QUnit.test("The {{link-to}} helper disregards query-params in activeness computation when current-when specified", function() { App.Router.map(function() { this.route('parent'); }); Ember.TEMPLATES.application = compile( "{{#link-to 'parent' (query-params page=1) current-when='parent' id='app-link'}}Parent{{/link-to}} {{outlet}}"); Ember.TEMPLATES.parent = compile( "{{#link-to 'parent' (query-params page=1) current-when='parent' id='parent-link'}}Parent{{/link-to}} {{outlet}}"); App.ParentController = Ember.Controller.extend({ queryParams: ['page'], page: 1 }); bootApplication(); equal(Ember.$('#app-link').attr('href'), '/parent'); shouldNotBeActive('#app-link'); Ember.run(router, 'handleURL', '/parent?page=2'); equal(Ember.$('#app-link').attr('href'), '/parent'); shouldBeActive('#app-link'); equal(Ember.$('#parent-link').attr('href'), '/parent'); shouldBeActive('#parent-link'); var parentController = container.lookup('controller:parent'); equal(parentController.get('page'), 2); Ember.run(parentController, 'set', 'page', 3); equal(router.get('location.path'), '/parent?page=3'); shouldBeActive('#app-link'); shouldBeActive('#parent-link'); Ember.$('#app-link').click(); equal(router.get('location.path'), '/parent'); }); function basicEagerURLUpdateTest(setTagName) { expect(6); if (setTagName) { Ember.TEMPLATES.application = compile("{{outlet}}{{link-to 'Index' 'index' id='index-link'}}{{link-to 'About' 'about' id='about-link' tagName='span'}}"); } bootApplication(); equal(updateCount, 0); Ember.run(Ember.$('#about-link'), 'click'); // URL should be eagerly updated now equal(updateCount, 1); equal(router.get('location.path'), '/about'); // Resolve the promise. Ember.run(aboutDefer, 'resolve'); equal(router.get('location.path'), '/about'); // Shouldn't have called update url again. equal(updateCount, 1); equal(router.get('location.path'), '/about'); } var aboutDefer; if (!Ember.FEATURES.isEnabled('ember-routing-transitioning-classes')) { QUnit.module("The {{link-to}} helper: eager URL updating", { setup: function() { Ember.run(function() { sharedSetup(); registry.unregister('router:main'); registry.register('router:main', Router); Router.map(function() { this.route('about'); }); App.AboutRoute = Ember.Route.extend({ model: function() { aboutDefer = Ember.RSVP.defer(); return aboutDefer.promise; } }); Ember.TEMPLATES.application = compile("{{outlet}}{{link-to 'Index' 'index' id='index-link'}}{{link-to 'About' 'about' id='about-link'}}"); }); }, teardown: function() { sharedTeardown(); aboutDefer = null; } }); QUnit.test("invoking a link-to with a slow promise eager updates url", function() { basicEagerURLUpdateTest(false); }); QUnit.test("when link-to eagerly updates url, the path it provides does NOT include the rootURL", function() { expect(2); // HistoryLocation is the only Location class that will cause rootURL to be // prepended to link-to href's right now var HistoryTestLocation = Ember.HistoryLocation.extend({ location: { hash: '', hostname: 'emberjs.com', href: 'http://emberjs.com/app/', pathname: '/app/', protocol: 'http:', port: '', search: '' }, // Don't actually touch the URL replaceState: function(path) {}, pushState: function(path) {}, setURL: function(path) { set(this, 'path', path); }, replaceURL: function(path) { set(this, 'path', path); } }); registry.register('location:historyTest', HistoryTestLocation); Router.reopen({ location: 'historyTest', rootURL: '/app/' }); bootApplication(); // href should have rootURL prepended equal(Ember.$('#about-link').attr('href'), '/app/about'); Ember.run(Ember.$('#about-link'), 'click'); // Actual path provided to Location class should NOT have rootURL equal(router.get('location.path'), '/about'); }); QUnit.test("non `a` tags also eagerly update URL", function() { basicEagerURLUpdateTest(true); }); QUnit.test("invoking a link-to with a promise that rejects on the run loop doesn't update url", function() { App.AboutRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject(); } }); bootApplication(); Ember.run(Ember.$('#about-link'), 'click'); // Shouldn't have called update url. equal(updateCount, 0); equal(router.get('location.path'), '', 'url was not updated'); }); QUnit.test("invoking a link-to whose transition gets aborted in will transition doesn't update the url", function() { App.IndexRoute = Ember.Route.extend({ actions: { willTransition: function(transition) { ok(true, "aborting transition"); transition.abort(); } } }); bootApplication(); Ember.run(Ember.$('#about-link'), 'click'); // Shouldn't have called update url. equal(updateCount, 0); equal(router.get('location.path'), '', 'url was not updated'); }); } QUnit.module("The {{link-to}} helper: .transitioning-in .transitioning-out CSS classes", { setup: function() { Ember.run(function() { sharedSetup(); registry.unregister('router:main'); registry.register('router:main', Router); Router.map(function() { this.route('about'); this.route('other'); }); App.AboutRoute = Ember.Route.extend({ model: function() { aboutDefer = Ember.RSVP.defer(); return aboutDefer.promise; } }); Ember.TEMPLATES.application = compile("{{outlet}}{{link-to 'Index' 'index' id='index-link'}}{{link-to 'About' 'about' id='about-link'}}{{link-to 'Other' 'other' id='other-link'}}"); }); }, teardown: function() { sharedTeardown(); aboutDefer = null; } }); QUnit.test("while a transition is underway", function() { expect(18); bootApplication(); function assertHasClass(className) { var i = 1; while (i < arguments.length) { var $a = arguments[i]; var shouldHaveClass = arguments[i+1]; equal($a.hasClass(className), shouldHaveClass, $a.attr('id') + " should " + (shouldHaveClass ? '' : "not ") + "have class " + className); i +=2; } } var $index = Ember.$('#index-link'); var $about = Ember.$('#about-link'); var $other = Ember.$('#other-link'); Ember.run($about, 'click'); assertHasClass('active', $index, true, $about, false, $other, false); assertHasClass('ember-transitioning-in', $index, false, $about, true, $other, false); assertHasClass('ember-transitioning-out', $index, true, $about, false, $other, false); Ember.run(aboutDefer, 'resolve'); assertHasClass('active', $index, false, $about, true, $other, false); assertHasClass('ember-transitioning-in', $index, false, $about, false, $other, false); assertHasClass('ember-transitioning-out', $index, false, $about, false, $other, false); }); }); enifed('ember/tests/helpers/link_to_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests/helpers'); test('ember/tests/helpers/link_to_test.js should pass jscs', function() { ok(true, 'ember/tests/helpers/link_to_test.js should pass jscs.'); }); }); enifed('ember/tests/helpers/link_to_test.jshint', function () { 'use strict'; module('JSHint - ember/tests/helpers'); test('ember/tests/helpers/link_to_test.js should pass jshint', function() { ok(true, 'ember/tests/helpers/link_to_test.js should pass jshint.'); }); }); enifed('ember/tests/homepage_example_test', ['ember', 'ember-htmlbars/compat'], function (__dep0__, EmberHandlebars) { 'use strict'; var compile = EmberHandlebars['default'].compile; var App, $fixture; function setupExample() { // setup templates Ember.TEMPLATES.application = compile("{{outlet}}"); Ember.TEMPLATES.index = compile("

    People

      {{#each person in model}}
    • Hello, {{person.fullName}}!
    • {{/each}}
    "); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: Ember.computed('firstName', 'lastName', function() { return this.get('firstName') + " " + this.get('lastName'); }) }); App.IndexRoute = Ember.Route.extend({ model: function() { var people = Ember.A([ App.Person.create({ firstName: "Tom", lastName: "Dale" }), App.Person.create({ firstName: "Yehuda", lastName: "Katz" }) ]); return people; } }); } QUnit.module("Homepage Example", { setup: function() { Ember.run(function() { App = Ember.Application.create({ name: "App", rootElement: '#qunit-fixture' }); App.deferReadiness(); App.Router.reopen({ location: 'none' }); App.LoadingRoute = Ember.Route.extend(); }); $fixture = Ember.$('#qunit-fixture'); setupExample(); }, teardown: function() { Ember.run(function() { App.destroy(); }); App = null; Ember.TEMPLATES = {}; } }); QUnit.test("The example renders correctly", function() { Ember.run(App, 'advanceReadiness'); equal($fixture.find('h1:contains(People)').length, 1); equal($fixture.find('li').length, 2); equal($fixture.find('li:nth-of-type(1)').text(), 'Hello, Tom Dale!'); equal($fixture.find('li:nth-of-type(2)').text(), 'Hello, Yehuda Katz!'); }); }); enifed('ember/tests/homepage_example_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests'); test('ember/tests/homepage_example_test.js should pass jscs', function() { ok(true, 'ember/tests/homepage_example_test.js should pass jscs.'); }); }); enifed('ember/tests/homepage_example_test.jshint', function () { 'use strict'; module('JSHint - ember/tests'); test('ember/tests/homepage_example_test.js should pass jshint', function() { ok(true, 'ember/tests/homepage_example_test.js should pass jshint.'); }); }); enifed('ember/tests/routing/basic_test', ['ember', 'ember-metal/enumerable_utils', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-views/system/action_manager', 'ember-htmlbars/compat'], function (__dep0__, enumerable_utils, property_get, property_set, ActionManager, EmberHandlebars) { 'use strict'; var compile = EmberHandlebars['default'].compile; var Router, App, router, registry, container, originalLoggerError; function bootApplication() { router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); } function handleURL(path) { return Ember.run(function() { return router.handleURL(path).then(function(value) { ok(true, 'url: `' + path + '` was handled'); return value; }, function(reason) { ok(false, 'failed to visit:`' + path + '` reason: `' + QUnit.jsDump.parse(reason)); throw reason; }); }); } function handleURLAborts(path) { Ember.run(function() { router.handleURL(path).then(function(value) { ok(false, 'url: `' + path + '` was NOT to be handled'); }, function(reason) { ok(reason && reason.message === "TransitionAborted", 'url: `' + path + '` was to be aborted'); }); }); } function handleURLRejectsWith(path, expectedReason) { Ember.run(function() { router.handleURL(path).then(function(value) { ok(false, 'expected handleURLing: `' + path + '` to fail'); }, function(reason) { equal(expectedReason, reason); }); }); } QUnit.module("Basic Routing", { setup: function() { Ember.run(function() { App = Ember.Application.create({ name: "App", rootElement: '#qunit-fixture' }); App.deferReadiness(); App.Router.reopen({ location: 'none' }); Router = App.Router; App.LoadingRoute = Ember.Route.extend({ }); registry = App.registry; container = App.__container__; Ember.TEMPLATES.application = compile("{{outlet}}"); Ember.TEMPLATES.home = compile("

    Hours

    "); Ember.TEMPLATES.homepage = compile("

    Megatroll

    {{model.home}}

    "); Ember.TEMPLATES.camelot = compile('

    Is a silly place

    '); originalLoggerError = Ember.Logger.error; }); }, teardown: function() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; Ember.Logger.error = originalLoggerError; }); } }); QUnit.test("warn on URLs not included in the route set", function () { Router.map(function() { this.route("home", { path: "/" }); }); bootApplication(); expectAssertion(function() { Ember.run(function() { router.handleURL("/what-is-this-i-dont-even"); }); }, "The URL '/what-is-this-i-dont-even' did not match any routes in your application"); }); QUnit.test("The Homepage", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ }); var currentPath; App.ApplicationController = Ember.Controller.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = property_get.get(this, 'currentPath'); }) }); bootApplication(); equal(currentPath, 'home'); equal(Ember.$('h3:contains(Hours)', '#qunit-fixture').length, 1, "The home template was rendered"); }); QUnit.test("The Home page and the Camelot page with multiple Router.map calls", function() { Router.map(function() { this.route("home", { path: "/" }); }); Router.map(function() { this.route("camelot", { path: "/camelot" }); }); App.HomeRoute = Ember.Route.extend({ }); App.CamelotRoute = Ember.Route.extend({ }); var currentPath; App.ApplicationController = Ember.Controller.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = property_get.get(this, 'currentPath'); }) }); App.CamelotController = Ember.Controller.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = property_get.get(this, 'currentPath'); }) }); bootApplication(); handleURL("/camelot"); equal(currentPath, 'camelot'); equal(Ember.$('h3:contains(silly)', '#qunit-fixture').length, 1, "The camelot template was rendered"); handleURL("/"); equal(currentPath, 'home'); equal(Ember.$('h3:contains(Hours)', '#qunit-fixture').length, 1, "The home template was rendered"); }); QUnit.test("The Homepage register as activeView", function() { Router.map(function() { this.route("home", { path: "/" }); this.route("homepage"); }); App.HomeRoute = Ember.Route.extend({ }); App.HomepageRoute = Ember.Route.extend({ }); bootApplication(); ok(router._lookupActiveView('home'), '`home` active view is connected'); handleURL('/homepage'); ok(router._lookupActiveView('homepage'), '`homepage` active view is connected'); equal(router._lookupActiveView('home'), undefined, '`home` active view is disconnected'); }); QUnit.test("The Homepage with explicit template name in renderTemplate", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render('homepage'); } }); bootApplication(); equal(Ember.$('h3:contains(Megatroll)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); QUnit.test("An alternate template will pull in an alternate controller", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render('homepage'); } }); App.HomepageController = Ember.Controller.extend({ model: { home: "Comes from homepage" } }); bootApplication(); equal(Ember.$('h3:contains(Megatroll) + p:contains(Comes from homepage)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); QUnit.test("An alternate template will pull in an alternate controller instead of controllerName", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ controllerName: 'foo', renderTemplate: function() { this.render('homepage'); } }); App.FooController = Ember.Controller.extend({ model: { home: "Comes from Foo" } }); App.HomepageController = Ember.Controller.extend({ model: { home: "Comes from homepage" } }); bootApplication(); equal(Ember.$('h3:contains(Megatroll) + p:contains(Comes from homepage)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); QUnit.test("The template will pull in an alternate controller via key/value", function() { Router.map(function() { this.route("homepage", { path: "/" }); }); App.HomepageRoute = Ember.Route.extend({ renderTemplate: function() { this.render({ controller: 'home' }); } }); App.HomeController = Ember.Controller.extend({ model: { home: "Comes from home." } }); bootApplication(); equal(Ember.$('h3:contains(Megatroll) + p:contains(Comes from home.)', '#qunit-fixture').length, 1, "The homepage template was rendered from data from the HomeController"); }); QUnit.test("The Homepage with explicit template name in renderTemplate and controller", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeController = Ember.Controller.extend({ model: { home: "YES I AM HOME" } }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render('homepage'); } }); bootApplication(); equal(Ember.$('h3:contains(Megatroll) + p:contains(YES I AM HOME)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); QUnit.test("Model passed via renderTemplate model is set as controller's model", function() { Ember.TEMPLATES['bio'] = compile("

    {{model.name}}

    "); App.BioController = Ember.Controller.extend(); Router.map(function() { this.route('home', { path: '/' }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render('bio', { model: { name: 'emberjs' } }); } }); bootApplication(); equal(Ember.$('p:contains(emberjs)', '#qunit-fixture').length, 1, "Passed model was set as controllers model"); }); QUnit.test("Renders correct view with slash notation", function() { Ember.TEMPLATES['home/page'] = compile("

    {{view.name}}

    "); Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render('home/page'); } }); App.HomePageView = Ember.View.extend({ name: "Home/Page" }); bootApplication(); equal(Ember.$('p:contains(Home/Page)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); QUnit.test("Renders the view given in the view option", function() { Ember.TEMPLATES['home'] = compile("

    {{view.name}}

    "); Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render({ view: 'homePage' }); } }); App.HomePageView = Ember.View.extend({ name: "Home/Page" }); bootApplication(); equal(Ember.$('p:contains(Home/Page)', '#qunit-fixture').length, 1, "The homepage view was rendered"); }); QUnit.test('render does not replace templateName if user provided', function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.the_real_home_template = compile( "

    THIS IS THE REAL HOME

    " ); App.HomeView = Ember.View.extend({ templateName: 'the_real_home_template' }); App.HomeController = Ember.Controller.extend(); App.HomeRoute = Ember.Route.extend(); bootApplication(); equal(Ember.$('p', '#qunit-fixture').text(), "THIS IS THE REAL HOME", "The homepage template was rendered"); }); QUnit.test('render does not replace template if user provided', function () { Router.map(function () { this.route("home", { path: "/" }); }); App.HomeView = Ember.View.extend({ template: compile("

    THIS IS THE REAL HOME

    ") }); App.HomeController = Ember.Controller.extend(); App.HomeRoute = Ember.Route.extend(); bootApplication(); Ember.run(function () { router.handleURL("/"); }); equal(Ember.$('p', '#qunit-fixture').text(), "THIS IS THE REAL HOME", "The homepage template was rendered"); }); QUnit.test('render uses templateName from route', function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.the_real_home_template = compile( "

    THIS IS THE REAL HOME

    " ); App.HomeController = Ember.Controller.extend(); App.HomeRoute = Ember.Route.extend({ templateName: 'the_real_home_template' }); bootApplication(); equal(Ember.$('p', '#qunit-fixture').text(), "THIS IS THE REAL HOME", "The homepage template was rendered"); }); QUnit.test('defining templateName allows other templates to be rendered', function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.alert = compile( "
    Invader!
    " ); Ember.TEMPLATES.the_real_home_template = compile( "

    THIS IS THE REAL HOME

    {{outlet 'alert'}}" ); App.HomeController = Ember.Controller.extend(); App.HomeRoute = Ember.Route.extend({ templateName: 'the_real_home_template', actions: { showAlert: function() { this.render('alert', { into: 'home', outlet: 'alert' }); } } }); bootApplication(); equal(Ember.$('p', '#qunit-fixture').text(), "THIS IS THE REAL HOME", "The homepage template was rendered"); Ember.run(function() { router.send('showAlert'); }); equal(Ember.$('.alert-box', '#qunit-fixture').text(), "Invader!", "Template for alert was render into outlet"); }); QUnit.test('Specifying a name to render should have precedence over everything else', function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeController = Ember.Controller.extend(); App.HomeRoute = Ember.Route.extend({ templateName: 'home', controllerName: 'home', viewName: 'home', renderTemplate: function() { this.render('homepage'); } }); App.HomeView = Ember.View.extend({ template: compile("

    This should not be rendered

    {{model.home}}

    ") }); App.HomepageController = Ember.Controller.extend({ model: { home: 'Tinytroll' } }); App.HomepageView = Ember.View.extend({ layout: compile( "Outer{{yield}}troll" ), templateName: 'homepage' }); bootApplication(); equal(Ember.$('h3', '#qunit-fixture').text(), "Megatroll", "The homepage template was rendered"); equal(Ember.$('p', '#qunit-fixture').text(), "Tinytroll", "The homepage controller was used"); equal(Ember.$('span', '#qunit-fixture').text(), "Outertroll", "The homepage view was used"); }); QUnit.test("The Homepage with a `setupController` hook", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ setupController: function(controller) { property_set.set(controller, 'hours', Ember.A([ "Monday through Friday: 9am to 5pm", "Saturday: Noon to Midnight", "Sunday: Noon to 6pm" ])); } }); Ember.TEMPLATES.home = compile( "
      {{#each entry in hours}}
    • {{entry}}
    • {{/each}}
    " ); bootApplication(); equal(Ember.$('ul li', '#qunit-fixture').eq(2).text(), "Sunday: Noon to 6pm", "The template was rendered with the hours context"); }); QUnit.test("The route controller is still set when overriding the setupController hook", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ setupController: function(controller) { // no-op // importantly, we are not calling this._super here } }); registry.register('controller:home', Ember.Controller.extend()); bootApplication(); deepEqual(container.lookup('route:home').controller, container.lookup('controller:home'), "route controller is the home controller"); }); QUnit.test("The route controller can be specified via controllerName", function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.home = compile( "

    {{myValue}}

    " ); App.HomeRoute = Ember.Route.extend({ controllerName: 'myController' }); registry.register('controller:myController', Ember.Controller.extend({ myValue: "foo" })); bootApplication(); deepEqual(container.lookup('route:home').controller, container.lookup('controller:myController'), "route controller is set by controllerName"); equal(Ember.$('p', '#qunit-fixture').text(), "foo", "The homepage template was rendered with data from the custom controller"); }); QUnit.test("The route controller specified via controllerName is used in render", function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.alternative_home = compile( "

    alternative home: {{myValue}}

    " ); App.HomeRoute = Ember.Route.extend({ controllerName: 'myController', renderTemplate: function() { this.render("alternative_home"); } }); registry.register('controller:myController', Ember.Controller.extend({ myValue: "foo" })); bootApplication(); deepEqual(container.lookup('route:home').controller, container.lookup('controller:myController'), "route controller is set by controllerName"); equal(Ember.$('p', '#qunit-fixture').text(), "alternative home: foo", "The homepage template was rendered with data from the custom controller"); }); QUnit.test("The route controller specified via controllerName is used in render even when a controller with the routeName is available", function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.home = compile( "

    home: {{myValue}}

    " ); App.HomeRoute = Ember.Route.extend({ controllerName: 'myController' }); registry.register('controller:home', Ember.Controller.extend({ myValue: "home" })); registry.register('controller:myController', Ember.Controller.extend({ myValue: "myController" })); bootApplication(); deepEqual(container.lookup('route:home').controller, container.lookup('controller:myController'), "route controller is set by controllerName"); equal(Ember.$('p', '#qunit-fixture').text(), "home: myController", "The homepage template was rendered with data from the custom controller"); }); QUnit.test("The Homepage with a `setupController` hook modifying other controllers", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ setupController: function(controller) { property_set.set(this.controllerFor('home'), 'hours', Ember.A([ "Monday through Friday: 9am to 5pm", "Saturday: Noon to Midnight", "Sunday: Noon to 6pm" ])); } }); Ember.TEMPLATES.home = compile( "
      {{#each entry in hours}}
    • {{entry}}
    • {{/each}}
    " ); bootApplication(); equal(Ember.$('ul li', '#qunit-fixture').eq(2).text(), "Sunday: Noon to 6pm", "The template was rendered with the hours context"); }); QUnit.test("The Homepage with a computed context that does not get overridden", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeController = Ember.ArrayController.extend({ model: Ember.computed(function() { return Ember.A([ "Monday through Friday: 9am to 5pm", "Saturday: Noon to Midnight", "Sunday: Noon to 6pm" ]); }) }); Ember.TEMPLATES.home = compile( "
      {{#each passage in model}}
    • {{passage}}
    • {{/each}}
    " ); bootApplication(); equal(Ember.$('ul li', '#qunit-fixture').eq(2).text(), "Sunday: Noon to 6pm", "The template was rendered with the context intact"); }); QUnit.test("The Homepage getting its controller context via model", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ model: function() { return Ember.A([ "Monday through Friday: 9am to 5pm", "Saturday: Noon to Midnight", "Sunday: Noon to 6pm" ]); }, setupController: function(controller, model) { equal(this.controllerFor('home'), controller); property_set.set(this.controllerFor('home'), 'hours', model); } }); Ember.TEMPLATES.home = compile( "
      {{#each entry in hours}}
    • {{entry}}
    • {{/each}}
    " ); bootApplication(); equal(Ember.$('ul li', '#qunit-fixture').eq(2).text(), "Sunday: Noon to 6pm", "The template was rendered with the hours context"); }); QUnit.test("The Specials Page getting its controller context by deserializing the params hash", function() { Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); App.SpecialRoute = Ember.Route.extend({ model: function(params) { return Ember.Object.create({ menuItemId: params.menu_item_id }); }, setupController: function(controller, model) { property_set.set(controller, 'model', model); } }); Ember.TEMPLATES.special = compile( "

    {{model.menuItemId}}

    " ); bootApplication(); registry.register('controller:special', Ember.Controller.extend()); handleURL("/specials/1"); equal(Ember.$('p', '#qunit-fixture').text(), "1", "The model was used to render the template"); }); QUnit.test("The Specials Page defaults to looking models up via `find`", function() { Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); App.MenuItem = Ember.Object.extend(); App.MenuItem.reopenClass({ find: function(id) { return App.MenuItem.create({ id: id }); } }); App.SpecialRoute = Ember.Route.extend({ setupController: function(controller, model) { property_set.set(controller, 'model', model); } }); Ember.TEMPLATES.special = compile( "

    {{model.id}}

    " ); bootApplication(); registry.register('controller:special', Ember.Controller.extend()); handleURL("/specials/1"); equal(Ember.$('p', '#qunit-fixture').text(), "1", "The model was used to render the template"); }); QUnit.test("The Special Page returning a promise puts the app into a loading state until the promise is resolved", function() { Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); var menuItem, resolve; App.MenuItem = Ember.Object.extend(); App.MenuItem.reopenClass({ find: function(id) { menuItem = App.MenuItem.create({ id: id }); return new Ember.RSVP.Promise(function(res) { resolve = res; }); } }); App.LoadingRoute = Ember.Route.extend({ }); App.SpecialRoute = Ember.Route.extend({ setupController: function(controller, model) { property_set.set(controller, 'model', model); } }); Ember.TEMPLATES.special = compile( "

    {{model.id}}

    " ); Ember.TEMPLATES.loading = compile( "

    LOADING!

    " ); bootApplication(); registry.register('controller:special', Ember.Controller.extend()); handleURL("/specials/1"); equal(Ember.$('p', '#qunit-fixture').text(), "LOADING!", "The app is in the loading state"); Ember.run(function() { resolve(menuItem); }); equal(Ember.$('p', '#qunit-fixture').text(), "1", "The app is now in the specials state"); }); QUnit.test("The loading state doesn't get entered for promises that resolve on the same run loop", function() { Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); App.MenuItem = Ember.Object.extend(); App.MenuItem.reopenClass({ find: function(id) { return { id: id }; } }); App.LoadingRoute = Ember.Route.extend({ enter: function() { ok(false, "LoadingRoute shouldn't have been entered."); } }); App.SpecialRoute = Ember.Route.extend({ setupController: function(controller, model) { property_set.set(controller, 'model', model); } }); Ember.TEMPLATES.special = compile( "

    {{model.id}}

    " ); Ember.TEMPLATES.loading = compile( "

    LOADING!

    " ); bootApplication(); registry.register('controller:special', Ember.Controller.extend()); handleURL("/specials/1"); equal(Ember.$('p', '#qunit-fixture').text(), "1", "The app is now in the specials state"); }); /* asyncTest("The Special page returning an error fires the error hook on SpecialRoute", function() { Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); var menuItem; App.MenuItem = Ember.Object.extend(Ember.DeferredMixin); App.MenuItem.reopenClass({ find: function(id) { menuItem = App.MenuItem.create({ id: id }); Ember.run.later(function() { menuItem.resolve(menuItem); }, 1); return menuItem; } }); App.SpecialRoute = Ember.Route.extend({ setup: function() { throw 'Setup error'; }, actions: { error: function(reason) { equal(reason, 'Setup error'); QUnit.start(); } } }); bootApplication(); handleURLRejectsWith('/specials/1', 'Setup error'); }); */ QUnit.test("The Special page returning an error invokes SpecialRoute's error handler", function() { Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); var menuItem, promise, resolve; App.MenuItem = Ember.Object.extend(); App.MenuItem.reopenClass({ find: function(id) { menuItem = App.MenuItem.create({ id: id }); promise = new Ember.RSVP.Promise(function(res) { resolve = res; }); return promise; } }); App.SpecialRoute = Ember.Route.extend({ setup: function() { throw 'Setup error'; }, actions: { error: function(reason) { equal(reason, 'Setup error', 'SpecialRoute#error received the error thrown from setup'); } } }); bootApplication(); handleURLRejectsWith('/specials/1', 'Setup error'); Ember.run(function() { resolve(menuItem); }); }); function testOverridableErrorHandler(handlersName) { expect(2); Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); var menuItem, resolve; App.MenuItem = Ember.Object.extend(); App.MenuItem.reopenClass({ find: function(id) { menuItem = App.MenuItem.create({ id: id }); return new Ember.RSVP.Promise(function(res) { resolve = res; }); } }); var attrs = {}; attrs[handlersName] = { error: function(reason) { equal(reason, 'Setup error', "error was correctly passed to custom ApplicationRoute handler"); } }; App.ApplicationRoute = Ember.Route.extend(attrs); App.SpecialRoute = Ember.Route.extend({ setup: function() { throw 'Setup error'; } }); bootApplication(); handleURLRejectsWith("/specials/1", "Setup error"); Ember.run(function() { resolve(menuItem); }); } QUnit.test("ApplicationRoute's default error handler can be overridden", function() { testOverridableErrorHandler('actions'); }); QUnit.test("ApplicationRoute's default error handler can be overridden (with DEPRECATED `events`)", function() { ignoreDeprecation(function() { testOverridableErrorHandler('events'); }); }); asyncTest("Moving from one page to another triggers the correct callbacks", function() { expect(3); Router.map(function() { this.route("home", { path: "/" }); this.resource("special", { path: "/specials/:menu_item_id" }); }); App.MenuItem = Ember.Object.extend(); App.SpecialRoute = Ember.Route.extend({ setupController: function(controller, model) { property_set.set(controller, 'model', model); } }); Ember.TEMPLATES.home = compile( "

    Home

    " ); Ember.TEMPLATES.special = compile( "

    {{model.id}}

    " ); bootApplication(); registry.register('controller:special', Ember.Controller.extend()); var transition = handleURL('/'); Ember.run(function() { transition.then(function() { equal(Ember.$('h3', '#qunit-fixture').text(), "Home", "The app is now in the initial state"); var promiseContext = App.MenuItem.create({ id: 1 }); Ember.run.later(function() { Ember.RSVP.resolve(promiseContext); }, 1); return router.transitionTo('special', promiseContext); }).then(function(result) { deepEqual(router.location.path, '/specials/1'); QUnit.start(); }); }); }); asyncTest("Nested callbacks are not exited when moving to siblings", function() { Router.map(function() { this.resource("root", { path: "/" }, function() { this.resource("special", { path: "/specials/:menu_item_id" }); }); }); var currentPath; App.ApplicationController = Ember.Controller.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = property_get.get(this, 'currentPath'); }) }); var menuItem; App.MenuItem = Ember.Object.extend(); App.MenuItem.reopenClass({ find: function(id) { menuItem = App.MenuItem.create({ id: id }); return menuItem; } }); App.LoadingRoute = Ember.Route.extend({ }); App.RootRoute = Ember.Route.extend({ model: function() { rootModel++; return this._super.apply(this, arguments); }, serialize: function() { rootSerialize++; return this._super.apply(this, arguments); }, setupController: function() { rootSetup++; }, renderTemplate: function() { rootRender++; } }); App.HomeRoute = Ember.Route.extend({ }); App.SpecialRoute = Ember.Route.extend({ setupController: function(controller, model) { property_set.set(controller, 'model', model); } }); Ember.TEMPLATES['root/index'] = compile( "

    Home

    " ); Ember.TEMPLATES.special = compile( "

    {{model.id}}

    " ); Ember.TEMPLATES.loading = compile( "

    LOADING!

    " ); var rootSetup = 0; var rootRender = 0; var rootModel = 0; var rootSerialize = 0; bootApplication(); registry.register('controller:special', Ember.Controller.extend()); equal(Ember.$('h3', '#qunit-fixture').text(), "Home", "The app is now in the initial state"); equal(rootSetup, 1, "The root setup was triggered"); equal(rootRender, 1, "The root render was triggered"); equal(rootSerialize, 0, "The root serialize was not called"); equal(rootModel, 1, "The root model was called"); router = container.lookup('router:main'); Ember.run(function() { var menuItem = App.MenuItem.create({ id: 1 }); Ember.run.later(function() { Ember.RSVP.resolve(menuItem); }, 1); router.transitionTo('special', menuItem).then(function(result) { equal(rootSetup, 1, "The root setup was not triggered again"); equal(rootRender, 1, "The root render was not triggered again"); equal(rootSerialize, 0, "The root serialize was not called"); // TODO: Should this be changed? equal(rootModel, 1, "The root model was called again"); deepEqual(router.location.path, '/specials/1'); equal(currentPath, 'root.special'); QUnit.start(); }); }); }); asyncTest("Events are triggered on the controller if a matching action name is implemented", function() { Router.map(function() { this.route("home", { path: "/" }); }); var model = { name: "Tom Dale" }; var stateIsNotCalled = true; App.HomeRoute = Ember.Route.extend({ model: function() { return model; }, actions: { showStuff: function(obj) { stateIsNotCalled = false; } } }); Ember.TEMPLATES.home = compile( "{{name}}" ); var controller = Ember.Controller.extend({ actions: { showStuff: function(context) { ok(stateIsNotCalled, "an event on the state is not triggered"); deepEqual(context, { name: "Tom Dale" }, "an event with context is passed"); QUnit.start(); } } }); registry.register('controller:home', controller); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); asyncTest("Events are triggered on the current state when defined in `actions` object", function() { Router.map(function() { this.route("home", { path: "/" }); }); var model = { name: "Tom Dale" }; App.HomeRoute = Ember.Route.extend({ model: function() { return model; }, actions: { showStuff: function(obj) { ok(this instanceof App.HomeRoute, "the handler is an App.HomeRoute"); // Using Ember.copy removes any private Ember vars which older IE would be confused by deepEqual(Ember.copy(obj, true), { name: "Tom Dale" }, "the context is correct"); QUnit.start(); } } }); Ember.TEMPLATES.home = compile( "{{model.name}}" ); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); asyncTest("Events defined in `actions` object are triggered on the current state when routes are nested", function() { Router.map(function() { this.resource("root", { path: "/" }, function() { this.route("index", { path: "/" }); }); }); var model = { name: "Tom Dale" }; App.RootRoute = Ember.Route.extend({ actions: { showStuff: function(obj) { ok(this instanceof App.RootRoute, "the handler is an App.HomeRoute"); // Using Ember.copy removes any private Ember vars which older IE would be confused by deepEqual(Ember.copy(obj, true), { name: "Tom Dale" }, "the context is correct"); QUnit.start(); } } }); App.RootIndexRoute = Ember.Route.extend({ model: function() { return model; } }); Ember.TEMPLATES['root/index'] = compile( "{{model.name}}" ); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); asyncTest("Events are triggered on the current state when defined in `events` object (DEPRECATED)", function() { Router.map(function() { this.route("home", { path: "/" }); }); var model = { name: "Tom Dale" }; App.HomeRoute = Ember.Route.extend({ model: function() { return model; }, events: { showStuff: function(obj) { ok(this instanceof App.HomeRoute, "the handler is an App.HomeRoute"); // Using Ember.copy removes any private Ember vars which older IE would be confused by deepEqual(Ember.copy(obj, true), { name: "Tom Dale" }, "the context is correct"); QUnit.start(); } } }); Ember.TEMPLATES.home = compile( "{{name}}" ); expectDeprecation(/Action handlers contained in an `events` object are deprecated/); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); asyncTest("Events defined in `events` object are triggered on the current state when routes are nested (DEPRECATED)", function() { Router.map(function() { this.resource("root", { path: "/" }, function() { this.route("index", { path: "/" }); }); }); var model = { name: "Tom Dale" }; App.RootRoute = Ember.Route.extend({ events: { showStuff: function(obj) { ok(this instanceof App.RootRoute, "the handler is an App.HomeRoute"); // Using Ember.copy removes any private Ember vars which older IE would be confused by deepEqual(Ember.copy(obj, true), { name: "Tom Dale" }, "the context is correct"); QUnit.start(); } } }); App.RootIndexRoute = Ember.Route.extend({ model: function() { return model; } }); Ember.TEMPLATES['root/index'] = compile( "{{name}}" ); expectDeprecation(/Action handlers contained in an `events` object are deprecated/); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); QUnit.test("Events can be handled by inherited event handlers", function() { expect(4); App.SuperRoute = Ember.Route.extend({ actions: { foo: function() { ok(true, 'foo'); }, bar: function(msg) { equal(msg, "HELLO"); } } }); App.RouteMixin = Ember.Mixin.create({ actions: { bar: function(msg) { equal(msg, "HELLO"); this._super(msg); } } }); App.IndexRoute = App.SuperRoute.extend(App.RouteMixin, { actions: { baz: function() { ok(true, 'baz'); } } }); bootApplication(); router.send("foo"); router.send("bar", "HELLO"); router.send("baz"); }); asyncTest("Actions are not triggered on the controller if a matching action name is implemented as a method", function() { Router.map(function() { this.route("home", { path: "/" }); }); var model = { name: "Tom Dale" }; var stateIsNotCalled = true; App.HomeRoute = Ember.Route.extend({ model: function() { return model; }, actions: { showStuff: function(context) { ok(stateIsNotCalled, "an event on the state is not triggered"); deepEqual(context, { name: "Tom Dale" }, "an event with context is passed"); QUnit.start(); } } }); Ember.TEMPLATES.home = compile( "{{name}}" ); var controller = Ember.Controller.extend({ showStuff: function(context) { stateIsNotCalled = false; ok(stateIsNotCalled, "an event on the state is not triggered"); } }); registry.register('controller:home', controller); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); asyncTest("actions can be triggered with multiple arguments", function() { Router.map(function() { this.resource("root", { path: "/" }, function() { this.route("index", { path: "/" }); }); }); var model1 = { name: "Tilde" }; var model2 = { name: "Tom Dale" }; App.RootRoute = Ember.Route.extend({ actions: { showStuff: function(obj1, obj2) { ok(this instanceof App.RootRoute, "the handler is an App.HomeRoute"); // Using Ember.copy removes any private Ember vars which older IE would be confused by deepEqual(Ember.copy(obj1, true), { name: "Tilde" }, "the first context is correct"); deepEqual(Ember.copy(obj2, true), { name: "Tom Dale" }, "the second context is correct"); QUnit.start(); } } }); App.RootIndexController = Ember.Controller.extend({ model1: model1, model2: model2 }); Ember.TEMPLATES['root/index'] = compile( "{{model1.name}}" ); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = ActionManager['default'].registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); QUnit.test("transitioning multiple times in a single run loop only sets the URL once", function() { Router.map(function() { this.route("root", { path: "/" }); this.route("foo"); this.route("bar"); }); bootApplication(); var urlSetCount = 0; router.get('location').setURL = function(path) { urlSetCount++; property_set.set(this, 'path', path); }; equal(urlSetCount, 0); Ember.run(function() { router.transitionTo("foo"); router.transitionTo("bar"); }); equal(urlSetCount, 1); equal(router.get('location').getURL(), "/bar"); }); QUnit.test('navigating away triggers a url property change', function() { expect(3); Router.map(function() { this.route('root', { path: '/' }); this.route('foo', { path: '/foo' }); this.route('bar', { path: '/bar' }); }); bootApplication(); Ember.run(function() { Ember.addObserver(router, 'url', function() { ok(true, "url change event was fired"); }); }); enumerable_utils.forEach(['foo', 'bar', '/foo'], function(destination) { Ember.run(router, 'transitionTo', destination); }); }); QUnit.test("using replaceWith calls location.replaceURL if available", function() { var setCount = 0; var replaceCount = 0; Router.reopen({ location: Ember.NoneLocation.createWithMixins({ setURL: function(path) { setCount++; property_set.set(this, 'path', path); }, replaceURL: function(path) { replaceCount++; property_set.set(this, 'path', path); } }) }); Router.map(function() { this.route("root", { path: "/" }); this.route("foo"); }); bootApplication(); equal(setCount, 0); equal(replaceCount, 0); Ember.run(function() { router.replaceWith("foo"); }); equal(setCount, 0, 'should not call setURL'); equal(replaceCount, 1, 'should call replaceURL once'); equal(router.get('location').getURL(), "/foo"); }); QUnit.test("using replaceWith calls setURL if location.replaceURL is not defined", function() { var setCount = 0; Router.reopen({ location: Ember.NoneLocation.createWithMixins({ setURL: function(path) { setCount++; property_set.set(this, 'path', path); } }) }); Router.map(function() { this.route("root", { path: "/" }); this.route("foo"); }); bootApplication(); equal(setCount, 0); Ember.run(function() { router.replaceWith("foo"); }); equal(setCount, 1, 'should call setURL once'); equal(router.get('location').getURL(), "/foo"); }); QUnit.test("Route inherits model from parent route", function() { expect(9); Router.map(function() { this.resource("the_post", { path: "/posts/:post_id" }, function() { this.route("comments"); this.resource("shares", { path: "/shares/:share_id" }, function() { this.route("share"); }); }); }); var post1 = {}; var post2 = {}; var post3 = {}; var currentPost; var share1 = {}; var share2 = {}; var share3 = {}; var posts = { 1: post1, 2: post2, 3: post3 }; var shares = { 1: share1, 2: share2, 3: share3 }; App.ThePostRoute = Ember.Route.extend({ model: function(params) { return posts[params.post_id]; } }); App.ThePostCommentsRoute = Ember.Route.extend({ afterModel: function(post, transition) { var parent_model = this.modelFor('thePost'); equal(post, parent_model); } }); App.SharesRoute = Ember.Route.extend({ model: function(params) { return shares[params.share_id]; } }); App.SharesShareRoute = Ember.Route.extend({ afterModel: function(share, transition) { var parent_model = this.modelFor('shares'); equal(share, parent_model); } }); bootApplication(); currentPost = post1; handleURL("/posts/1/comments"); handleURL("/posts/1/shares/1"); currentPost = post2; handleURL("/posts/2/comments"); handleURL("/posts/2/shares/2"); currentPost = post3; handleURL("/posts/3/comments"); handleURL("/posts/3/shares/3"); }); QUnit.test("Resource inherits model from parent resource", function() { expect(6); Router.map(function() { this.resource("the_post", { path: "/posts/:post_id" }, function() { this.resource("comments", function() { }); }); }); var post1 = {}; var post2 = {}; var post3 = {}; var currentPost; var posts = { 1: post1, 2: post2, 3: post3 }; App.ThePostRoute = Ember.Route.extend({ model: function(params) { return posts[params.post_id]; } }); App.CommentsRoute = Ember.Route.extend({ afterModel: function(post, transition) { var parent_model = this.modelFor('thePost'); equal(post, parent_model); } }); bootApplication(); currentPost = post1; handleURL("/posts/1/comments"); currentPost = post2; handleURL("/posts/2/comments"); currentPost = post3; handleURL("/posts/3/comments"); }); QUnit.test("It is possible to get the model from a parent route", function() { expect(9); Router.map(function() { this.resource("the_post", { path: "/posts/:post_id" }, function() { this.resource("comments"); }); }); var post1 = {}; var post2 = {}; var post3 = {}; var currentPost; var posts = { 1: post1, 2: post2, 3: post3 }; App.ThePostRoute = Ember.Route.extend({ model: function(params) { return posts[params.post_id]; } }); App.CommentsRoute = Ember.Route.extend({ model: function() { // Allow both underscore / camelCase format. equal(this.modelFor('thePost'), currentPost); equal(this.modelFor('the_post'), currentPost); } }); bootApplication(); currentPost = post1; handleURL("/posts/1/comments"); currentPost = post2; handleURL("/posts/2/comments"); currentPost = post3; handleURL("/posts/3/comments"); }); QUnit.test("A redirection hook is provided", function() { Router.map(function() { this.route("choose", { path: "/" }); this.route("home"); }); var chooseFollowed = 0; var destination; App.ChooseRoute = Ember.Route.extend({ redirect: function() { if (destination) { this.transitionTo(destination); } }, setupController: function() { chooseFollowed++; } }); destination = 'home'; bootApplication(); equal(chooseFollowed, 0, "The choose route wasn't entered since a transition occurred"); equal(Ember.$("h3:contains(Hours)", "#qunit-fixture").length, 1, "The home template was rendered"); equal(router.container.lookup('controller:application').get('currentPath'), 'home'); }); QUnit.test("Redirecting from the middle of a route aborts the remainder of the routes", function() { expect(3); Router.map(function() { this.route("home"); this.resource("foo", function() { this.resource("bar", function() { this.route("baz"); }); }); }); App.BarRoute = Ember.Route.extend({ redirect: function() { this.transitionTo("home"); }, setupController: function() { ok(false, "Should transition before setupController"); } }); App.BarBazRoute = Ember.Route.extend({ enter: function() { ok(false, "Should abort transition getting to next route"); } }); bootApplication(); handleURLAborts("/foo/bar/baz"); equal(router.container.lookup('controller:application').get('currentPath'), 'home'); equal(router.get('location').getURL(), "/home"); }); QUnit.test("Redirecting to the current target in the middle of a route does not abort initial routing", function() { expect(5); Router.map(function() { this.route("home"); this.resource("foo", function() { this.resource("bar", function() { this.route("baz"); }); }); }); var successCount = 0; App.BarRoute = Ember.Route.extend({ redirect: function() { this.transitionTo("bar.baz").then(function() { successCount++; }); }, setupController: function() { ok(true, "Should still invoke bar's setupController"); } }); App.BarBazRoute = Ember.Route.extend({ setupController: function() { ok(true, "Should still invoke bar.baz's setupController"); } }); bootApplication(); handleURL("/foo/bar/baz"); equal(router.container.lookup('controller:application').get('currentPath'), 'foo.bar.baz'); equal(successCount, 1, 'transitionTo success handler was called once'); }); QUnit.test("Redirecting to the current target with a different context aborts the remainder of the routes", function() { expect(4); Router.map(function() { this.route("home"); this.resource("foo", function() { this.resource("bar", { path: "bar/:id" }, function() { this.route("baz"); }); }); }); var model = { id: 2 }; var count = 0; App.BarRoute = Ember.Route.extend({ afterModel: function(context) { if (count++ > 10) { ok(false, 'infinite loop'); } else { this.transitionTo("bar.baz", model); } }, serialize: function(params) { return params; } }); App.BarBazRoute = Ember.Route.extend({ setupController: function() { ok(true, "Should still invoke setupController"); } }); bootApplication(); handleURLAborts("/foo/bar/1/baz"); equal(router.container.lookup('controller:application').get('currentPath'), 'foo.bar.baz'); equal(router.get('location').getURL(), "/foo/bar/2/baz"); }); QUnit.test("Transitioning from a parent event does not prevent currentPath from being set", function() { Router.map(function() { this.resource("foo", function() { this.resource("bar", function() { this.route("baz"); }); this.route("qux"); }); }); App.FooRoute = Ember.Route.extend({ actions: { goToQux: function() { this.transitionTo('foo.qux'); } } }); bootApplication(); var applicationController = router.container.lookup('controller:application'); handleURL("/foo/bar/baz"); equal(applicationController.get('currentPath'), 'foo.bar.baz'); Ember.run(function() { router.send("goToQux"); }); equal(applicationController.get('currentPath'), 'foo.qux'); equal(router.get('location').getURL(), "/foo/qux"); }); QUnit.test("Generated names can be customized when providing routes with dot notation", function() { expect(4); Ember.TEMPLATES.index = compile("
    Index
    "); Ember.TEMPLATES.application = compile("

    Home

    {{outlet}}
    "); Ember.TEMPLATES.foo = compile("
    {{outlet}}
    "); Ember.TEMPLATES.bar = compile("
    {{outlet}}
    "); Ember.TEMPLATES['bar/baz'] = compile("

    {{name}}Bottom!

    "); Router.map(function() { this.resource("foo", { path: "/top" }, function() { this.resource("bar", { path: "/middle" }, function() { this.route("baz", { path: "/bottom" }); }); }); }); App.FooRoute = Ember.Route.extend({ renderTemplate: function() { ok(true, "FooBarRoute was called"); return this._super.apply(this, arguments); } }); App.BarBazRoute = Ember.Route.extend({ renderTemplate: function() { ok(true, "BarBazRoute was called"); return this._super.apply(this, arguments); } }); App.BarController = Ember.Controller.extend({ name: "Bar" }); App.BarBazController = Ember.Controller.extend({ name: "BarBaz" }); bootApplication(); handleURL("/top/middle/bottom"); equal(Ember.$('.main .middle .bottom p', '#qunit-fixture').text(), "BarBazBottom!", "The templates were rendered into their appropriate parents"); }); QUnit.test("Child routes render into their parent route's template by default", function() { Ember.TEMPLATES.index = compile("
    Index
    "); Ember.TEMPLATES.application = compile("

    Home

    {{outlet}}
    "); Ember.TEMPLATES.top = compile("
    {{outlet}}
    "); Ember.TEMPLATES.middle = compile("
    {{outlet}}
    "); Ember.TEMPLATES['middle/bottom'] = compile("

    Bottom!

    "); Router.map(function() { this.resource("top", function() { this.resource("middle", function() { this.route("bottom"); }); }); }); bootApplication(); handleURL("/top/middle/bottom"); equal(Ember.$('.main .middle .bottom p', '#qunit-fixture').text(), "Bottom!", "The templates were rendered into their appropriate parents"); }); QUnit.test("Child routes render into specified template", function() { Ember.TEMPLATES.index = compile("
    Index
    "); Ember.TEMPLATES.application = compile("

    Home

    {{outlet}}
    "); Ember.TEMPLATES.top = compile("
    {{outlet}}
    "); Ember.TEMPLATES.middle = compile("
    {{outlet}}
    "); Ember.TEMPLATES['middle/bottom'] = compile("

    Bottom!

    "); Router.map(function() { this.resource("top", function() { this.resource("middle", function() { this.route("bottom"); }); }); }); App.MiddleBottomRoute = Ember.Route.extend({ renderTemplate: function() { this.render('middle/bottom', { into: 'top' }); } }); bootApplication(); handleURL("/top/middle/bottom"); equal(Ember.$('.main .middle .bottom p', '#qunit-fixture').length, 0, "should not render into the middle template"); equal(Ember.$('.main .middle > p', '#qunit-fixture').text(), "Bottom!", "The template was rendered into the top template"); }); QUnit.test("Rendering into specified template with slash notation", function() { Ember.TEMPLATES['person/profile'] = compile("profile {{outlet}}"); Ember.TEMPLATES['person/details'] = compile("details!"); Router.map(function() { this.resource("home", { path: '/' }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { this.render('person/profile'); this.render('person/details', { into: 'person/profile' }); } }); bootApplication(); equal(Ember.$('#qunit-fixture:contains(profile details!)').length, 1, "The templates were rendered"); }); QUnit.test("Parent route context change", function() { var editCount = 0; var editedPostIds = Ember.A(); Ember.TEMPLATES.application = compile("{{outlet}}"); Ember.TEMPLATES.posts = compile("{{outlet}}"); Ember.TEMPLATES.post = compile("{{outlet}}"); Ember.TEMPLATES['post/index'] = compile("showing"); Ember.TEMPLATES['post/edit'] = compile("editing"); Router.map(function() { this.resource("posts", function() { this.resource("post", { path: "/:postId" }, function() { this.route("edit"); }); }); }); App.PostsRoute = Ember.Route.extend({ actions: { showPost: function(context) { this.transitionTo('post', context); } } }); App.PostRoute = Ember.Route.extend({ model: function(params) { return { id: params.postId }; }, actions: { editPost: function(context) { this.transitionTo('post.edit'); } } }); App.PostEditRoute = Ember.Route.extend({ model: function(params) { var postId = this.modelFor("post").id; editedPostIds.push(postId); return null; }, setup: function() { this._super.apply(this, arguments); editCount++; } }); bootApplication(); handleURL("/posts/1"); Ember.run(function() { router.send('editPost'); }); Ember.run(function() { router.send('showPost', { id: '2' }); }); Ember.run(function() { router.send('editPost'); }); equal(editCount, 2, 'set up the edit route twice without failure'); deepEqual(editedPostIds, ['1', '2'], 'modelFor posts.post returns the right context'); }); QUnit.test("Router accounts for rootURL on page load when using history location", function() { var rootURL = window.location.pathname + '/app'; var postsTemplateRendered = false; var setHistory, HistoryTestLocation; setHistory = function(obj, path) { obj.set('history', { state: { path: path } }); }; // Create new implementation that extends HistoryLocation // and set current location to rootURL + '/posts' HistoryTestLocation = Ember.HistoryLocation.extend({ initState: function() { var path = rootURL + '/posts'; setHistory(this, path); this.set('location', { pathname: path, href: 'http://localhost/' + path }); }, replaceState: function(path) { setHistory(this, path); }, pushState: function(path) { setHistory(this, path); } }); registry.register('location:historyTest', HistoryTestLocation); Router.reopen({ location: 'historyTest', rootURL: rootURL }); Router.map(function() { this.resource("posts", { path: '/posts' }); }); App.PostsRoute = Ember.Route.extend({ model: function() {}, renderTemplate: function() { postsTemplateRendered = true; } }); bootApplication(); ok(postsTemplateRendered, "Posts route successfully stripped from rootURL"); }); QUnit.test("The rootURL is passed properly to the location implementation", function() { expect(1); var rootURL = "/blahzorz"; var HistoryTestLocation; HistoryTestLocation = Ember.HistoryLocation.extend({ rootURL: 'this is not the URL you are looking for', initState: function() { equal(this.get('rootURL'), rootURL); } }); registry.register('location:history-test', HistoryTestLocation); Router.reopen({ location: 'history-test', rootURL: rootURL, // if we transition in this test we will receive failures // if the tests are run from a static file _doURLTransition: function() { } }); bootApplication(); }); QUnit.test("Only use route rendered into main outlet for default into property on child", function() { Ember.TEMPLATES.application = compile("{{outlet 'menu'}}{{outlet}}"); Ember.TEMPLATES.posts = compile("{{outlet}}"); Ember.TEMPLATES['posts/index'] = compile("postsIndex"); Ember.TEMPLATES['posts/menu'] = compile("postsMenu"); Router.map(function() { this.resource("posts", function() {}); }); App.PostsMenuView = Ember.View.extend({ tagName: 'div', templateName: 'posts/menu', classNames: ['posts-menu'] }); App.PostsIndexView = Ember.View.extend({ tagName: 'p', classNames: ['posts-index'] }); App.PostsRoute = Ember.Route.extend({ renderTemplate: function() { this.render(); this.render('postsMenu', { into: 'application', outlet: 'menu' }); } }); bootApplication(); handleURL("/posts"); equal(Ember.$('div.posts-menu:contains(postsMenu)', '#qunit-fixture').length, 1, "The posts/menu template was rendered"); equal(Ember.$('p.posts-index:contains(postsIndex)', '#qunit-fixture').length, 1, "The posts/index template was rendered"); }); QUnit.test("Generating a URL should not affect currentModel", function() { Router.map(function() { this.route("post", { path: "/posts/:post_id" }); }); var posts = { 1: { id: 1 }, 2: { id: 2 } }; App.PostRoute = Ember.Route.extend({ model: function(params) { return posts[params.post_id]; } }); bootApplication(); handleURL("/posts/1"); var route = container.lookup('route:post'); equal(route.modelFor('post'), posts[1]); var url = router.generate('post', posts[2]); equal(url, "/posts/2"); equal(route.modelFor('post'), posts[1]); }); QUnit.test("Generated route should be an instance of App.Route if provided", function() { var generatedRoute; Router.map(function() { this.route('posts'); }); App.Route = Ember.Route.extend(); bootApplication(); handleURL("/posts"); generatedRoute = container.lookup('route:posts'); ok(generatedRoute instanceof App.Route, 'should extend the correct route'); }); QUnit.test("Nested index route is not overriden by parent's implicit index route", function() { Router.map(function() { this.resource('posts', function() { this.route('index', { path: ':category' }); }); }); App.Route = Ember.Route.extend({ serialize: function(model) { return { category: model.category }; } }); bootApplication(); Ember.run(function() { router.transitionTo('posts', { category: 'emberjs' }); }); deepEqual(router.location.path, '/posts/emberjs'); }); QUnit.test("Application template does not duplicate when re-rendered", function() { Ember.TEMPLATES.application = compile("

    I Render Once

    {{outlet}}"); Router.map(function() { this.route('posts'); }); App.ApplicationRoute = Ember.Route.extend({ model: function() { return Ember.A(); } }); bootApplication(); // should cause application template to re-render handleURL('/posts'); equal(Ember.$('h3:contains(I Render Once)').size(), 1); }); QUnit.test("Child routes should render inside the application template if the application template causes a redirect", function() { Ember.TEMPLATES.application = compile("

    App

    {{outlet}}"); Ember.TEMPLATES.posts = compile("posts"); Router.map(function() { this.route('posts'); this.route('photos'); }); App.ApplicationRoute = Ember.Route.extend({ afterModel: function() { this.transitionTo('posts'); } }); bootApplication(); equal(Ember.$('#qunit-fixture > div').text(), "App posts"); }); QUnit.test("The template is not re-rendered when the route's context changes", function() { Router.map(function() { this.route("page", { path: "/page/:name" }); }); App.PageRoute = Ember.Route.extend({ model: function(params) { return Ember.Object.create({ name: params.name }); } }); var insertionCount = 0; App.PageView = Ember.View.extend({ didInsertElement: function() { insertionCount += 1; } }); Ember.TEMPLATES.page = compile( "

    {{model.name}}

    " ); bootApplication(); handleURL("/page/first"); equal(Ember.$('p', '#qunit-fixture').text(), "first"); equal(insertionCount, 1); handleURL("/page/second"); equal(Ember.$('p', '#qunit-fixture').text(), "second"); equal(insertionCount, 1, "view should have inserted only once"); Ember.run(function() { router.transitionTo('page', Ember.Object.create({ name: 'third' })); }); equal(Ember.$('p', '#qunit-fixture').text(), "third"); equal(insertionCount, 1, "view should still have inserted only once"); }); QUnit.test("The template is not re-rendered when two routes present the exact same template, view, & controller", function() { Router.map(function() { this.route("first"); this.route("second"); this.route("third"); this.route("fourth"); }); App.SharedRoute = Ember.Route.extend({ viewName: 'shared', setupController: function(controller) { this.controllerFor('shared').set('message', "This is the " + this.routeName + " message"); }, renderTemplate: function(controller, context) { this.render({ controller: 'shared' }); } }); App.FirstRoute = App.SharedRoute.extend(); App.SecondRoute = App.SharedRoute.extend(); App.ThirdRoute = App.SharedRoute.extend(); App.FourthRoute = App.SharedRoute.extend({ viewName: 'fourth' }); App.SharedController = Ember.Controller.extend(); var insertionCount = 0; App.SharedView = Ember.View.extend({ templateName: 'shared', didInsertElement: function() { insertionCount += 1; } }); // Extending, in essence, creates a different view App.FourthView = App.SharedView.extend(); Ember.TEMPLATES.shared = compile( "

    {{message}}

    " ); bootApplication(); handleURL("/first"); equal(Ember.$('p', '#qunit-fixture').text(), "This is the first message"); equal(insertionCount, 1, 'expected one assertion'); // Transition by URL handleURL("/second"); equal(Ember.$('p', '#qunit-fixture').text(), "This is the second message"); equal(insertionCount, 1, "view should have inserted only once"); // Then transition directly by route name Ember.run(function() { router.transitionTo('third').then(function(value) { ok(true, 'expected transition'); }, function(reason) { ok(false, 'unexpected transition failure: ', QUnit.jsDump.parse(reason)); }); }); equal(Ember.$('p', '#qunit-fixture').text(), "This is the third message"); equal(insertionCount, 1, "view should still have inserted only once"); // Lastly transition to a different view, with the same controller and template handleURL("/fourth"); equal(Ember.$('p', '#qunit-fixture').text(), "This is the fourth message"); equal(insertionCount, 2, "view should have inserted a second time"); }); QUnit.test("ApplicationRoute with model does not proxy the currentPath", function() { var model = {}; var currentPath; App.ApplicationRoute = Ember.Route.extend({ model: function () { return model; } }); App.ApplicationController = Ember.Controller.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = property_get.get(this, 'currentPath'); }) }); bootApplication(); equal(currentPath, 'index', 'currentPath is index'); equal('currentPath' in model, false, 'should have defined currentPath on controller'); }); QUnit.test("Promises encountered on app load put app into loading state until resolved", function() { expect(2); var deferred = Ember.RSVP.defer(); App.IndexRoute = Ember.Route.extend({ model: function() { return deferred.promise; } }); Ember.TEMPLATES.index = compile("

    INDEX

    "); Ember.TEMPLATES.loading = compile("

    LOADING

    "); bootApplication(); equal(Ember.$('p', '#qunit-fixture').text(), "LOADING", "The loading state is displaying."); Ember.run(deferred.resolve); equal(Ember.$('p', '#qunit-fixture').text(), "INDEX", "The index route is display."); }); QUnit.test("Route should tear down multiple outlets", function() { Ember.TEMPLATES.application = compile("{{outlet 'menu'}}{{outlet}}{{outlet 'footer'}}"); Ember.TEMPLATES.posts = compile("{{outlet}}"); Ember.TEMPLATES.users = compile("users"); Ember.TEMPLATES['posts/index'] = compile("postsIndex"); Ember.TEMPLATES['posts/menu'] = compile("postsMenu"); Ember.TEMPLATES['posts/footer'] = compile("postsFooter"); Router.map(function() { this.resource("posts", function() {}); this.resource("users", function() {}); }); App.PostsMenuView = Ember.View.extend({ tagName: 'div', templateName: 'posts/menu', classNames: ['posts-menu'] }); App.PostsIndexView = Ember.View.extend({ tagName: 'p', classNames: ['posts-index'] }); App.PostsFooterView = Ember.View.extend({ tagName: 'div', templateName: 'posts/footer', classNames: ['posts-footer'] }); App.PostsRoute = Ember.Route.extend({ renderTemplate: function() { this.render('postsMenu', { into: 'application', outlet: 'menu' }); this.render(); this.render('postsFooter', { into: 'application', outlet: 'footer' }); } }); bootApplication(); handleURL('/posts'); equal(Ember.$('div.posts-menu:contains(postsMenu)', '#qunit-fixture').length, 1, "The posts/menu template was rendered"); equal(Ember.$('p.posts-index:contains(postsIndex)', '#qunit-fixture').length, 1, "The posts/index template was rendered"); equal(Ember.$('div.posts-footer:contains(postsFooter)', '#qunit-fixture').length, 1, "The posts/footer template was rendered"); handleURL('/users'); equal(Ember.$('div.posts-menu:contains(postsMenu)', '#qunit-fixture').length, 0, "The posts/menu template was removed"); equal(Ember.$('p.posts-index:contains(postsIndex)', '#qunit-fixture').length, 0, "The posts/index template was removed"); equal(Ember.$('div.posts-footer:contains(postsFooter)', '#qunit-fixture').length, 0, "The posts/footer template was removed"); }); QUnit.test("Route supports clearing outlet explicitly", function() { Ember.TEMPLATES.application = compile("{{outlet}}{{outlet 'modal'}}"); Ember.TEMPLATES.posts = compile("{{outlet}}"); Ember.TEMPLATES.users = compile("users"); Ember.TEMPLATES['posts/index'] = compile("postsIndex {{outlet}}"); Ember.TEMPLATES['posts/modal'] = compile("postsModal"); Ember.TEMPLATES['posts/extra'] = compile("postsExtra"); Router.map(function() { this.resource("posts", function() {}); this.resource("users", function() {}); }); App.PostsIndexView = Ember.View.extend({ classNames: ['posts-index'] }); App.PostsModalView = Ember.View.extend({ templateName: 'posts/modal', classNames: ['posts-modal'] }); App.PostsExtraView = Ember.View.extend({ templateName: 'posts/extra', classNames: ['posts-extra'] }); App.PostsRoute = Ember.Route.extend({ actions: { showModal: function() { this.render('postsModal', { into: 'application', outlet: 'modal' }); }, hideModal: function() { this.disconnectOutlet({ outlet: 'modal', parentView: 'application' }); } } }); App.PostsIndexRoute = Ember.Route.extend({ actions: { showExtra: function() { this.render('postsExtra', { into: 'posts/index' }); }, hideExtra: function() { this.disconnectOutlet({ parentView: 'posts/index' }); } } }); bootApplication(); handleURL('/posts'); equal(Ember.$('div.posts-index:contains(postsIndex)', '#qunit-fixture').length, 1, "The posts/index template was rendered"); Ember.run(function() { router.send('showModal'); }); equal(Ember.$('div.posts-modal:contains(postsModal)', '#qunit-fixture').length, 1, "The posts/modal template was rendered"); Ember.run(function() { router.send('showExtra'); }); equal(Ember.$('div.posts-extra:contains(postsExtra)', '#qunit-fixture').length, 1, "The posts/extra template was rendered"); Ember.run(function() { router.send('hideModal'); }); equal(Ember.$('div.posts-modal:contains(postsModal)', '#qunit-fixture').length, 0, "The posts/modal template was removed"); Ember.run(function() { router.send('hideExtra'); }); equal(Ember.$('div.posts-extra:contains(postsExtra)', '#qunit-fixture').length, 0, "The posts/extra template was removed"); handleURL('/users'); equal(Ember.$('div.posts-index:contains(postsIndex)', '#qunit-fixture').length, 0, "The posts/index template was removed"); equal(Ember.$('div.posts-modal:contains(postsModal)', '#qunit-fixture').length, 0, "The posts/modal template was removed"); equal(Ember.$('div.posts-extra:contains(postsExtra)', '#qunit-fixture').length, 0, "The posts/extra template was removed"); }); QUnit.test("Route supports clearing outlet using string parameter", function() { Ember.TEMPLATES.application = compile("{{outlet}}{{outlet 'modal'}}"); Ember.TEMPLATES.posts = compile("{{outlet}}"); Ember.TEMPLATES.users = compile("users"); Ember.TEMPLATES['posts/index'] = compile("postsIndex {{outlet}}"); Ember.TEMPLATES['posts/modal'] = compile("postsModal"); Router.map(function() { this.resource("posts", function() {}); this.resource("users", function() {}); }); App.PostsIndexView = Ember.View.extend({ classNames: ['posts-index'] }); App.PostsModalView = Ember.View.extend({ templateName: 'posts/modal', classNames: ['posts-modal'] }); App.PostsRoute = Ember.Route.extend({ actions: { showModal: function() { this.render('postsModal', { into: 'application', outlet: 'modal' }); }, hideModal: function() { this.disconnectOutlet('modal'); } } }); bootApplication(); handleURL('/posts'); equal(Ember.$('div.posts-index:contains(postsIndex)', '#qunit-fixture').length, 1, "The posts/index template was rendered"); Ember.run(function() { router.send('showModal'); }); equal(Ember.$('div.posts-modal:contains(postsModal)', '#qunit-fixture').length, 1, "The posts/modal template was rendered"); Ember.run(function() { router.send('hideModal'); }); equal(Ember.$('div.posts-modal:contains(postsModal)', '#qunit-fixture').length, 0, "The posts/modal template was removed"); handleURL('/users'); equal(Ember.$('div.posts-index:contains(postsIndex)', '#qunit-fixture').length, 0, "The posts/index template was removed"); equal(Ember.$('div.posts-modal:contains(postsModal)', '#qunit-fixture').length, 0, "The posts/modal template was removed"); }); QUnit.test("Route silently fails when cleaning an outlet from an inactive view", function() { expect(1); // handleURL Ember.TEMPLATES.application = compile("{{outlet}}"); Ember.TEMPLATES.posts = compile("{{outlet 'modal'}}"); Ember.TEMPLATES.modal = compile("A Yo."); Router.map(function() { this.route("posts"); }); App.PostsRoute = Ember.Route.extend({ actions: { hideSelf: function() { this.disconnectOutlet({ outlet: 'main', parentView: 'application' }); }, showModal: function() { this.render('modal', { into: 'posts', outlet: 'modal' }); }, hideModal: function() { this.disconnectOutlet({ outlet: 'modal', parentView: 'posts' }); } } }); bootApplication(); handleURL('/posts'); Ember.run(function() { router.send('showModal'); }); Ember.run(function() { router.send('hideSelf'); }); Ember.run(function() { router.send('hideModal'); }); }); QUnit.test("Router `willTransition` hook passes in cancellable transition", function() { // Should hit willTransition 3 times, once for the initial route, and then 2 more times // for the two handleURL calls below expect(3); Router.map(function() { this.route("nork"); this.route("about"); }); Router.reopen({ init: function() { this._super(); this.on('willTransition', this.testWillTransitionHook); }, testWillTransitionHook: function(transition, url) { ok(true, "willTransition was called " + url); transition.abort(); } }); App.LoadingRoute = Ember.Route.extend({ activate: function() { ok(false, "LoadingRoute was not entered"); } }); App.NorkRoute = Ember.Route.extend({ activate: function() { ok(false, "NorkRoute was not entered"); } }); App.AboutRoute = Ember.Route.extend({ activate: function() { ok(false, "AboutRoute was not entered"); } }); bootApplication(); // Attempted transitions out of index should abort. Ember.run(router, 'handleURL', '/nork'); Ember.run(router, 'handleURL', '/about'); }); QUnit.test("Aborting/redirecting the transition in `willTransition` prevents LoadingRoute from being entered", function() { expect(8); Router.map(function() { this.route("nork"); this.route("about"); }); var redirect = false; App.IndexRoute = Ember.Route.extend({ actions: { willTransition: function(transition) { ok(true, "willTransition was called"); if (redirect) { // router.js won't refire `willTransition` for this redirect this.transitionTo('about'); } else { transition.abort(); } } } }); var deferred = null; App.LoadingRoute = Ember.Route.extend({ activate: function() { ok(deferred, "LoadingRoute should be entered at this time"); }, deactivate: function() { ok(true, "LoadingRoute was exited"); } }); App.NorkRoute = Ember.Route.extend({ activate: function() { ok(true, "NorkRoute was entered"); } }); App.AboutRoute = Ember.Route.extend({ activate: function() { ok(true, "AboutRoute was entered"); }, model: function() { if (deferred) { return deferred.promise; } } }); bootApplication(); // Attempted transitions out of index should abort. Ember.run(router, 'transitionTo', 'nork'); Ember.run(router, 'handleURL', '/nork'); // Attempted transitions out of index should redirect to about redirect = true; Ember.run(router, 'transitionTo', 'nork'); Ember.run(router, 'transitionTo', 'index'); // Redirected transitions out of index to a route with a // promise model should pause the transition and // activate LoadingRoute deferred = Ember.RSVP.defer(); Ember.run(router, 'transitionTo', 'nork'); Ember.run(deferred.resolve); }); QUnit.test("`didTransition` event fires on the router", function() { expect(3); Router.map(function() { this.route("nork"); }); router = container.lookup('router:main'); router.one('didTransition', function() { ok(true, 'didTransition fired on initial routing'); }); bootApplication(); router.one('didTransition', function() { ok(true, 'didTransition fired on the router'); equal(router.get('url'), "/nork", 'The url property is updated by the time didTransition fires'); }); Ember.run(router, 'transitionTo', 'nork'); }); QUnit.test("`didTransition` can be reopened", function() { expect(1); Router.map(function() { this.route("nork"); }); Router.reopen({ didTransition: function() { this._super.apply(this, arguments); ok(true, 'reopened didTransition was called'); } }); bootApplication(); }); QUnit.test("`activate` event fires on the route", function() { expect(2); var eventFired = 0; Router.map(function() { this.route("nork"); }); App.NorkRoute = Ember.Route.extend({ init: function() { this._super.apply(this, arguments); this.on("activate", function() { equal(++eventFired, 1, "activate event is fired once"); }); }, activate: function() { ok(true, "activate hook is called"); } }); bootApplication(); Ember.run(router, 'transitionTo', 'nork'); }); QUnit.test("`deactivate` event fires on the route", function() { expect(2); var eventFired = 0; Router.map(function() { this.route("nork"); this.route("dork"); }); App.NorkRoute = Ember.Route.extend({ init: function() { this._super.apply(this, arguments); this.on("deactivate", function() { equal(++eventFired, 1, "deactivate event is fired once"); }); }, deactivate: function() { ok(true, "deactivate hook is called"); } }); bootApplication(); Ember.run(router, 'transitionTo', 'nork'); Ember.run(router, 'transitionTo', 'dork'); }); QUnit.test("Actions can be handled by inherited action handlers", function() { expect(4); App.SuperRoute = Ember.Route.extend({ actions: { foo: function() { ok(true, 'foo'); }, bar: function(msg) { equal(msg, "HELLO"); } } }); App.RouteMixin = Ember.Mixin.create({ actions: { bar: function(msg) { equal(msg, "HELLO"); this._super(msg); } } }); App.IndexRoute = App.SuperRoute.extend(App.RouteMixin, { actions: { baz: function() { ok(true, 'baz'); } } }); bootApplication(); router.send("foo"); router.send("bar", "HELLO"); router.send("baz"); }); QUnit.test("currentRouteName is a property installed on ApplicationController that can be used in transitionTo", function() { expect(24); Router.map(function() { this.resource("be", function() { this.resource("excellent", function() { this.resource("to", function() { this.resource("each", function() { this.route("other"); }); }); }); }); }); bootApplication(); var appController = router.container.lookup('controller:application'); function transitionAndCheck(path, expectedPath, expectedRouteName) { if (path) { Ember.run(router, 'transitionTo', path); } equal(appController.get('currentPath'), expectedPath); equal(appController.get('currentRouteName'), expectedRouteName); } transitionAndCheck(null, 'index', 'index'); transitionAndCheck('/be', 'be.index', 'be.index'); transitionAndCheck('/be/excellent', 'be.excellent.index', 'excellent.index'); transitionAndCheck('/be/excellent/to', 'be.excellent.to.index', 'to.index'); transitionAndCheck('/be/excellent/to/each', 'be.excellent.to.each.index', 'each.index'); transitionAndCheck('/be/excellent/to/each/other', 'be.excellent.to.each.other', 'each.other'); transitionAndCheck('index', 'index', 'index'); transitionAndCheck('be', 'be.index', 'be.index'); transitionAndCheck('excellent', 'be.excellent.index', 'excellent.index'); transitionAndCheck('to.index', 'be.excellent.to.index', 'to.index'); transitionAndCheck('each', 'be.excellent.to.each.index', 'each.index'); transitionAndCheck('each.other', 'be.excellent.to.each.other', 'each.other'); }); QUnit.test("Route model hook finds the same model as a manual find", function() { var Post; App.Post = Ember.Object.extend(); App.Post.reopenClass({ find: function() { Post = this; return {}; } }); Router.map(function() { this.route('post', { path: '/post/:post_id' }); }); bootApplication(); handleURL('/post/1'); equal(App.Post, Post); }); QUnit.test("Can register an implementation via Ember.Location.registerImplementation (DEPRECATED)", function() { var TestLocation = Ember.NoneLocation.extend({ implementation: 'test' }); expectDeprecation(/Using the Ember.Location.registerImplementation is no longer supported/); Ember.Location.registerImplementation('test', TestLocation); Router.reopen({ location: 'test' }); bootApplication(); equal(router.get('location.implementation'), 'test', 'custom location implementation can be registered with registerImplementation'); }); QUnit.test("Ember.Location.registerImplementation is deprecated", function() { var TestLocation = Ember.NoneLocation.extend({ implementation: 'test' }); expectDeprecation(function() { Ember.Location.registerImplementation('test', TestLocation); }, "Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead."); }); QUnit.test("Routes can refresh themselves causing their model hooks to be re-run", function() { Router.map(function() { this.resource('parent', { path: '/parent/:parent_id' }, function() { this.route('child'); }); }); var appcount = 0; App.ApplicationRoute = Ember.Route.extend({ model: function() { ++appcount; } }); var parentcount = 0; App.ParentRoute = Ember.Route.extend({ model: function(params) { equal(params.parent_id, '123'); ++parentcount; }, actions: { refreshParent: function() { this.refresh(); } } }); var childcount = 0; App.ParentChildRoute = Ember.Route.extend({ model: function() { ++childcount; } }); bootApplication(); equal(appcount, 1); equal(parentcount, 0); equal(childcount, 0); Ember.run(router, 'transitionTo', 'parent.child', '123'); equal(appcount, 1); equal(parentcount, 1); equal(childcount, 1); Ember.run(router, 'send', 'refreshParent'); equal(appcount, 1); equal(parentcount, 2); equal(childcount, 2); }); QUnit.test("Specifying non-existent controller name in route#render throws", function() { expect(1); Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ renderTemplate: function() { try { this.render('homepage', { controller: 'stefanpenneristhemanforme' }); } catch(e) { equal(e.message, "You passed `controller: 'stefanpenneristhemanforme'` into the `render` method, but no such controller could be found."); } } }); bootApplication(); }); QUnit.test("Redirecting with null model doesn't error out", function() { Router.map(function() { this.route("home", { path: '/' }); this.route("about", { path: '/about/:hurhurhur' }); }); App.HomeRoute = Ember.Route.extend({ beforeModel: function() { this.transitionTo('about', null); } }); App.AboutRoute = Ember.Route.extend({ serialize: function(model) { if (model === null) { return { hurhurhur: 'TreeklesMcGeekles' }; } } }); bootApplication(); equal(router.get('location.path'), "/about/TreeklesMcGeekles"); }); QUnit.test("rejecting the model hooks promise with a non-error prints the `message` property", function() { var rejectedMessage = 'OMG!! SOOOOOO BAD!!!!'; var rejectedStack = 'Yeah, buddy: stack gets printed too.'; Router.map(function() { this.route("yippie", { path: "/" }); }); Ember.Logger.error = function(initialMessage, errorMessage, errorStack) { equal(initialMessage, 'Error while processing route: yippie', 'a message with the current route name is printed'); equal(errorMessage, rejectedMessage, "the rejected reason's message property is logged"); equal(errorStack, rejectedStack, "the rejected reason's stack property is logged"); }; App.YippieRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject({ message: rejectedMessage, stack: rejectedStack }); } }); bootApplication(); }); QUnit.test("rejecting the model hooks promise with no reason still logs error", function() { Router.map(function() { this.route("wowzers", { path: "/" }); }); Ember.Logger.error = function(initialMessage) { equal(initialMessage, 'Error while processing route: wowzers', 'a message with the current route name is printed'); }; App.WowzersRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject(); } }); bootApplication(); }); QUnit.test("rejecting the model hooks promise with a string shows a good error", function() { var originalLoggerError = Ember.Logger.error; var rejectedMessage = "Supercalifragilisticexpialidocious"; Router.map(function() { this.route("yondo", { path: "/" }); }); Ember.Logger.error = function(initialMessage, errorMessage) { equal(initialMessage, 'Error while processing route: yondo', 'a message with the current route name is printed'); equal(errorMessage, rejectedMessage, "the rejected reason's message property is logged"); }; App.YondoRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject(rejectedMessage); } }); bootApplication(); Ember.Logger.error = originalLoggerError; }); QUnit.test("willLeave, willChangeContext, willChangeModel actions don't fire unless feature flag enabled", function() { expect(1); App.Router.map(function() { this.route('about'); }); function shouldNotFire() { ok(false, "this action shouldn't have been received"); } App.IndexRoute = Ember.Route.extend({ actions: { willChangeModel: shouldNotFire, willChangeContext: shouldNotFire, willLeave: shouldNotFire } }); App.AboutRoute = Ember.Route.extend({ setupController: function() { ok(true, "about route was entered"); } }); bootApplication(); Ember.run(router, 'transitionTo', 'about'); }); QUnit.test("Errors in transitionTo within redirect hook are logged", function() { expect(3); var actual = []; Router.map(function() { this.route('yondo', { path: "/" }); this.route('stink-bomb'); }); App.YondoRoute = Ember.Route.extend({ redirect: function() { this.transitionTo('stink-bomb', { something: 'goes boom' }); } }); Ember.Logger.error = function() { // push the arguments onto an array so we can detect if the error gets logged twice actual.push(arguments); }; bootApplication(); equal(actual.length, 1, 'the error is only logged once'); equal(actual[0][0], 'Error while processing route: yondo', 'source route is printed'); ok(actual[0][1].match(/More context objects were passed than there are dynamic segments for the route: stink-bomb/), 'the error is printed'); }); QUnit.test("Errors in transition show error template if available", function() { Ember.TEMPLATES.error = compile("
    Error!
    "); Router.map(function() { this.route('yondo', { path: "/" }); this.route('stink-bomb'); }); App.YondoRoute = Ember.Route.extend({ redirect: function() { this.transitionTo('stink-bomb', { something: 'goes boom' }); } }); bootApplication(); equal(Ember.$('#error').length, 1, "Error template was rendered."); }); QUnit.test("Route#resetController gets fired when changing models and exiting routes", function() { expect(4); Router.map(function() { this.resource("a", function() { this.resource("b", { path: '/b/:id' }, function() { }); this.resource("c", { path: '/c/:id' }, function() { }); }); this.route('out'); }); var calls = []; var SpyRoute = Ember.Route.extend({ setupController: function(controller, model, transition) { calls.push(['setup', this.routeName]); }, resetController: function(controller) { calls.push(['reset', this.routeName]); } }); App.ARoute = SpyRoute.extend(); App.BRoute = SpyRoute.extend(); App.CRoute = SpyRoute.extend(); App.OutRoute = SpyRoute.extend(); bootApplication(); deepEqual(calls, []); Ember.run(router, 'transitionTo', 'b', 'b-1'); deepEqual(calls, [['setup', 'a'], ['setup', 'b']]); calls.length = 0; Ember.run(router, 'transitionTo', 'c', 'c-1'); deepEqual(calls, [['reset', 'b'], ['setup', 'c']]); calls.length = 0; Ember.run(router, 'transitionTo', 'out'); deepEqual(calls, [['reset', 'c'], ['reset', 'a'], ['setup', 'out']]); }); QUnit.test("Exception during initialization of non-initial route is not swallowed", function() { Router.map(function() { this.route('boom'); }); App.BoomRoute = Ember.Route.extend({ init: function() { throw new Error("boom!"); } }); bootApplication(); throws(function() { Ember.run(router, 'transitionTo', 'boom'); }, /\bboom\b/); }); QUnit.test("Exception during load of non-initial route is not swallowed", function() { Router.map(function() { this.route('boom'); }); var lookup = container.lookup; container.lookup = function() { if (arguments[0] === 'route:boom') { throw new Error("boom!"); } return lookup.apply(this, arguments); }; App.BoomRoute = Ember.Route.extend({ init: function() { throw new Error("boom!"); } }); bootApplication(); throws(function() { Ember.run(router, 'transitionTo', 'boom'); }); }); QUnit.test("Exception during initialization of initial route is not swallowed", function() { Router.map(function() { this.route('boom', { path: '/' }); }); App.BoomRoute = Ember.Route.extend({ init: function() { throw new Error("boom!"); } }); throws(function() { bootApplication(); }, /\bboom\b/); }); QUnit.test("Exception during load of initial route is not swallowed", function() { Router.map(function() { this.route('boom', { path: '/' }); }); var lookup = container.lookup; container.lookup = function() { if (arguments[0] === 'route:boom') { throw new Error("boom!"); } return lookup.apply(this, arguments); }; App.BoomRoute = Ember.Route.extend({ init: function() { throw new Error("boom!"); } }); throws(function() { bootApplication(); }, /\bboom\b/); }); }); enifed('ember/tests/routing/basic_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests/routing'); test('ember/tests/routing/basic_test.js should pass jscs', function() { ok(true, 'ember/tests/routing/basic_test.js should pass jscs.'); }); }); enifed('ember/tests/routing/basic_test.jshint', function () { 'use strict'; module('JSHint - ember/tests/routing'); test('ember/tests/routing/basic_test.js should pass jshint', function() { ok(true, 'ember/tests/routing/basic_test.js should pass jshint.'); }); }); enifed('ember/tests/routing/query_params_test', ['ember', 'ember-metal/computed', 'ember-metal/platform/define_property', 'ember-runtime/system/string', 'ember-htmlbars/compat'], function (__dep0__, computed, define_property, string, EmberHandlebars) { 'use strict'; var compile = EmberHandlebars['default'].compile; var Router, App, router, registry, container; var get = Ember.get; function withoutMeta(object) { if (define_property.canDefineNonEnumerableProperties) { return object; } var newObject = Ember.$.extend(true, {}, object); delete newObject['__ember_meta__']; return newObject; } function bootApplication() { router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); } function handleURL(path) { return Ember.run(function() { return router.handleURL(path).then(function(value) { ok(true, 'url: `' + path + '` was handled'); return value; }, function(reason) { ok(false, 'failed to visit:`' + path + '` reason: `' + QUnit.jsDump.parse(reason)); throw reason; }); }); } var startingURL = ''; var expectedReplaceURL, expectedPushURL; function setAndFlush(obj, prop, value) { Ember.run(obj, 'set', prop, value); } var TestLocation = Ember.NoneLocation.extend({ initState: function() { this.set('path', startingURL); }, setURL: function(path) { if (expectedReplaceURL) { ok(false, "pushState occurred but a replaceState was expected"); } if (expectedPushURL) { equal(path, expectedPushURL, "an expected pushState occurred"); expectedPushURL = null; } this.set('path', path); }, replaceURL: function(path) { if (expectedPushURL) { ok(false, "replaceState occurred but a pushState was expected"); } if (expectedReplaceURL) { equal(path, expectedReplaceURL, "an expected replaceState occurred"); expectedReplaceURL = null; } this.set('path', path); } }); function sharedSetup() { Ember.run(function() { App = Ember.Application.create({ name: "App", rootElement: '#qunit-fixture' }); App.deferReadiness(); registry = App.registry; container = App.__container__; registry.register('location:test', TestLocation); startingURL = expectedReplaceURL = expectedPushURL = ''; App.Router.reopen({ location: 'test' }); Router = App.Router; App.LoadingRoute = Ember.Route.extend({ }); Ember.TEMPLATES.application = compile("{{outlet}}"); Ember.TEMPLATES.home = compile("

    Hours

    "); }); } function sharedTeardown() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; }); } QUnit.module("Routing w/ Query Params", { setup: function() { sharedSetup(); }, teardown: function() { sharedTeardown(); } }); QUnit.test("Single query params can be set", function() { Router.map(function() { this.route("home", { path: '/' }); }); App.HomeController = Ember.Controller.extend({ queryParams: ['foo'], foo: "123" }); bootApplication(); var controller = container.lookup('controller:home'); setAndFlush(controller, 'foo', '456'); equal(router.get('location.path'), "/?foo=456"); setAndFlush(controller, 'foo', '987'); equal(router.get('location.path'), "/?foo=987"); }); QUnit.test("Query params can map to different url keys", function() { App.IndexController = Ember.Controller.extend({ queryParams: [{ foo: 'other_foo', bar: { as: 'other_bar' } }], foo: "FOO", bar: "BAR" }); bootApplication(); equal(router.get('location.path'), ""); var controller = container.lookup('controller:index'); setAndFlush(controller, 'foo', 'LEX'); equal(router.get('location.path'), "/?other_foo=LEX"); setAndFlush(controller, 'foo', 'WOO'); equal(router.get('location.path'), "/?other_foo=WOO"); Ember.run(router, 'transitionTo', '/?other_foo=NAW'); equal(controller.get('foo'), "NAW"); setAndFlush(controller, 'bar', 'NERK'); Ember.run(router, 'transitionTo', '/?other_bar=NERK&other_foo=NAW'); }); QUnit.test("Routes have overridable serializeQueryParamKey hook", function() { App.IndexRoute = Ember.Route.extend({ serializeQueryParamKey: Ember.String.dasherize }); App.IndexController = Ember.Controller.extend({ queryParams: 'funTimes', funTimes: "" }); bootApplication(); equal(router.get('location.path'), ""); var controller = container.lookup('controller:index'); setAndFlush(controller, 'funTimes', 'woot'); equal(router.get('location.path'), "/?fun-times=woot"); }); QUnit.test("No replaceURL occurs on startup because default values don't show up in URL", function() { expect(0); App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: "123" }); expectedReplaceURL = "/?foo=123"; bootApplication(); }); QUnit.test("Can override inherited QP behavior by specifying queryParams as a computed property", function() { expect(0); var SharedMixin = Ember.Mixin.create({ queryParams: ['a'], a: 0 }); App.IndexController = Ember.Controller.extend(SharedMixin, { queryParams: computed.computed(function() { return ['c']; }), c: true }); bootApplication(); var indexController = container.lookup('controller:index'); expectedReplaceURL = "not gonna happen"; Ember.run(indexController, 'set', 'a', 1); }); QUnit.test("model hooks receives query params", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); App.IndexRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { omg: 'lol' }); } }); bootApplication(); equal(router.get('location.path'), ""); }); QUnit.test("controllers won't be eagerly instantiated by internal query params logic", function() { expect(10); Router.map(function() { this.resource('cats', function() { this.route('index', { path: '/' }); }); this.route("home", { path: '/' }); this.route("about"); }); Ember.TEMPLATES.home = compile("

    {{link-to 'About' 'about' (query-params lol='wat') id='link-to-about'}}

    "); Ember.TEMPLATES.about = compile("

    {{link-to 'Home' 'home' (query-params foo='naw')}}

    "); Ember.TEMPLATES['cats/index'] = compile("

    {{link-to 'Cats' 'cats' (query-params name='domino') id='cats-link'}}

    "); var homeShouldBeCreated = false; var aboutShouldBeCreated = false; var catsIndexShouldBeCreated = false; App.HomeRoute = Ember.Route.extend({ setup: function() { homeShouldBeCreated = true; this._super.apply(this, arguments); } }); App.HomeController = Ember.Controller.extend({ queryParams: ['foo'], foo: "123", init: function() { this._super.apply(this, arguments); ok(homeShouldBeCreated, "HomeController should be created at this time"); } }); App.AboutRoute = Ember.Route.extend({ setup: function() { aboutShouldBeCreated = true; this._super.apply(this, arguments); } }); App.AboutController = Ember.Controller.extend({ queryParams: ['lol'], lol: "haha", init: function() { this._super.apply(this, arguments); ok(aboutShouldBeCreated, "AboutController should be created at this time"); } }); App.CatsIndexRoute = Ember.Route.extend({ model: function() { return []; }, setup: function() { catsIndexShouldBeCreated = true; this._super.apply(this, arguments); }, setupController: function(controller, context) { controller.set('model', context); } }); App.CatsIndexController = Ember.Controller.extend({ queryParams: ['breed', 'name'], breed: 'Golden', name: null, init: function() { this._super.apply(this, arguments); ok(catsIndexShouldBeCreated, "CatsIndexController should be created at this time"); } }); bootApplication(); equal(router.get('location.path'), "", 'url is correct'); var controller = container.lookup('controller:home'); setAndFlush(controller, 'foo', '456'); equal(router.get('location.path'), "/?foo=456", 'url is correct'); equal(Ember.$('#link-to-about').attr('href'), "/about?lol=wat", "link to about is correct"); Ember.run(router, 'transitionTo', 'about'); equal(router.get('location.path'), "/about", 'url is correct'); Ember.run(router, 'transitionTo', 'cats'); equal(router.get('location.path'), "/cats", 'url is correct'); equal(Ember.$('#cats-link').attr('href'), "/cats?name=domino", "link to cats is correct"); Ember.run(Ember.$('#cats-link'), 'click'); equal(router.get('location.path'), "/cats?name=domino", 'url is correct'); }); QUnit.test("model hooks receives query params (overridden by incoming url value)", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); App.IndexRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { omg: 'yes' }); } }); startingURL = "/?omg=yes"; bootApplication(); equal(router.get('location.path'), "/?omg=yes"); }); QUnit.test("Route#paramsFor fetches query params", function() { expect(1); Router.map(function() { this.route('index', { path: '/:something' }); }); App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: 'fooapp' }); App.IndexRoute = Ember.Route.extend({ model: function(params, transition) { deepEqual(this.paramsFor('index'), { something: 'omg', foo: 'fooapp' }, "could retrieve params for index"); } }); startingURL = "/omg"; bootApplication(); }); QUnit.test("Route#paramsFor fetches falsy query params", function() { expect(1); App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: true }); App.IndexRoute = Ember.Route.extend({ model: function(params, transition) { equal(params.foo, false); } }); startingURL = "/?foo=false"; bootApplication(); }); QUnit.test("model hook can query prefix-less application params", function() { App.ApplicationController = Ember.Controller.extend({ queryParams: ['appomg'], appomg: 'applol' }); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); App.ApplicationRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { appomg: 'applol' }); } }); App.IndexRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { omg: 'lol' }); deepEqual(this.paramsFor('application'), { appomg: 'applol' }); } }); bootApplication(); equal(router.get('location.path'), ""); }); QUnit.test("model hook can query prefix-less application params (overridden by incoming url value)", function() { App.ApplicationController = Ember.Controller.extend({ queryParams: ['appomg'], appomg: 'applol' }); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); App.ApplicationRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { appomg: 'appyes' }); } }); App.IndexRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { omg: 'yes' }); deepEqual(this.paramsFor('application'), { appomg: 'appyes' }); } }); startingURL = "/?appomg=appyes&omg=yes"; bootApplication(); equal(router.get('location.path'), "/?appomg=appyes&omg=yes"); }); QUnit.test("can opt into full transition by setting refreshModel in route queryParams", function() { expect(6); App.ApplicationController = Ember.Controller.extend({ queryParams: ['appomg'], appomg: 'applol' }); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); var appModelCount = 0; App.ApplicationRoute = Ember.Route.extend({ model: function(params) { appModelCount++; } }); var indexModelCount = 0; App.IndexRoute = Ember.Route.extend({ queryParams: { omg: { refreshModel: true } }, model: function(params) { indexModelCount++; if (indexModelCount === 1) { deepEqual(params, { omg: 'lol' }); } else if (indexModelCount === 2) { deepEqual(params, { omg: 'lex' }); } } }); bootApplication(); equal(appModelCount, 1); equal(indexModelCount, 1); var indexController = container.lookup('controller:index'); setAndFlush(indexController, 'omg', 'lex'); equal(appModelCount, 1); equal(indexModelCount, 2); }); QUnit.test("Use Ember.get to retrieve query params 'refreshModel' configuration", function() { expect(6); App.ApplicationController = Ember.Controller.extend({ queryParams: ['appomg'], appomg: 'applol' }); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); var appModelCount = 0; App.ApplicationRoute = Ember.Route.extend({ model: function(params) { appModelCount++; } }); var indexModelCount = 0; App.IndexRoute = Ember.Route.extend({ queryParams: Ember.Object.create({ unknownProperty: function(keyName) { return { refreshModel: true }; } }), model: function(params) { indexModelCount++; if (indexModelCount === 1) { deepEqual(params, { omg: 'lol' }); } else if (indexModelCount === 2) { deepEqual(params, { omg: 'lex' }); } } }); bootApplication(); equal(appModelCount, 1); equal(indexModelCount, 1); var indexController = container.lookup('controller:index'); setAndFlush(indexController, 'omg', 'lex'); equal(appModelCount, 1); equal(indexModelCount, 2); }); QUnit.test("can use refreshModel even w URL changes that remove QPs from address bar", function() { expect(4); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); var indexModelCount = 0; App.IndexRoute = Ember.Route.extend({ queryParams: { omg: { refreshModel: true } }, model: function(params) { indexModelCount++; var data; if (indexModelCount === 1) { data = 'foo'; } else if (indexModelCount === 2) { data = 'lol'; } deepEqual(params, { omg: data }, "index#model receives right data"); } }); startingURL = '/?omg=foo'; bootApplication(); handleURL('/'); var indexController = container.lookup('controller:index'); equal(indexController.get('omg'), 'lol'); }); QUnit.test("warn user that routes query params configuration must be an Object, not an Array", function() { expect(1); App.ApplicationRoute = Ember.Route.extend({ queryParams: [ { commitBy: { replace: true } } ] }); expectAssertion(function() { bootApplication(); }, 'You passed in `[{"commitBy":{"replace":true}}]` as the value for `queryParams` but `queryParams` cannot be an Array'); }); QUnit.test("can opt into a replace query by specifying replace:true in the Router config hash", function() { expect(2); App.ApplicationController = Ember.Controller.extend({ queryParams: ['alex'], alex: 'matchneer' }); App.ApplicationRoute = Ember.Route.extend({ queryParams: { alex: { replace: true } } }); bootApplication(); equal(router.get('location.path'), ""); var appController = container.lookup('controller:application'); expectedReplaceURL = "/?alex=wallace"; setAndFlush(appController, 'alex', 'wallace'); }); QUnit.test("Route query params config can be configured using property name instead of URL key", function() { expect(2); App.ApplicationController = Ember.Controller.extend({ queryParams: [ { commitBy: 'commit_by' } ] }); App.ApplicationRoute = Ember.Route.extend({ queryParams: { commitBy: { replace: true } } }); bootApplication(); equal(router.get('location.path'), ""); var appController = container.lookup('controller:application'); expectedReplaceURL = "/?commit_by=igor_seb"; setAndFlush(appController, 'commitBy', 'igor_seb'); }); QUnit.test("An explicit replace:false on a changed QP always wins and causes a pushState", function() { expect(3); App.ApplicationController = Ember.Controller.extend({ queryParams: ['alex', 'steely'], alex: 'matchneer', steely: 'dan' }); App.ApplicationRoute = Ember.Route.extend({ queryParams: { alex: { replace: true }, steely: { replace: false } } }); bootApplication(); var appController = container.lookup('controller:application'); expectedPushURL = "/?alex=wallace&steely=jan"; Ember.run(appController, 'setProperties', { alex: 'wallace', steely: 'jan' }); expectedPushURL = "/?alex=wallace&steely=fran"; Ember.run(appController, 'setProperties', { steely: 'fran' }); expectedReplaceURL = "/?alex=sriracha&steely=fran"; Ember.run(appController, 'setProperties', { alex: 'sriracha' }); }); QUnit.test("can opt into full transition by setting refreshModel in route queryParams when transitioning from child to parent", function() { Ember.TEMPLATES.parent = compile('{{outlet}}'); Ember.TEMPLATES['parent/child'] = compile("{{link-to 'Parent' 'parent' (query-params foo='change') id='parent-link'}}"); App.Router.map(function() { this.resource('parent', function() { this.route('child'); }); }); var parentModelCount = 0; App.ParentRoute = Ember.Route.extend({ model: function() { parentModelCount++; }, queryParams: { foo: { refreshModel: true } } }); App.ParentController = Ember.Controller.extend({ queryParams: ['foo'], foo: 'abc' }); startingURL = '/parent/child?foo=lol'; bootApplication(); equal(parentModelCount, 1); container.lookup('controller:parent'); Ember.run(Ember.$('#parent-link'), 'click'); equal(parentModelCount, 2); }); QUnit.test("Use Ember.get to retrieve query params 'replace' configuration", function() { expect(2); App.ApplicationController = Ember.Controller.extend({ queryParams: ['alex'], alex: 'matchneer' }); App.ApplicationRoute = Ember.Route.extend({ queryParams: Ember.Object.create({ unknownProperty: function(keyName) { // We are simulating all qps requiring refresh return { replace: true }; } }) }); bootApplication(); equal(router.get('location.path'), ""); var appController = container.lookup('controller:application'); expectedReplaceURL = "/?alex=wallace"; setAndFlush(appController, 'alex', 'wallace'); }); QUnit.test("can override incoming QP values in setupController", function() { expect(3); App.Router.map(function() { this.route('about'); }); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); App.IndexRoute = Ember.Route.extend({ setupController: function(controller) { ok(true, "setupController called"); controller.set('omg', 'OVERRIDE'); }, actions: { queryParamsDidChange: function() { ok(false, "queryParamsDidChange shouldn't fire"); } } }); startingURL = "/about"; bootApplication(); equal(router.get('location.path'), "/about"); Ember.run(router, 'transitionTo', 'index'); equal(router.get('location.path'), "/?omg=OVERRIDE"); }); QUnit.test("can override incoming QP array values in setupController", function() { expect(3); App.Router.map(function() { this.route('about'); }); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: ['lol'] }); App.IndexRoute = Ember.Route.extend({ setupController: function(controller) { ok(true, "setupController called"); controller.set('omg', ['OVERRIDE']); }, actions: { queryParamsDidChange: function() { ok(false, "queryParamsDidChange shouldn't fire"); } } }); startingURL = "/about"; bootApplication(); equal(router.get('location.path'), "/about"); Ember.run(router, 'transitionTo', 'index'); equal(router.get('location.path'), "/?omg=" + encodeURIComponent(JSON.stringify(['OVERRIDE']))); }); QUnit.test("URL transitions that remove QPs still register as QP changes", function() { expect(2); App.IndexController = Ember.Controller.extend({ queryParams: ['omg'], omg: 'lol' }); startingURL = "/?omg=borf"; bootApplication(); var indexController = container.lookup('controller:index'); equal(indexController.get('omg'), 'borf'); Ember.run(router, 'transitionTo', '/'); equal(indexController.get('omg'), 'lol'); }); QUnit.test("Subresource naming style is supported", function() { Router.map(function() { this.resource('abc.def', { path: '/abcdef' }, function() { this.route('zoo'); }); }); Ember.TEMPLATES.application = compile("{{link-to 'A' 'abc.def' (query-params foo='123') id='one'}}{{link-to 'B' 'abc.def.zoo' (query-params foo='123' bar='456') id='two'}}{{outlet}}"); App.AbcDefController = Ember.Controller.extend({ queryParams: ['foo'], foo: 'lol' }); App.AbcDefZooController = Ember.Controller.extend({ queryParams: ['bar'], bar: 'haha' }); bootApplication(); equal(router.get('location.path'), ""); equal(Ember.$('#one').attr('href'), "/abcdef?foo=123"); equal(Ember.$('#two').attr('href'), "/abcdef/zoo?bar=456&foo=123"); Ember.run(Ember.$('#one'), 'click'); equal(router.get('location.path'), "/abcdef?foo=123"); Ember.run(Ember.$('#two'), 'click'); equal(router.get('location.path'), "/abcdef/zoo?bar=456&foo=123"); }); QUnit.test("transitionTo supports query params", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: 'lol' }); bootApplication(); equal(router.get('location.path'), ""); Ember.run(router, 'transitionTo', { queryParams: { foo: "borf" } }); equal(router.get('location.path'), "/?foo=borf", "shorthand supported"); Ember.run(router, 'transitionTo', { queryParams: { 'index:foo': "blaf" } }); equal(router.get('location.path'), "/?foo=blaf", "longform supported"); Ember.run(router, 'transitionTo', { queryParams: { 'index:foo': false } }); equal(router.get('location.path'), "/?foo=false", "longform supported (bool)"); Ember.run(router, 'transitionTo', { queryParams: { foo: false } }); equal(router.get('location.path'), "/?foo=false", "shorhand supported (bool)"); }); QUnit.test("transitionTo supports query params (multiple)", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo', 'bar'], foo: 'lol', bar: 'wat' }); bootApplication(); equal(router.get('location.path'), ""); Ember.run(router, 'transitionTo', { queryParams: { foo: "borf" } }); equal(router.get('location.path'), "/?foo=borf", "shorthand supported"); Ember.run(router, 'transitionTo', { queryParams: { 'index:foo': "blaf" } }); equal(router.get('location.path'), "/?foo=blaf", "longform supported"); Ember.run(router, 'transitionTo', { queryParams: { 'index:foo': false } }); equal(router.get('location.path'), "/?foo=false", "longform supported (bool)"); Ember.run(router, 'transitionTo', { queryParams: { foo: false } }); equal(router.get('location.path'), "/?foo=false", "shorhand supported (bool)"); }); QUnit.test("setting controller QP to empty string doesn't generate null in URL", function() { expect(1); App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: "123" }); bootApplication(); var controller = container.lookup('controller:index'); expectedPushURL = "/?foo="; setAndFlush(controller, 'foo', ''); }); QUnit.test("A default boolean value deserializes QPs as booleans rather than strings", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: false }); App.IndexRoute = Ember.Route.extend({ model: function(params) { equal(params.foo, true, "model hook received foo as boolean true"); } }); startingURL = "/?foo=true"; bootApplication(); var controller = container.lookup('controller:index'); equal(controller.get('foo'), true); handleURL('/?foo=false'); equal(controller.get('foo'), false); }); QUnit.test("Query param without value are empty string", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: '' }); startingURL = "/?foo="; bootApplication(); var controller = container.lookup('controller:index'); equal(controller.get('foo'), ""); }); QUnit.test("Array query params can be set", function() { Router.map(function() { this.route("home", { path: '/' }); }); App.HomeController = Ember.Controller.extend({ queryParams: ['foo'], foo: [] }); bootApplication(); var controller = container.lookup('controller:home'); setAndFlush(controller, 'foo', [1,2]); equal(router.get('location.path'), "/?foo=%5B1%2C2%5D"); setAndFlush(controller, 'foo', [3,4]); equal(router.get('location.path'), "/?foo=%5B3%2C4%5D"); }); QUnit.test("(de)serialization: arrays", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: [1] }); bootApplication(); equal(router.get('location.path'), ""); Ember.run(router, 'transitionTo', { queryParams: { foo: [2,3] } }); equal(router.get('location.path'), "/?foo=%5B2%2C3%5D", "shorthand supported"); Ember.run(router, 'transitionTo', { queryParams: { 'index:foo': [4,5] } }); equal(router.get('location.path'), "/?foo=%5B4%2C5%5D", "longform supported"); Ember.run(router, 'transitionTo', { queryParams: { foo: [] } }); equal(router.get('location.path'), "/?foo=%5B%5D", "longform supported"); }); QUnit.test("Url with array query param sets controller property to array", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: '' }); startingURL = "/?foo[]=1&foo[]=2&foo[]=3"; bootApplication(); var controller = container.lookup('controller:index'); deepEqual(controller.get('foo'), ["1","2","3"]); }); QUnit.test("Array query params can be pushed/popped", function() { Router.map(function() { this.route("home", { path: '/' }); }); App.HomeController = Ember.Controller.extend({ queryParams: ['foo'], foo: Ember.A([]) }); bootApplication(); equal(router.get('location.path'), ""); var controller = container.lookup('controller:home'); Ember.run(controller.foo, 'pushObject', 1); equal(router.get('location.path'), "/?foo=%5B1%5D"); deepEqual(controller.foo, [1]); Ember.run(controller.foo, 'popObject'); equal(router.get('location.path'), "/"); deepEqual(controller.foo, []); Ember.run(controller.foo, 'pushObject', 1); equal(router.get('location.path'), "/?foo=%5B1%5D"); deepEqual(controller.foo, [1]); Ember.run(controller.foo, 'popObject'); equal(router.get('location.path'), "/"); deepEqual(controller.foo, []); Ember.run(controller.foo, 'pushObject', 1); equal(router.get('location.path'), "/?foo=%5B1%5D"); deepEqual(controller.foo, [1]); Ember.run(controller.foo, 'pushObject', 2); equal(router.get('location.path'), "/?foo=%5B1%2C2%5D"); deepEqual(controller.foo, [1, 2]); Ember.run(controller.foo, 'popObject'); equal(router.get('location.path'), "/?foo=%5B1%5D"); deepEqual(controller.foo, [1]); Ember.run(controller.foo, 'unshiftObject', 'lol'); equal(router.get('location.path'), "/?foo=%5B%22lol%22%2C1%5D"); deepEqual(controller.foo, ['lol', 1]); }); QUnit.test("Overwriting with array with same content shouldn't refire update", function() { expect(3); var modelCount = 0; Router.map(function() { this.route("home", { path: '/' }); }); App.HomeRoute = Ember.Route.extend({ model: function() { modelCount++; } }); App.HomeController = Ember.Controller.extend({ queryParams: ['foo'], foo: Ember.A([1]) }); bootApplication(); equal(modelCount, 1); var controller = container.lookup('controller:home'); setAndFlush(controller, 'model', Ember.A([1])); equal(modelCount, 1); equal(router.get('location.path'), ""); }); QUnit.test("Defaulting to params hash as the model should not result in that params object being watched", function() { expect(1); Router.map(function() { this.route('other'); }); // This causes the params hash, which is returned as a route's // model if no other model could be resolved given the provided // params (and no custom model hook was defined), to be watched, // unless we return a copy of the params hash. App.ApplicationController = Ember.Controller.extend({ queryParams: ['woot'], woot: 'wat' }); App.OtherRoute = Ember.Route.extend({ model: function(p, trans) { var m = Ember.meta(trans.params.application); ok(!m.watching.woot, "A meta object isn't constructed for this params POJO"); } }); bootApplication(); Ember.run(router, 'transitionTo', 'other'); }); QUnit.test("A child of a resource route still defaults to parent route's model even if the child route has a query param", function() { expect(1); App.IndexController = Ember.Controller.extend({ queryParams: ['woot'] }); App.ApplicationRoute = Ember.Route.extend({ model: function(p, trans) { return { woot: true }; } }); App.IndexRoute = Ember.Route.extend({ setupController: function(controller, model) { deepEqual(withoutMeta(model), { woot: true }, "index route inherited model route from parent route"); } }); bootApplication(); }); QUnit.test("opting into replace does not affect transitions between routes", function() { expect(5); Ember.TEMPLATES.application = compile( "{{link-to 'Foo' 'foo' id='foo-link'}}" + "{{link-to 'Bar' 'bar' id='bar-no-qp-link'}}" + "{{link-to 'Bar' 'bar' (query-params raytiley='isanerd') id='bar-link'}}" + "{{outlet}}" ); App.Router.map(function() { this.route('foo'); this.route('bar'); }); App.BarController = Ember.Controller.extend({ queryParams: ['raytiley'], raytiley: 'isadork' }); App.BarRoute = Ember.Route.extend({ queryParams: { raytiley: { replace: true } } }); bootApplication(); var controller = container.lookup('controller:bar'); expectedPushURL = '/foo'; Ember.run(Ember.$('#foo-link'), 'click'); expectedPushURL = '/bar'; Ember.run(Ember.$('#bar-no-qp-link'), 'click'); expectedReplaceURL = '/bar?raytiley=boo'; setAndFlush(controller, 'raytiley', 'boo'); expectedPushURL = '/foo'; Ember.run(Ember.$('#foo-link'), 'click'); expectedPushURL = '/bar?raytiley=isanerd'; Ember.run(Ember.$('#bar-link'), 'click'); }); QUnit.test("Undefined isn't deserialized into a string", function() { expect(3); Router.map(function() { this.route("example"); }); Ember.TEMPLATES.application = compile("{{link-to 'Example' 'example' id='the-link'}}"); App.ExampleController = Ember.Controller.extend({ queryParams: ['foo'] // uncommon to not support default value, but should assume undefined. }); App.ExampleRoute = Ember.Route.extend({ model: function(params) { deepEqual(params, { foo: undefined }); } }); bootApplication(); var $link = Ember.$('#the-link'); equal($link.attr('href'), "/example"); Ember.run($link, 'click'); var controller = container.lookup('controller:example'); equal(get(controller, 'foo'), undefined); }); QUnit.test("query params have been set by the time setupController is called", function() { expect(1); App.ApplicationController = Ember.Controller.extend({ queryParams: ['foo'], foo: "wat" }); App.ApplicationRoute = Ember.Route.extend({ setupController: function(controller) { equal(controller.get('foo'), 'YEAH', "controller's foo QP property set before setupController called"); } }); startingURL = '/?foo=YEAH'; bootApplication(); }); var testParamlessLinks = function(routeName) { QUnit.test("param-less links in an app booted with query params in the URL don't reset the query params: " + routeName, function() { expect(1); Ember.TEMPLATES[routeName] = compile("{{link-to 'index' 'index' id='index-link'}}"); App[string.capitalize(routeName) + "Controller"] = Ember.Controller.extend({ queryParams: ['foo'], foo: "wat" }); startingURL = '/?foo=YEAH'; bootApplication(); equal(Ember.$('#index-link').attr('href'), '/?foo=YEAH'); }); }; testParamlessLinks('application'); testParamlessLinks('index'); QUnit.module("Model Dep Query Params", { setup: function() { sharedSetup(); App.Router.map(function() { this.resource('article', { path: '/a/:id' }, function() { this.resource('comments'); }); }); var articles = this.articles = Ember.A([{ id: 'a-1' }, { id: 'a-2' }, { id: 'a-3' }]); App.ApplicationController = Ember.Controller.extend({ articles: this.articles }); var self = this; App.ArticleRoute = Ember.Route.extend({ queryParams: {}, model: function(params) { if (self.expectedModelHookParams) { deepEqual(params, self.expectedModelHookParams, "the ArticleRoute model hook received the expected merged dynamic segment + query params hash"); self.expectedModelHookParams = null; } return articles.findProperty('id', params.id); } }); App.ArticleController = Ember.Controller.extend({ queryParams: ['q', 'z'], q: 'wat', z: 0 }); App.CommentsController = Ember.Controller.extend({ queryParams: 'page', page: 1 }); Ember.TEMPLATES.application = compile("{{#each a in articles}} {{link-to 'Article' 'article' a id=a.id}} {{/each}} {{outlet}}"); this.boot = function() { bootApplication(); self.$link1 = Ember.$('#a-1'); self.$link2 = Ember.$('#a-2'); self.$link3 = Ember.$('#a-3'); equal(self.$link1.attr('href'), '/a/a-1'); equal(self.$link2.attr('href'), '/a/a-2'); equal(self.$link3.attr('href'), '/a/a-3'); self.controller = container.lookup('controller:article'); }; }, teardown: function() { sharedTeardown(); ok(!this.expectedModelHookParams, "there should be no pending expectation of expected model hook params"); } }); QUnit.test("query params have 'model' stickiness by default", function() { this.boot(); Ember.run(this.$link1, 'click'); equal(router.get('location.path'), '/a/a-1'); setAndFlush(this.controller, 'q', 'lol'); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2'); equal(this.$link3.attr('href'), '/a/a-3'); Ember.run(this.$link2, 'click'); equal(this.controller.get('q'), 'wat'); equal(this.controller.get('z'), 0); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-2' }); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2'); equal(this.$link3.attr('href'), '/a/a-3'); }); QUnit.test("query params have 'model' stickiness by default (url changes)", function() { this.boot(); this.expectedModelHookParams = { id: 'a-1', q: 'lol', z: 0 }; handleURL('/a/a-1?q=lol'); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-1' }); equal(this.controller.get('q'), 'lol'); equal(this.controller.get('z'), 0); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2'); equal(this.$link3.attr('href'), '/a/a-3'); this.expectedModelHookParams = { id: 'a-2', q: 'lol', z: 0 }; handleURL('/a/a-2?q=lol'); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-2' }, "controller's model changed to a-2"); equal(this.controller.get('q'), 'lol'); equal(this.controller.get('z'), 0); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2?q=lol'); // fail equal(this.$link3.attr('href'), '/a/a-3'); this.expectedModelHookParams = { id: 'a-3', q: 'lol', z: 123 }; handleURL('/a/a-3?q=lol&z=123'); equal(this.controller.get('q'), 'lol'); equal(this.controller.get('z'), 123); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2?q=lol'); equal(this.$link3.attr('href'), '/a/a-3?q=lol&z=123'); }); QUnit.test("query params have 'model' stickiness by default (params-based transitions)", function() { Ember.TEMPLATES.application = compile("{{#each a in articles}} {{link-to 'Article' 'article' a.id id=a.id}} {{/each}}"); this.boot(); this.expectedModelHookParams = { id: 'a-1', q: 'wat', z: 0 }; Ember.run(router, 'transitionTo', 'article', 'a-1'); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-1' }); equal(this.controller.get('q'), 'wat'); equal(this.controller.get('z'), 0); equal(this.$link1.attr('href'), '/a/a-1'); equal(this.$link2.attr('href'), '/a/a-2'); equal(this.$link3.attr('href'), '/a/a-3'); this.expectedModelHookParams = { id: 'a-2', q: 'lol', z: 0 }; Ember.run(router, 'transitionTo', 'article', 'a-2', { queryParams: { q: 'lol' } }); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-2' }); equal(this.controller.get('q'), 'lol'); equal(this.controller.get('z'), 0); equal(this.$link1.attr('href'), '/a/a-1'); equal(this.$link2.attr('href'), '/a/a-2?q=lol'); equal(this.$link3.attr('href'), '/a/a-3'); this.expectedModelHookParams = { id: 'a-3', q: 'hay', z: 0 }; Ember.run(router, 'transitionTo', 'article', 'a-3', { queryParams: { q: 'hay' } }); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-3' }); equal(this.controller.get('q'), 'hay'); equal(this.controller.get('z'), 0); equal(this.$link1.attr('href'), '/a/a-1'); equal(this.$link2.attr('href'), '/a/a-2?q=lol'); equal(this.$link3.attr('href'), '/a/a-3?q=hay'); this.expectedModelHookParams = { id: 'a-2', q: 'lol', z: 1 }; Ember.run(router, 'transitionTo', 'article', 'a-2', { queryParams: { z: 1 } }); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-2' }); equal(this.controller.get('q'), 'lol'); equal(this.controller.get('z'), 1); equal(this.$link1.attr('href'), '/a/a-1'); equal(this.$link2.attr('href'), '/a/a-2?q=lol&z=1'); equal(this.$link3.attr('href'), '/a/a-3?q=hay'); }); QUnit.test("'controller' stickiness shares QP state between models", function() { App.ArticleController.reopen({ queryParams: { q: { scope: 'controller' } } }); this.boot(); Ember.run(this.$link1, 'click'); equal(router.get('location.path'), '/a/a-1'); setAndFlush(this.controller, 'q', 'lol'); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2?q=lol'); equal(this.$link3.attr('href'), '/a/a-3?q=lol'); Ember.run(this.$link2, 'click'); equal(this.controller.get('q'), 'lol'); equal(this.controller.get('z'), 0); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-2' }); equal(this.$link1.attr('href'), '/a/a-1?q=lol'); equal(this.$link2.attr('href'), '/a/a-2?q=lol'); equal(this.$link3.attr('href'), '/a/a-3?q=lol'); this.expectedModelHookParams = { id: 'a-3', q: 'haha', z: 123 }; handleURL('/a/a-3?q=haha&z=123'); deepEqual(withoutMeta(this.controller.get('model')), { id: 'a-3' }); equal(this.controller.get('q'), 'haha'); equal(this.controller.get('z'), 123); equal(this.$link1.attr('href'), '/a/a-1?q=haha'); equal(this.$link2.attr('href'), '/a/a-2?q=haha'); equal(this.$link3.attr('href'), '/a/a-3?q=haha&z=123'); setAndFlush(this.controller, 'q', 'woot'); equal(this.$link1.attr('href'), '/a/a-1?q=woot'); equal(this.$link2.attr('href'), '/a/a-2?q=woot'); equal(this.$link3.attr('href'), '/a/a-3?q=woot&z=123'); }); QUnit.test("'model' stickiness is scoped to current or first dynamic parent route", function() { this.boot(); Ember.run(router, 'transitionTo', 'comments', 'a-1'); var commentsCtrl = container.lookup('controller:comments'); equal(commentsCtrl.get('page'), 1); equal(router.get('location.path'), '/a/a-1/comments'); setAndFlush(commentsCtrl, 'page', 2); equal(router.get('location.path'), '/a/a-1/comments?page=2'); setAndFlush(commentsCtrl, 'page', 3); equal(router.get('location.path'), '/a/a-1/comments?page=3'); Ember.run(router, 'transitionTo', 'comments', 'a-2'); equal(commentsCtrl.get('page'), 1); equal(router.get('location.path'), '/a/a-2/comments'); Ember.run(router, 'transitionTo', 'comments', 'a-1'); equal(commentsCtrl.get('page'), 3); equal(router.get('location.path'), '/a/a-1/comments?page=3'); }); QUnit.test("can reset query params using the resetController hook", function() { App.Router.map(function() { this.resource('article', { path: '/a/:id' }, function() { this.resource('comments'); }); this.route('about'); }); App.ArticleRoute.reopen({ resetController: function(controller, isExiting) { this.controllerFor('comments').set('page', 1); if (isExiting) { controller.set('q', 'imdone'); } } }); Ember.TEMPLATES.about = compile("{{link-to 'A' 'comments' 'a-1' id='one'}} {{link-to 'B' 'comments' 'a-2' id='two'}}"); this.boot(); Ember.run(router, 'transitionTo', 'comments', 'a-1'); var commentsCtrl = container.lookup('controller:comments'); equal(commentsCtrl.get('page'), 1); equal(router.get('location.path'), '/a/a-1/comments'); setAndFlush(commentsCtrl, 'page', 2); equal(router.get('location.path'), '/a/a-1/comments?page=2'); Ember.run(router, 'transitionTo', 'comments', 'a-2'); equal(commentsCtrl.get('page'), 1); equal(this.controller.get('q'), 'wat'); Ember.run(router, 'transitionTo', 'comments', 'a-1'); equal(router.get('location.path'), '/a/a-1/comments'); equal(commentsCtrl.get('page'), 1); Ember.run(router, 'transitionTo', 'about'); equal(Ember.$('#one').attr('href'), "/a/a-1/comments?q=imdone"); equal(Ember.$('#two').attr('href'), "/a/a-2/comments"); }); QUnit.test("can unit test without bucket cache", function() { var controller = container.lookup('controller:article'); controller._bucketCache = null; controller.set('q', "i used to break"); equal(controller.get('q'), "i used to break"); }); QUnit.module("Query Params - overlapping query param property names", { setup: function() { sharedSetup(); App.Router.map(function() { this.resource('parent', function() { this.route('child'); }); }); this.boot = function() { bootApplication(); Ember.run(router, 'transitionTo', 'parent.child'); }; }, teardown: function() { sharedTeardown(); } }); QUnit.test("can remap same-named qp props", function() { App.ParentController = Ember.Controller.extend({ queryParams: { page: 'parentPage' }, page: 1 }); App.ParentChildController = Ember.Controller.extend({ queryParams: { page: 'childPage' }, page: 1 }); this.boot(); equal(router.get('location.path'), '/parent/child'); var parentController = container.lookup('controller:parent'); var parentChildController = container.lookup('controller:parent.child'); setAndFlush(parentController, 'page', 2); equal(router.get('location.path'), '/parent/child?parentPage=2'); setAndFlush(parentController, 'page', 1); equal(router.get('location.path'), '/parent/child'); setAndFlush(parentChildController, 'page', 2); equal(router.get('location.path'), '/parent/child?childPage=2'); setAndFlush(parentChildController, 'page', 1); equal(router.get('location.path'), '/parent/child'); Ember.run(function() { parentController.set('page', 2); parentChildController.set('page', 2); }); equal(router.get('location.path'), '/parent/child?childPage=2&parentPage=2'); Ember.run(function() { parentController.set('page', 1); parentChildController.set('page', 1); }); equal(router.get('location.path'), '/parent/child'); }); QUnit.test("query params in the same route hierarchy with the same url key get auto-scoped", function() { App.ParentController = Ember.Controller.extend({ queryParams: { foo: 'shared' }, foo: 1 }); App.ParentChildController = Ember.Controller.extend({ queryParams: { bar: 'shared' }, bar: 1 }); var self = this; expectAssertion(function() { self.boot(); }, "You're not allowed to have more than one controller property map to the same query param key, but both `parent:foo` and `parent.child:bar` map to `shared`. You can fix this by mapping one of the controller properties to a different query param key via the `as` config option, e.g. `foo: { as: 'other-foo' }`"); }); QUnit.test("Support shared but overridable mixin pattern", function() { var HasPage = Ember.Mixin.create({ queryParams: 'page', page: 1 }); App.ParentController = Ember.Controller.extend(HasPage, { queryParams: { page: 'yespage' } }); App.ParentChildController = Ember.Controller.extend(HasPage); this.boot(); equal(router.get('location.path'), '/parent/child'); var parentController = container.lookup('controller:parent'); var parentChildController = container.lookup('controller:parent.child'); setAndFlush(parentChildController, 'page', 2); equal(router.get('location.path'), '/parent/child?page=2'); equal(parentController.get('page'), 1); equal(parentChildController.get('page'), 2); setAndFlush(parentController, 'page', 2); equal(router.get('location.path'), '/parent/child?page=2&yespage=2'); equal(parentController.get('page'), 2); equal(parentChildController.get('page'), 2); }); }); enifed('ember/tests/routing/query_params_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests/routing'); test('ember/tests/routing/query_params_test.js should pass jscs', function() { ok(true, 'ember/tests/routing/query_params_test.js should pass jscs.'); }); }); enifed('ember/tests/routing/query_params_test.jshint', function () { 'use strict'; module('JSHint - ember/tests/routing'); test('ember/tests/routing/query_params_test.js should pass jshint', function() { ok(true, 'ember/tests/routing/query_params_test.js should pass jshint.'); }); }); enifed('ember/tests/routing/router_map_test', ['ember', 'ember-template-compiler/system/compile'], function (__dep0__, compile) { 'use strict'; var Router, router, App, container; function bootApplication() { router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); } function handleURL(path) { return Ember.run(function() { return router.handleURL(path).then(function(value) { ok(true, 'url: `' + path + '` was handled'); return value; }, function(reason) { ok(false, 'failed to visit:`' + path + '` reason: `' + QUnit.jsDump.parse(reason)); throw reason; }); }); } QUnit.module("Router.map", { setup: function() { Ember.run(function() { App = Ember.Application.create({ name: "App", rootElement: '#qunit-fixture' }); App.deferReadiness(); App.Router.reopen({ location: 'none' }); Router = App.Router; container = App.__container__; }); }, teardown: function() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; //Ember.Logger.error = originalLoggerError; }); } }); QUnit.test("Router.map returns an Ember Router class", function () { expect(1); var ret = App.Router.map(function() { this.route('hello'); }); ok(Ember.Router.detect(ret)); }); QUnit.test("Router.map can be called multiple times", function () { expect(4); Ember.TEMPLATES.hello = compile['default']("Hello!"); Ember.TEMPLATES.goodbye = compile['default']("Goodbye!"); App.Router.map(function() { this.route('hello'); }); App.Router.map(function() { this.route('goodbye'); }); bootApplication(); handleURL('/hello'); equal(Ember.$('#qunit-fixture').text(), "Hello!", "The hello template was rendered"); handleURL('/goodbye'); equal(Ember.$('#qunit-fixture').text(), "Goodbye!", "The goodbye template was rendered"); }); }); enifed('ember/tests/routing/router_map_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests/routing'); test('ember/tests/routing/router_map_test.js should pass jscs', function() { ok(true, 'ember/tests/routing/router_map_test.js should pass jscs.'); }); }); enifed('ember/tests/routing/router_map_test.jshint', function () { 'use strict'; module('JSHint - ember/tests/routing'); test('ember/tests/routing/router_map_test.js should pass jshint', function() { ok(true, 'ember/tests/routing/router_map_test.js should pass jshint.'); }); }); enifed('ember/tests/routing/substates_test', ['ember', 'ember-htmlbars/compat'], function (__dep0__, EmberHandlebars) { 'use strict'; var compile = EmberHandlebars['default'].compile; var Router, App, templates, router, container, counter; function step(expectedValue, description) { equal(counter, expectedValue, "Step " + expectedValue + ": " + description); counter++; } function bootApplication(startingURL) { for (var name in templates) { Ember.TEMPLATES[name] = compile(templates[name]); } if (startingURL) { Ember.NoneLocation.reopen({ path: startingURL }); } startingURL = startingURL || ''; router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); } QUnit.module("Loading/Error Substates", { setup: function() { counter = 1; Ember.run(function() { App = Ember.Application.create({ name: "App", rootElement: '#qunit-fixture' }); App.deferReadiness(); App.Router.reopen({ location: 'none' }); Router = App.Router; container = App.__container__; templates = { application: '
    {{outlet}}
    ', index: 'INDEX', loading: 'LOADING', bro: 'BRO', sis: 'SIS' }; }); }, teardown: function() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; }); Ember.NoneLocation.reopen({ path: '' }); } }); QUnit.test("Slow promise from a child route of application enters nested loading state", function() { var broModel = {}; var broDeferred = Ember.RSVP.defer(); Router.map(function() { this.route('bro'); }); App.ApplicationRoute = Ember.Route.extend({ setupController: function() { step(2, "ApplicationRoute#setup"); } }); App.BroRoute = Ember.Route.extend({ model: function() { step(1, "BroRoute#model"); return broDeferred.promise; } }); bootApplication('/bro'); equal(Ember.$('#app', '#qunit-fixture').text(), "LOADING", "The Loading template is nested in application template's outlet"); Ember.run(broDeferred, 'resolve', broModel); equal(Ember.$('#app', '#qunit-fixture').text(), "BRO", "bro template has loaded and replaced loading template"); }); QUnit.test("Slow promises waterfall on startup", function() { expect(7); var grandmaDeferred = Ember.RSVP.defer(); var sallyDeferred = Ember.RSVP.defer(); Router.map(function() { this.resource('grandma', function() { this.resource('mom', function() { this.route('sally'); }); }); }); templates.grandma = "GRANDMA {{outlet}}"; templates.mom = "MOM {{outlet}}"; templates['mom/loading'] = "MOMLOADING"; templates['mom/sally'] = "SALLY"; App.GrandmaRoute = Ember.Route.extend({ model: function() { step(1, "GrandmaRoute#model"); return grandmaDeferred.promise; } }); App.MomRoute = Ember.Route.extend({ model: function() { step(2, "Mom#model"); return {}; } }); App.MomSallyRoute = Ember.Route.extend({ model: function() { step(3, "SallyRoute#model"); return sallyDeferred.promise; }, setupController: function() { step(4, "SallyRoute#setupController"); } }); bootApplication('/grandma/mom/sally'); equal(Ember.$('#app', '#qunit-fixture').text(), "LOADING", "The Loading template is nested in application template's outlet"); Ember.run(grandmaDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "GRANDMA MOM MOMLOADING", "Mom's child loading route is displayed due to sally's slow promise"); Ember.run(sallyDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "GRANDMA MOM SALLY", "Sally template displayed"); }); QUnit.test("ApplicationRoute#currentPath reflects loading state path", function() { expect(4); var momDeferred = Ember.RSVP.defer(); Router.map(function() { this.resource('grandma', function() { this.route('mom'); }); }); templates.grandma = "GRANDMA {{outlet}}"; templates['grandma/loading'] = "GRANDMALOADING"; templates['grandma/mom'] = "MOM"; App.GrandmaMomRoute = Ember.Route.extend({ model: function() { return momDeferred.promise; } }); bootApplication('/grandma/mom'); equal(Ember.$('#app', '#qunit-fixture').text(), "GRANDMA GRANDMALOADING"); var appController = container.lookup('controller:application'); equal(appController.get('currentPath'), "grandma.loading", "currentPath reflects loading state"); Ember.run(momDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "GRANDMA MOM"); equal(appController.get('currentPath'), "grandma.mom", "currentPath reflects final state"); }); QUnit.test("Slow promises returned from ApplicationRoute#model don't enter LoadingRoute", function() { expect(2); var appDeferred = Ember.RSVP.defer(); App.ApplicationRoute = Ember.Route.extend({ model: function() { return appDeferred.promise; } }); App.LoadingRoute = Ember.Route.extend({ setupController: function() { ok(false, "shouldn't get here"); } }); bootApplication(); equal(Ember.$('#app', '#qunit-fixture').text(), "", "nothing has been rendered yet"); Ember.run(appDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "INDEX"); }); QUnit.test("Don't enter loading route unless either route or template defined", function() { delete templates.loading; expect(2); var indexDeferred = Ember.RSVP.defer(); App.ApplicationController = Ember.Controller.extend(); App.IndexRoute = Ember.Route.extend({ model: function() { return indexDeferred.promise; } }); bootApplication(); var appController = container.lookup('controller:application'); ok(appController.get('currentPath') !== "loading", "loading state not entered"); Ember.run(indexDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "INDEX"); }); QUnit.test("Enter loading route if only LoadingRoute defined", function() { delete templates.loading; expect(4); var indexDeferred = Ember.RSVP.defer(); App.IndexRoute = Ember.Route.extend({ model: function() { step(1, "IndexRoute#model"); return indexDeferred.promise; } }); App.LoadingRoute = Ember.Route.extend({ setupController: function() { step(2, "LoadingRoute#setupController"); } }); bootApplication(); var appController = container.lookup('controller:application'); equal(appController.get('currentPath'), "loading", "loading state entered"); Ember.run(indexDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "INDEX"); }); QUnit.test("Enter child loading state of pivot route", function() { expect(4); var deferred = Ember.RSVP.defer(); Router.map(function() { this.resource('grandma', function() { this.resource('mom', function() { this.route('sally'); }); this.route('smells'); }); }); templates['grandma/loading'] = "GMONEYLOADING"; App.ApplicationController = Ember.Controller.extend(); App.MomSallyRoute = Ember.Route.extend({ setupController: function() { step(1, "SallyRoute#setupController"); } }); App.GrandmaSmellsRoute = Ember.Route.extend({ model: function() { return deferred.promise; } }); bootApplication('/grandma/mom/sally'); var appController = container.lookup('controller:application'); equal(appController.get('currentPath'), "grandma.mom.sally", "Initial route fully loaded"); Ember.run(router, 'transitionTo', 'grandma.smells'); equal(appController.get('currentPath'), "grandma.loading", "in pivot route's child loading state"); Ember.run(deferred, 'resolve', {}); equal(appController.get('currentPath'), "grandma.smells", "Finished transition"); }); QUnit.test("Loading actions bubble to root, but don't enter substates above pivot", function() { expect(6); delete templates.loading; var sallyDeferred = Ember.RSVP.defer(); var smellsDeferred = Ember.RSVP.defer(); Router.map(function() { this.resource('grandma', function() { this.resource('mom', function() { this.route('sally'); }); this.route('smells'); }); }); App.ApplicationController = Ember.Controller.extend(); App.ApplicationRoute = Ember.Route.extend({ actions: { loading: function(transition, route) { ok(true, "loading action received on ApplicationRoute"); } } }); App.MomSallyRoute = Ember.Route.extend({ model: function() { return sallyDeferred.promise; } }); App.GrandmaSmellsRoute = Ember.Route.extend({ model: function() { return smellsDeferred.promise; } }); bootApplication('/grandma/mom/sally'); var appController = container.lookup('controller:application'); ok(!appController.get('currentPath'), "Initial route fully loaded"); Ember.run(sallyDeferred, 'resolve', {}); equal(appController.get('currentPath'), "grandma.mom.sally", "transition completed"); Ember.run(router, 'transitionTo', 'grandma.smells'); equal(appController.get('currentPath'), "grandma.mom.sally", "still in initial state because the only loading state is above the pivot route"); Ember.run(smellsDeferred, 'resolve', {}); equal(appController.get('currentPath'), "grandma.smells", "Finished transition"); }); QUnit.test("Default error event moves into nested route", function() { expect(5); templates['grandma'] = "GRANDMA {{outlet}}"; templates['grandma/error'] = "ERROR: {{model.msg}}"; Router.map(function() { this.resource('grandma', function() { this.resource('mom', function() { this.route('sally'); }); }); }); App.ApplicationController = Ember.Controller.extend(); App.MomSallyRoute = Ember.Route.extend({ model: function() { step(1, "MomSallyRoute#model"); return Ember.RSVP.reject({ msg: "did it broke?" }); }, actions: { error: function() { step(2, "MomSallyRoute#actions.error"); return true; } } }); bootApplication('/grandma/mom/sally'); step(3, "App finished booting"); equal(Ember.$('#app', '#qunit-fixture').text(), "GRANDMA ERROR: did it broke?", "error bubbles"); var appController = container.lookup('controller:application'); equal(appController.get('currentPath'), 'grandma.error', "Initial route fully loaded"); }); QUnit.test("Slow promises returned from ApplicationRoute#model enter ApplicationLoadingRoute if present", function() { expect(2); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; var appDeferred = Ember.RSVP.defer(); App.ApplicationRoute = Ember.Route.extend({ model: function() { return appDeferred.promise; } }); var loadingRouteEntered = false; App.ApplicationLoadingRoute = Ember.Route.extend({ setupController: function() { loadingRouteEntered = true; } }); bootApplication(); ok(loadingRouteEntered, "ApplicationLoadingRoute was entered"); Ember.run(appDeferred, 'resolve', {}); equal(Ember.$('#app', '#qunit-fixture').text(), "INDEX"); }); QUnit.test("Slow promises returned from ApplicationRoute#model enter application_loading if template present", function() { expect(3); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['application_loading'] = 'TOPLEVEL LOADING'; var appDeferred = Ember.RSVP.defer(); App.ApplicationRoute = Ember.Route.extend({ model: function() { return appDeferred.promise; } }); var loadingRouteEntered = false; App.ApplicationLoadingRoute = Ember.Route.extend({ setupController: function() { loadingRouteEntered = true; } }); App.ApplicationLoadingView = Ember.View.extend({ elementId: 'toplevel-loading' }); bootApplication(); equal(Ember.$('#qunit-fixture > #toplevel-loading').text(), "TOPLEVEL LOADING"); Ember.run(appDeferred, 'resolve', {}); equal(Ember.$('#toplevel-loading', '#qunit-fixture').length, 0, 'top-level loading View has been entirely removed from DOM'); equal(Ember.$('#app', '#qunit-fixture').text(), "INDEX"); }); QUnit.test("Default error event moves into nested route, prioritizing more specifically named error route", function() { expect(5); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['grandma'] = "GRANDMA {{outlet}}"; templates['grandma/error'] = "ERROR: {{model.msg}}"; templates['grandma/mom_error'] = "MOM ERROR: {{model.msg}}"; Router.map(function() { this.resource('grandma', function() { this.resource('mom', function() { this.route('sally'); }); }); }); App.ApplicationController = Ember.Controller.extend(); App.MomSallyRoute = Ember.Route.extend({ model: function() { step(1, "MomSallyRoute#model"); return Ember.RSVP.reject({ msg: "did it broke?" }); }, actions: { error: function() { step(2, "MomSallyRoute#actions.error"); return true; } } }); bootApplication('/grandma/mom/sally'); step(3, "App finished booting"); equal(Ember.$('#app', '#qunit-fixture').text(), "GRANDMA MOM ERROR: did it broke?", "the more specifically-named mom error substate was entered over the other error route"); var appController = container.lookup('controller:application'); equal(appController.get('currentPath'), 'grandma.mom_error', "Initial route fully loaded"); }); QUnit.test("Prioritized substate entry works with preserved-namespace nested resources", function() { expect(2); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['foo/bar_loading'] = "FOOBAR LOADING"; templates['foo/bar/index'] = "YAY"; Router.map(function() { this.resource('foo', function() { this.resource('foo.bar', { path: '/bar' }, function() { }); }); }); App.ApplicationController = Ember.Controller.extend(); var deferred = Ember.RSVP.defer(); App.FooBarRoute = Ember.Route.extend({ model: function() { return deferred.promise; } }); bootApplication('/foo/bar'); equal(Ember.$('#app', '#qunit-fixture').text(), "FOOBAR LOADING", "foo.bar_loading was entered (as opposed to something like foo/foo/bar_loading)"); Ember.run(deferred, 'resolve'); equal(Ember.$('#app', '#qunit-fixture').text(), "YAY"); }); QUnit.test("Prioritized loading substate entry works with preserved-namespace nested routes", function() { expect(2); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['foo/bar_loading'] = "FOOBAR LOADING"; templates['foo/bar'] = "YAY"; Router.map(function() { this.route('foo', function() { this.route('bar'); }); }); App.ApplicationController = Ember.Controller.extend(); var deferred = Ember.RSVP.defer(); App.FooBarRoute = Ember.Route.extend({ model: function() { return deferred.promise; } }); bootApplication('/foo/bar'); equal(Ember.$('#app', '#qunit-fixture').text(), "FOOBAR LOADING", "foo.bar_loading was entered (as opposed to something like foo/foo/bar_loading)"); Ember.run(deferred, 'resolve'); equal(Ember.$('#app', '#qunit-fixture').text(), "YAY"); }); QUnit.test("Prioritized error substate entry works with preserved-namespace nested routes", function() { expect(1); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['foo/bar_error'] = "FOOBAR ERROR: {{model.msg}}"; templates['foo/bar'] = "YAY"; Router.map(function() { this.route('foo', function() { this.route('bar'); }); }); App.ApplicationController = Ember.Controller.extend(); App.FooBarRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject({ msg: "did it broke?" }); } }); bootApplication('/foo/bar'); equal(Ember.$('#app', '#qunit-fixture').text(), "FOOBAR ERROR: did it broke?", "foo.bar_error was entered (as opposed to something like foo/foo/bar_error)"); }); QUnit.test("Prioritized loading substate entry works with auto-generated index routes", function() { expect(2); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['foo/index_loading'] = "FOO LOADING"; templates['foo/index'] = "YAY"; templates['foo'] = "{{outlet}}"; Router.map(function() { this.resource('foo', function() { this.route('bar'); }); }); App.ApplicationController = Ember.Controller.extend(); var deferred = Ember.RSVP.defer(); App.FooIndexRoute = Ember.Route.extend({ model: function() { return deferred.promise; } }); App.FooRoute = Ember.Route.extend({ model: function() { return true; } }); bootApplication('/foo'); equal(Ember.$('#app', '#qunit-fixture').text(), "FOO LOADING", "foo.index_loading was entered"); Ember.run(deferred, 'resolve'); equal(Ember.$('#app', '#qunit-fixture').text(), "YAY"); }); QUnit.test("Prioritized error substate entry works with auto-generated index routes", function() { expect(1); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['foo/index_error'] = "FOO ERROR: {{model.msg}}"; templates['foo/index'] = "YAY"; templates['foo'] = "{{outlet}}"; Router.map(function() { this.resource('foo', function() { this.route('bar'); }); }); App.ApplicationController = Ember.Controller.extend(); App.FooIndexRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject({ msg: "did it broke?" }); } }); App.FooRoute = Ember.Route.extend({ model: function() { return true; } }); bootApplication('/foo'); equal(Ember.$('#app', '#qunit-fixture').text(), "FOO ERROR: did it broke?", "foo.index_error was entered"); }); QUnit.test("Rejected promises returned from ApplicationRoute transition into top-level application_error", function() { expect(2); // fake a modules resolver App.registry.resolver.moduleBasedResolver = true; templates['application_error'] = '

    TOPLEVEL ERROR: {{model.msg}}

    '; var reject = true; App.ApplicationRoute = Ember.Route.extend({ model: function() { if (reject) { return Ember.RSVP.reject({ msg: "BAD NEWS BEARS" }); } else { return {}; } } }); bootApplication(); equal(Ember.$('#toplevel-error', '#qunit-fixture').text(), "TOPLEVEL ERROR: BAD NEWS BEARS"); reject = false; Ember.run(router, 'transitionTo', 'index'); equal(Ember.$('#app', '#qunit-fixture').text(), "INDEX"); }); }); enifed('ember/tests/routing/substates_test.jscs-test', function () { 'use strict'; module('JSCS - ember/tests/routing'); test('ember/tests/routing/substates_test.js should pass jscs', function() { ok(true, 'ember/tests/routing/substates_test.js should pass jscs.'); }); }); enifed('ember/tests/routing/substates_test.jshint', function () { 'use strict'; module('JSHint - ember/tests/routing'); test('ember/tests/routing/substates_test.js should pass jshint', function() { ok(true, 'ember/tests/routing/substates_test.js should pass jshint.'); }); }); enifed("htmlbars-util", ["./htmlbars-util/safe-string","./htmlbars-util/handlebars/utils","./htmlbars-util/namespaces","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var SafeString = __dependency1__["default"]; var escapeExpression = __dependency2__.escapeExpression; var getAttrNamespace = __dependency3__.getAttrNamespace; __exports__.SafeString = SafeString; __exports__.escapeExpression = escapeExpression; __exports__.getAttrNamespace = getAttrNamespace; }); enifed("htmlbars-util/array-utils", ["exports"], function(__exports__) { "use strict"; function forEach(array, callback, binding) { var i, l; if (binding === undefined) { for (i = 0, l = array.length; i < l; i++) { callback(array[i], i, array); } } else { for (i = 0, l = array.length; i < l; i++) { callback.call(binding, array[i], i, array); } } } __exports__.forEach = forEach;function map(array, callback) { var output = []; var i, l; for (i = 0, l = array.length; i < l; i++) { output.push(callback(array[i], i, array)); } return output; } __exports__.map = map;var getIdx; if (Array.prototype.indexOf) { getIdx = function(array, obj, from){ return array.indexOf(obj, from); }; } else { getIdx = function(array, obj, from) { if (from === undefined || from === null) { from = 0; } else if (from < 0) { from = Math.max(0, array.length + from); } for (var i = from, l= array.length; i < l; i++) { if (array[i] === obj) { return i; } } return -1; }; } var indexOfArray = getIdx; __exports__.indexOfArray = indexOfArray; }); enifed("htmlbars-util/handlebars/safe-string", ["exports"], function(__exports__) { "use strict"; // Build out our basic SafeString type function SafeString(string) { this.string = string; } SafeString.prototype.toString = SafeString.prototype.toHTML = function() { return "" + this.string; }; __exports__["default"] = SafeString; }); enifed("htmlbars-util/handlebars/utils", ["./safe-string","exports"], function(__dependency1__, __exports__) { "use strict"; /*jshint -W004 */ var SafeString = __dependency1__["default"]; var escape = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "`": "`" }; var badChars = /[&<>"'`]/g; var possible = /[&<>"'`]/; function escapeChar(chr) { return escape[chr]; } function extend(obj /* , ...source */) { for (var i = 1; i < arguments.length; i++) { for (var key in arguments[i]) { if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { obj[key] = arguments[i][key]; } } } return obj; } __exports__.extend = extend;var toString = Object.prototype.toString; __exports__.toString = toString; // Sourced from lodash // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt var isFunction = function(value) { return typeof value === 'function'; }; // fallback for older versions of Chrome and Safari /* istanbul ignore next */ if (isFunction(/x/)) { isFunction = function(value) { return typeof value === 'function' && toString.call(value) === '[object Function]'; }; } var isFunction; __exports__.isFunction = isFunction; /* istanbul ignore next */ var isArray = Array.isArray || function(value) { return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; }; __exports__.isArray = isArray; function escapeExpression(string) { // don't escape SafeStrings, since they're already safe if (string && string.toHTML) { return string.toHTML(); } else if (string == null) { return ""; } else if (!string) { return string + ''; } // Force a string conversion as this will be done by the append regardless and // the regex test will do this transparently behind the scenes, causing issues if // an object's to string has escaped characters in it. string = "" + string; if(!possible.test(string)) { return string; } return string.replace(badChars, escapeChar); } __exports__.escapeExpression = escapeExpression;function isEmpty(value) { if (!value && value !== 0) { return true; } else if (isArray(value) && value.length === 0) { return true; } else { return false; } } __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { return (contextPath ? contextPath + '.' : '') + id; } __exports__.appendContextPath = appendContextPath; }); enifed("htmlbars-util/namespaces", ["exports"], function(__exports__) { "use strict"; // ref http://dev.w3.org/html5/spec-LC/namespaces.html var defaultNamespaces = { html: 'http://www.w3.org/1999/xhtml', mathml: 'http://www.w3.org/1998/Math/MathML', svg: 'http://www.w3.org/2000/svg', xlink: 'http://www.w3.org/1999/xlink', xml: 'http://www.w3.org/XML/1998/namespace' }; function getAttrNamespace(attrName) { var namespace; var colonIndex = attrName.indexOf(':'); if (colonIndex !== -1) { var prefix = attrName.slice(0, colonIndex); namespace = defaultNamespaces[prefix]; } return namespace || null; } __exports__.getAttrNamespace = getAttrNamespace; }); enifed("htmlbars-util/object-utils", ["exports"], function(__exports__) { "use strict"; function merge(options, defaults) { for (var prop in defaults) { if (options.hasOwnProperty(prop)) { continue; } options[prop] = defaults[prop]; } return options; } __exports__.merge = merge; }); enifed("htmlbars-util/quoting", ["exports"], function(__exports__) { "use strict"; function escapeString(str) { str = str.replace(/\\/g, "\\\\"); str = str.replace(/"/g, '\\"'); str = str.replace(/\n/g, "\\n"); return str; } __exports__.escapeString = escapeString; function string(str) { return '"' + escapeString(str) + '"'; } __exports__.string = string; function array(a) { return "[" + a + "]"; } __exports__.array = array; function hash(pairs) { return "{" + pairs.join(", ") + "}"; } __exports__.hash = hash;function repeat(chars, times) { var str = ""; while (times--) { str += chars; } return str; } __exports__.repeat = repeat; }); enifed("htmlbars-util/safe-string", ["./handlebars/safe-string","exports"], function(__dependency1__, __exports__) { "use strict"; var SafeString = __dependency1__["default"]; __exports__["default"] = SafeString; }); enifed("morph-attr", ["./morph-attr/sanitize-attribute-value","./dom-helper/prop","./dom-helper/build-html-dom","./htmlbars-util","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var sanitizeAttributeValue = __dependency1__.sanitizeAttributeValue; var isAttrRemovalValue = __dependency2__.isAttrRemovalValue; var normalizeProperty = __dependency2__.normalizeProperty; var svgNamespace = __dependency3__.svgNamespace; var getAttrNamespace = __dependency4__.getAttrNamespace; function updateProperty(value) { this.domHelper.setPropertyStrict(this.element, this.attrName, value); } function updateAttribute(value) { if (isAttrRemovalValue(value)) { this.domHelper.removeAttribute(this.element, this.attrName); } else { this.domHelper.setAttribute(this.element, this.attrName, value); } } function updateAttributeNS(value) { if (isAttrRemovalValue(value)) { this.domHelper.removeAttribute(this.element, this.attrName); } else { this.domHelper.setAttributeNS(this.element, this.namespace, this.attrName, value); } } function AttrMorph(element, attrName, domHelper, namespace) { this.element = element; this.domHelper = domHelper; this.namespace = namespace !== undefined ? namespace : getAttrNamespace(attrName); this.escaped = true; var normalizedAttrName = normalizeProperty(this.element, attrName); if (this.namespace) { this._update = updateAttributeNS; this.attrName = attrName; } else { if (element.namespaceURI === svgNamespace || attrName === 'style' || !normalizedAttrName) { this.attrName = attrName; this._update = updateAttribute; } else { this.attrName = normalizedAttrName; this._update = updateProperty; } } } AttrMorph.prototype.setContent = function (value) { if (this.escaped) { var sanitized = sanitizeAttributeValue(this.domHelper, this.element, this.attrName, value); this._update(sanitized, this.namespace); } else { this._update(value, this.namespace); } }; __exports__["default"] = AttrMorph; __exports__.sanitizeAttributeValue = sanitizeAttributeValue; }); enifed("morph-attr/sanitize-attribute-value", ["exports"], function(__exports__) { "use strict"; /* jshint scripturl:true */ var badProtocols = { 'javascript:': true, 'vbscript:': true }; var badTags = { 'A': true, 'BODY': true, 'LINK': true, 'IMG': true, 'IFRAME': true }; var badAttributes = { 'href': true, 'src': true, 'background': true }; __exports__.badAttributes = badAttributes; function sanitizeAttributeValue(dom, element, attribute, value) { var tagName; if (!element) { tagName = null; } else { tagName = element.tagName; } if (value && value.toHTML) { return value.toHTML(); } if ((tagName === null || badTags[tagName]) && badAttributes[attribute]) { var protocol = dom.protocolForURL(value); if (badProtocols[protocol] === true) { return 'unsafe:' + value; } } return value; } __exports__.sanitizeAttributeValue = sanitizeAttributeValue; }); enifed("morph-range", ["exports"], function(__exports__) { "use strict"; var splice = Array.prototype.splice; function ensureStartEnd(start, end) { if (start === null || end === null) { throw new Error('a fragment parent must have boundary nodes in order to detect insertion'); } } function ensureContext(contextualElement) { if (!contextualElement || contextualElement.nodeType !== 1) { throw new Error('An element node must be provided for a contextualElement, you provided ' + (contextualElement ? 'nodeType ' + contextualElement.nodeType : 'nothing')); } } // TODO: this is an internal API, this should be an assert function Morph(parent, start, end, domHelper, contextualElement) { if (parent.nodeType === 11) { ensureStartEnd(start, end); this.element = null; } else { this.element = parent; } this._parent = parent; this.start = start; this.end = end; this.domHelper = domHelper; ensureContext(contextualElement); this.contextualElement = contextualElement; this.escaped = true; this.reset(); } Morph.prototype.reset = function() { this.text = null; this.owner = null; this.morphs = null; this.before = null; this.after = null; }; Morph.prototype.parent = function () { if (!this.element) { var parent = this.start.parentNode; if (this._parent !== parent) { this._parent = parent; } if (parent.nodeType === 1) { this.element = parent; } } return this._parent; }; Morph.prototype.destroy = function () { if (this.owner) { this.owner.removeMorph(this); } else { clear(this.element || this.parent(), this.start, this.end); } }; Morph.prototype.removeMorph = function (morph) { var morphs = this.morphs; for (var i=0, l=morphs.length; i 0 ? morphs[index-1] : null; var after = index < morphs.length ? morphs[index] : null; var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); var morph = new Morph(parent, start, end, this.domHelper, this.contextualElement); morph.owner = this; morph._update(parent, node); if (before !== null) { morph.before = before; before.end = start.nextSibling; before.after = morph; } if (after !== null) { morph.after = after; after.before = morph; after.start = end.previousSibling; } this.morphs.splice(index, 0, morph); return morph; }; Morph.prototype.replace = function (index, removedLength, addedNodes) { if (this.morphs === null) { this.morphs = []; } var parent = this.element || this.parent(); var morphs = this.morphs; var before = index > 0 ? morphs[index-1] : null; var after = index+removedLength < morphs.length ? morphs[index+removedLength] : null; var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); var addedLength = addedNodes === undefined ? 0 : addedNodes.length; var args, i, current; if (removedLength > 0) { clear(parent, start, end); } if (addedLength === 0) { if (before !== null) { before.after = after; before.end = end; } if (after !== null) { after.before = before; after.start = start; } morphs.splice(index, removedLength); return; } args = new Array(addedLength+2); if (addedLength > 0) { for (i=0; i " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; }).join(", ") } END IF **/ // This is a somewhat naive strategy, but should work in a lot of cases // A better strategy would properly resolve /posts/:id/new and /posts/edit/:id. // // This strategy generally prefers more static and less dynamic matching. // Specifically, it // // * prefers fewer stars to more, then // * prefers using stars for less of the match to more, then // * prefers fewer dynamic segments to more, then // * prefers more static segments to more function sortSolutions(states) { return states.sort(function(a, b) { if (a.types.stars !== b.types.stars) { return a.types.stars - b.types.stars; } if (a.types.stars) { if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } if (a.types.dynamics !== b.types.dynamics) { return b.types.dynamics - a.types.dynamics; } } if (a.types.dynamics !== b.types.dynamics) { return a.types.dynamics - b.types.dynamics; } if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } return 0; }); } function recognizeChar(states, ch) { var nextStates = []; for (var i=0, l=states.length; i 2 && key.slice(keyLength -2) === '[]') { isArray = true; key = key.slice(0, keyLength - 2); if(!queryParams[key]) { queryParams[key] = []; } } value = pair[1] ? decodeQueryParamPart(pair[1]) : ''; } if (isArray) { queryParams[key].push(value); } else { queryParams[key] = value; } } return queryParams; }, recognize: function(path) { var states = [ this.rootState ], pathLen, i, l, queryStart, queryParams = {}, isSlashDropped = false; queryStart = path.indexOf('?'); if (queryStart !== -1) { var queryString = path.substr(queryStart + 1, path.length); path = path.substr(0, queryStart); queryParams = this.parseQueryString(queryString); } path = decodeURI(path); // DEBUG GROUP path if (path.charAt(0) !== "/") { path = "/" + path; } pathLen = path.length; if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { path = path.substr(0, pathLen - 1); isSlashDropped = true; } for (i=0, l=path.length; i= 0 && proceed; --i) { var route = routes[i]; recognizer.add(routes, { as: route.handler }); proceed = route.path === '/' || route.path === '' || route.handler.slice(-6) === '.index'; } }); }, hasRoute: function(route) { return this.recognizer.hasRoute(route); }, queryParamsTransition: function(changelist, wasTransitioning, oldState, newState) { var router = this; fireQueryParamDidChange(this, newState, changelist); if (!wasTransitioning && this.activeTransition) { // One of the handlers in queryParamsDidChange // caused a transition. Just return that transition. return this.activeTransition; } else { // Running queryParamsDidChange didn't change anything. // Just update query params and be on our way. // We have to return a noop transition that will // perform a URL update at the end. This gives // the user the ability to set the url update // method (default is replaceState). var newTransition = new Transition(this); newTransition.queryParamsOnly = true; oldState.queryParams = finalizeQueryParamChange(this, newState.handlerInfos, newState.queryParams, newTransition); newTransition.promise = newTransition.promise.then(function(result) { updateURL(newTransition, oldState, true); if (router.didTransition) { router.didTransition(router.currentHandlerInfos); } return result; }, null, promiseLabel("Transition complete")); return newTransition; } }, // NOTE: this doesn't really belong here, but here // it shall remain until our ES6 transpiler can // handle cyclical deps. transitionByIntent: function(intent, isIntermediate) { try { return getTransitionByIntent.apply(this, arguments); } catch(e) { return new Transition(this, intent, null, e); } }, /** Clears the current and target route handlers and triggers exit on each of them starting at the leaf and traversing up through its ancestors. */ reset: function() { if (this.state) { forEach(this.state.handlerInfos.slice().reverse(), function(handlerInfo) { var handler = handlerInfo.handler; callHook(handler, 'exit'); }); } this.state = new TransitionState(); this.currentHandlerInfos = null; }, activeTransition: null, /** var handler = handlerInfo.handler; The entry point for handling a change to the URL (usually via the back and forward button). Returns an Array of handlers and the parameters associated with those parameters. @param {String} url a URL to process @return {Array} an Array of `[handler, parameter]` tuples */ handleURL: function(url) { // Perform a URL-based transition, but don't change // the URL afterward, since it already happened. var args = slice.call(arguments); if (url.charAt(0) !== '/') { args[0] = '/' + url; } return doTransition(this, args).method(null); }, /** Hook point for updating the URL. @param {String} url a URL to update to */ updateURL: function() { throw new Error("updateURL is not implemented"); }, /** Hook point for replacing the current URL, i.e. with replaceState By default this behaves the same as `updateURL` @param {String} url a URL to update to */ replaceURL: function(url) { this.updateURL(url); }, /** Transition into the specified named route. If necessary, trigger the exit callback on any handlers that are no longer represented by the target route. @param {String} name the name of the route */ transitionTo: function(name) { return doTransition(this, arguments); }, intermediateTransitionTo: function(name) { return doTransition(this, arguments, true); }, refresh: function(pivotHandler) { var state = this.activeTransition ? this.activeTransition.state : this.state; var handlerInfos = state.handlerInfos; var params = {}; for (var i = 0, len = handlerInfos.length; i < len; ++i) { var handlerInfo = handlerInfos[i]; params[handlerInfo.name] = handlerInfo.params || {}; } log(this, "Starting a refresh transition"); var intent = new NamedTransitionIntent({ name: handlerInfos[handlerInfos.length - 1].name, pivotHandler: pivotHandler || handlerInfos[0].handler, contexts: [], // TODO collect contexts...? queryParams: this._changedQueryParams || state.queryParams || {} }); return this.transitionByIntent(intent, false); }, /** Identical to `transitionTo` except that the current URL will be replaced if possible. This method is intended primarily for use with `replaceState`. @param {String} name the name of the route */ replaceWith: function(name) { return doTransition(this, arguments).method('replace'); }, /** Take a named route and context objects and generate a URL. @param {String} name the name of the route to generate a URL for @param {...Object} objects a list of objects to serialize @return {String} a URL */ generate: function(handlerName) { var partitionedArgs = extractQueryParams(slice.call(arguments, 1)), suppliedParams = partitionedArgs[0], queryParams = partitionedArgs[1]; // Construct a TransitionIntent with the provided params // and apply it to the present state of the router. var intent = new NamedTransitionIntent({ name: handlerName, contexts: suppliedParams }); var state = intent.applyToState(this.state, this.recognizer, this.getHandler); var params = {}; for (var i = 0, len = state.handlerInfos.length; i < len; ++i) { var handlerInfo = state.handlerInfos[i]; var handlerParams = handlerInfo.serialize(); merge(params, handlerParams); } params.queryParams = queryParams; return this.recognizer.generate(handlerName, params); }, applyIntent: function(handlerName, contexts) { var intent = new NamedTransitionIntent({ name: handlerName, contexts: contexts }); var state = this.activeTransition && this.activeTransition.state || this.state; return intent.applyToState(state, this.recognizer, this.getHandler); }, isActiveIntent: function(handlerName, contexts, queryParams, _state) { var state = _state || this.state, targetHandlerInfos = state.handlerInfos, found = false, names, object, handlerInfo, handlerObj, i, len; if (!targetHandlerInfos.length) { return false; } var targetHandler = targetHandlerInfos[targetHandlerInfos.length - 1].name; var recogHandlers = this.recognizer.handlersFor(targetHandler); var index = 0; for (len = recogHandlers.length; index < len; ++index) { handlerInfo = targetHandlerInfos[index]; if (handlerInfo.name === handlerName) { break; } } if (index === recogHandlers.length) { // The provided route name isn't even in the route hierarchy. return false; } var testState = new TransitionState(); testState.handlerInfos = targetHandlerInfos.slice(0, index + 1); recogHandlers = recogHandlers.slice(0, index + 1); var intent = new NamedTransitionIntent({ name: targetHandler, contexts: contexts }); var newState = intent.applyToHandlers(testState, recogHandlers, this.getHandler, targetHandler, true, true); var handlersEqual = handlerInfosEqual(newState.handlerInfos, testState.handlerInfos); if (!queryParams || !handlersEqual) { return handlersEqual; } // Get a hash of QPs that will still be active on new route var activeQPsOnNewHandler = {}; merge(activeQPsOnNewHandler, queryParams); var activeQueryParams = state.queryParams; for (var key in activeQueryParams) { if (activeQueryParams.hasOwnProperty(key) && activeQPsOnNewHandler.hasOwnProperty(key)) { activeQPsOnNewHandler[key] = activeQueryParams[key]; } } return handlersEqual && !getChangelist(activeQPsOnNewHandler, queryParams); }, isActive: function(handlerName) { var partitionedArgs = extractQueryParams(slice.call(arguments, 1)); return this.isActiveIntent(handlerName, partitionedArgs[0], partitionedArgs[1]); }, trigger: function(name) { var args = slice.call(arguments); trigger(this, this.currentHandlerInfos, false, args); }, /** Hook point for logging transition status updates. @param {String} message The message to log. */ log: null, _willChangeContextEvent: 'willChangeContext', _triggerWillChangeContext: function(handlerInfos, newTransition) { trigger(this, handlerInfos, true, [this._willChangeContextEvent, newTransition]); }, _triggerWillLeave: function(handlerInfos, newTransition, leavingChecker) { trigger(this, handlerInfos, true, ['willLeave', newTransition, leavingChecker]); } }; /** @private Fires queryParamsDidChange event */ function fireQueryParamDidChange(router, newState, queryParamChangelist) { // If queryParams changed trigger event if (queryParamChangelist) { // This is a little hacky but we need some way of storing // changed query params given that no activeTransition // is guaranteed to have occurred. router._changedQueryParams = queryParamChangelist.all; trigger(router, newState.handlerInfos, true, ['queryParamsDidChange', queryParamChangelist.changed, queryParamChangelist.all, queryParamChangelist.removed]); router._changedQueryParams = null; } } /** @private Takes an Array of `HandlerInfo`s, figures out which ones are exiting, entering, or changing contexts, and calls the proper handler hooks. For example, consider the following tree of handlers. Each handler is followed by the URL segment it handles. ``` |~index ("/") | |~posts ("/posts") | | |-showPost ("/:id") | | |-newPost ("/new") | | |-editPost ("/edit") | |~about ("/about/:id") ``` Consider the following transitions: 1. A URL transition to `/posts/1`. 1. Triggers the `*model` callbacks on the `index`, `posts`, and `showPost` handlers 2. Triggers the `enter` callback on the same 3. Triggers the `setup` callback on the same 2. A direct transition to `newPost` 1. Triggers the `exit` callback on `showPost` 2. Triggers the `enter` callback on `newPost` 3. Triggers the `setup` callback on `newPost` 3. A direct transition to `about` with a specified context object 1. Triggers the `exit` callback on `newPost` and `posts` 2. Triggers the `serialize` callback on `about` 3. Triggers the `enter` callback on `about` 4. Triggers the `setup` callback on `about` @param {Router} transition @param {TransitionState} newState */ function setupContexts(router, newState, transition) { var partition = partitionHandlers(router.state, newState); var i, l, handler; for (i=0, l=partition.exited.length; i= 0; --i) { var handlerInfo = handlerInfos[i]; merge(params, handlerInfo.params); if (handlerInfo.handler.inaccessibleByURL) { urlMethod = null; } } if (urlMethod) { params.queryParams = transition._visibleQueryParams || state.queryParams; var url = router.recognizer.generate(handlerName, params); if (urlMethod === 'replace') { router.replaceURL(url); } else { router.updateURL(url); } } } /** @private Updates the URL (if necessary) and calls `setupContexts` to update the router's array of `currentHandlerInfos`. */ function finalizeTransition(transition, newState) { try { log(transition.router, transition.sequence, "Resolved all models on destination route; finalizing transition."); var router = transition.router, handlerInfos = newState.handlerInfos, seq = transition.sequence; // Run all the necessary enter/setup/exit hooks setupContexts(router, newState, transition); // Check if a redirect occurred in enter/setup if (transition.isAborted) { // TODO: cleaner way? distinguish b/w targetHandlerInfos? router.state.handlerInfos = router.currentHandlerInfos; return Promise.reject(logAbort(transition)); } updateURL(transition, newState, transition.intent.url); transition.isActive = false; router.activeTransition = null; trigger(router, router.currentHandlerInfos, true, ['didTransition']); if (router.didTransition) { router.didTransition(router.currentHandlerInfos); } log(router, transition.sequence, "TRANSITION COMPLETE."); // Resolve with the final handler. return handlerInfos[handlerInfos.length - 1].handler; } catch(e) { if (!((e instanceof TransitionAborted))) { //var erroneousHandler = handlerInfos.pop(); var infos = transition.state.handlerInfos; transition.trigger(true, 'error', e, transition, infos[infos.length-1].handler); transition.abort(); } throw e; } } /** @private Begins and returns a Transition based on the provided arguments. Accepts arguments in the form of both URL transitions and named transitions. @param {Router} router @param {Array[Object]} args arguments passed to transitionTo, replaceWith, or handleURL */ function doTransition(router, args, isIntermediate) { // Normalize blank transitions to root URL transitions. var name = args[0] || '/'; var lastArg = args[args.length-1]; var queryParams = {}; if (lastArg && lastArg.hasOwnProperty('queryParams')) { queryParams = pop.call(args).queryParams; } var intent; if (args.length === 0) { log(router, "Updating query params"); // A query param update is really just a transition // into the route you're already on. var handlerInfos = router.state.handlerInfos; intent = new NamedTransitionIntent({ name: handlerInfos[handlerInfos.length - 1].name, contexts: [], queryParams: queryParams }); } else if (name.charAt(0) === '/') { log(router, "Attempting URL transition to " + name); intent = new URLTransitionIntent({ url: name }); } else { log(router, "Attempting transition to " + name); intent = new NamedTransitionIntent({ name: args[0], contexts: slice.call(args, 1), queryParams: queryParams }); } return router.transitionByIntent(intent, isIntermediate); } function handlerInfosEqual(handlerInfos, otherHandlerInfos) { if (handlerInfos.length !== otherHandlerInfos.length) { return false; } for (var i = 0, len = handlerInfos.length; i < len; ++i) { if (handlerInfos[i] !== otherHandlerInfos[i]) { return false; } } return true; } function finalizeQueryParamChange(router, resolvedHandlers, newQueryParams, transition) { // We fire a finalizeQueryParamChange event which // gives the new route hierarchy a chance to tell // us which query params it's consuming and what // their final values are. If a query param is // no longer consumed in the final route hierarchy, // its serialized segment will be removed // from the URL. for (var k in newQueryParams) { if (newQueryParams.hasOwnProperty(k) && newQueryParams[k] === null) { delete newQueryParams[k]; } } var finalQueryParamsArray = []; trigger(router, resolvedHandlers, true, ['finalizeQueryParamChange', newQueryParams, finalQueryParamsArray, transition]); if (transition) { transition._visibleQueryParams = {}; } var finalQueryParams = {}; for (var i = 0, len = finalQueryParamsArray.length; i < len; ++i) { var qp = finalQueryParamsArray[i]; finalQueryParams[qp.key] = qp.value; if (transition && qp.visible !== false) { transition._visibleQueryParams[qp.key] = qp.value; } } return finalQueryParams; } function notifyExistingHandlers(router, newState, newTransition) { var oldHandlers = router.state.handlerInfos, changing = [], leavingIndex = null, leaving, leavingChecker, i, oldHandlerLen, oldHandler, newHandler; oldHandlerLen = oldHandlers.length; for (i = 0; i < oldHandlerLen; i++) { oldHandler = oldHandlers[i]; newHandler = newState.handlerInfos[i]; if (!newHandler || oldHandler.name !== newHandler.name) { leavingIndex = i; break; } if (!newHandler.isResolved) { changing.push(oldHandler); } } if (leavingIndex !== null) { leaving = oldHandlers.slice(leavingIndex, oldHandlerLen); leavingChecker = function(name) { for (var h = 0, len = leaving.length; h < len; h++) { if (leaving[h].name === name) { return true; } } return false; }; router._triggerWillLeave(leaving, newTransition, leavingChecker); } if (changing.length > 0) { router._triggerWillChangeContext(changing, newTransition); } trigger(router, oldHandlers, true, ['willTransition', newTransition]); if (router.willTransition) { router.willTransition(oldHandlers, newState.handlerInfos, newTransition); } } __exports__["default"] = Router; }); enifed("router/transition-intent", ["./utils","exports"], function(__dependency1__, __exports__) { "use strict"; var merge = __dependency1__.merge; function TransitionIntent(props) { this.initialize(props); // TODO: wat this.data = this.data || {}; } TransitionIntent.prototype = { initialize: null, applyToState: null }; __exports__["default"] = TransitionIntent; }); enifed("router/transition-intent/named-transition-intent", ["../transition-intent","../transition-state","../handler-info/factory","../utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var TransitionIntent = __dependency1__["default"]; var TransitionState = __dependency2__["default"]; var handlerInfoFactory = __dependency3__["default"]; var isParam = __dependency4__.isParam; var extractQueryParams = __dependency4__.extractQueryParams; var merge = __dependency4__.merge; var subclass = __dependency4__.subclass; __exports__["default"] = subclass(TransitionIntent, { name: null, pivotHandler: null, contexts: null, queryParams: null, initialize: function(props) { this.name = props.name; this.pivotHandler = props.pivotHandler; this.contexts = props.contexts || []; this.queryParams = props.queryParams; }, applyToState: function(oldState, recognizer, getHandler, isIntermediate) { var partitionedArgs = extractQueryParams([this.name].concat(this.contexts)), pureArgs = partitionedArgs[0], queryParams = partitionedArgs[1], handlers = recognizer.handlersFor(pureArgs[0]); var targetRouteName = handlers[handlers.length-1].handler; return this.applyToHandlers(oldState, handlers, getHandler, targetRouteName, isIntermediate); }, applyToHandlers: function(oldState, handlers, getHandler, targetRouteName, isIntermediate, checkingIfActive) { var i, len; var newState = new TransitionState(); var objects = this.contexts.slice(0); var invalidateIndex = handlers.length; // Pivot handlers are provided for refresh transitions if (this.pivotHandler) { for (i = 0, len = handlers.length; i < len; ++i) { if (getHandler(handlers[i].handler) === this.pivotHandler) { invalidateIndex = i; break; } } } var pivotHandlerFound = !this.pivotHandler; for (i = handlers.length - 1; i >= 0; --i) { var result = handlers[i]; var name = result.handler; var handler = getHandler(name); var oldHandlerInfo = oldState.handlerInfos[i]; var newHandlerInfo = null; if (result.names.length > 0) { if (i >= invalidateIndex) { newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); } else { newHandlerInfo = this.getHandlerInfoForDynamicSegment(name, handler, result.names, objects, oldHandlerInfo, targetRouteName, i); } } else { // This route has no dynamic segment. // Therefore treat as a param-based handlerInfo // with empty params. This will cause the `model` // hook to be called with empty params, which is desirable. newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); } if (checkingIfActive) { // If we're performing an isActive check, we want to // serialize URL params with the provided context, but // ignore mismatches between old and new context. newHandlerInfo = newHandlerInfo.becomeResolved(null, newHandlerInfo.context); var oldContext = oldHandlerInfo && oldHandlerInfo.context; if (result.names.length > 0 && newHandlerInfo.context === oldContext) { // If contexts match in isActive test, assume params also match. // This allows for flexibility in not requiring that every last // handler provide a `serialize` method newHandlerInfo.params = oldHandlerInfo && oldHandlerInfo.params; } newHandlerInfo.context = oldContext; } var handlerToUse = oldHandlerInfo; if (i >= invalidateIndex || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { invalidateIndex = Math.min(i, invalidateIndex); handlerToUse = newHandlerInfo; } if (isIntermediate && !checkingIfActive) { handlerToUse = handlerToUse.becomeResolved(null, handlerToUse.context); } newState.handlerInfos.unshift(handlerToUse); } if (objects.length > 0) { throw new Error("More context objects were passed than there are dynamic segments for the route: " + targetRouteName); } if (!isIntermediate) { this.invalidateChildren(newState.handlerInfos, invalidateIndex); } merge(newState.queryParams, this.queryParams || {}); return newState; }, invalidateChildren: function(handlerInfos, invalidateIndex) { for (var i = invalidateIndex, l = handlerInfos.length; i < l; ++i) { var handlerInfo = handlerInfos[i]; handlerInfos[i] = handlerInfos[i].getUnresolved(); } }, getHandlerInfoForDynamicSegment: function(name, handler, names, objects, oldHandlerInfo, targetRouteName, i) { var numNames = names.length; var objectToUse; if (objects.length > 0) { // Use the objects provided for this transition. objectToUse = objects[objects.length - 1]; if (isParam(objectToUse)) { return this.createParamHandlerInfo(name, handler, names, objects, oldHandlerInfo); } else { objects.pop(); } } else if (oldHandlerInfo && oldHandlerInfo.name === name) { // Reuse the matching oldHandlerInfo return oldHandlerInfo; } else { if (this.preTransitionState) { var preTransitionHandlerInfo = this.preTransitionState.handlerInfos[i]; objectToUse = preTransitionHandlerInfo && preTransitionHandlerInfo.context; } else { // Ideally we should throw this error to provide maximal // information to the user that not enough context objects // were provided, but this proves too cumbersome in Ember // in cases where inner template helpers are evaluated // before parent helpers un-render, in which cases this // error somewhat prematurely fires. //throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]"); return oldHandlerInfo; } } return handlerInfoFactory('object', { name: name, handler: handler, context: objectToUse, names: names }); }, createParamHandlerInfo: function(name, handler, names, objects, oldHandlerInfo) { var params = {}; // Soak up all the provided string/numbers var numNames = names.length; while (numNames--) { // Only use old params if the names match with the new handler var oldParams = (oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params) || {}; var peek = objects[objects.length - 1]; var paramName = names[numNames]; if (isParam(peek)) { params[paramName] = "" + objects.pop(); } else { // If we're here, this means only some of the params // were string/number params, so try and use a param // value from a previous handler. if (oldParams.hasOwnProperty(paramName)) { params[paramName] = oldParams[paramName]; } else { throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name); } } } return handlerInfoFactory('param', { name: name, handler: handler, params: params }); } }); }); enifed("router/transition-intent/url-transition-intent", ["../transition-intent","../transition-state","../handler-info/factory","../utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var TransitionIntent = __dependency1__["default"]; var TransitionState = __dependency2__["default"]; var handlerInfoFactory = __dependency3__["default"]; var oCreate = __dependency4__.oCreate; var merge = __dependency4__.merge; var subclass = __dependency4__.subclass; __exports__["default"] = subclass(TransitionIntent, { url: null, initialize: function(props) { this.url = props.url; }, applyToState: function(oldState, recognizer, getHandler) { var newState = new TransitionState(); var results = recognizer.recognize(this.url), queryParams = {}, i, len; if (!results) { throw new UnrecognizedURLError(this.url); } var statesDiffer = false; for (i = 0, len = results.length; i < len; ++i) { var result = results[i]; var name = result.handler; var handler = getHandler(name); if (handler.inaccessibleByURL) { throw new UnrecognizedURLError(this.url); } var newHandlerInfo = handlerInfoFactory('param', { name: name, handler: handler, params: result.params }); var oldHandlerInfo = oldState.handlerInfos[i]; if (statesDiffer || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { statesDiffer = true; newState.handlerInfos[i] = newHandlerInfo; } else { newState.handlerInfos[i] = oldHandlerInfo; } } merge(newState.queryParams, results.queryParams); return newState; } }); /** Promise reject reasons passed to promise rejection handlers for failed transitions. */ function UnrecognizedURLError(message) { this.message = (message || "UnrecognizedURLError"); this.name = "UnrecognizedURLError"; } }); enifed("router/transition-state", ["./handler-info","./utils","rsvp/promise","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var ResolvedHandlerInfo = __dependency1__.ResolvedHandlerInfo; var forEach = __dependency2__.forEach; var promiseLabel = __dependency2__.promiseLabel; var callHook = __dependency2__.callHook; var Promise = __dependency3__["default"]; function TransitionState(other) { this.handlerInfos = []; this.queryParams = {}; this.params = {}; } TransitionState.prototype = { handlerInfos: null, queryParams: null, params: null, promiseLabel: function(label) { var targetName = ''; forEach(this.handlerInfos, function(handlerInfo) { if (targetName !== '') { targetName += '.'; } targetName += handlerInfo.name; }); return promiseLabel("'" + targetName + "': " + label); }, resolve: function(shouldContinue, payload) { var self = this; // First, calculate params for this state. This is useful // information to provide to the various route hooks. var params = this.params; forEach(this.handlerInfos, function(handlerInfo) { params[handlerInfo.name] = handlerInfo.params || {}; }); payload = payload || {}; payload.resolveIndex = 0; var currentState = this; var wasAborted = false; // The prelude RSVP.resolve() asyncs us into the promise land. return Promise.resolve(null, this.promiseLabel("Start transition")) .then(resolveOneHandlerInfo, null, this.promiseLabel('Resolve handler'))['catch'](handleError, this.promiseLabel('Handle error')); function innerShouldContinue() { return Promise.resolve(shouldContinue(), currentState.promiseLabel("Check if should continue"))['catch'](function(reason) { // We distinguish between errors that occurred // during resolution (e.g. beforeModel/model/afterModel), // and aborts due to a rejecting promise from shouldContinue(). wasAborted = true; return Promise.reject(reason); }, currentState.promiseLabel("Handle abort")); } function handleError(error) { // This is the only possible // reject value of TransitionState#resolve var handlerInfos = currentState.handlerInfos; var errorHandlerIndex = payload.resolveIndex >= handlerInfos.length ? handlerInfos.length - 1 : payload.resolveIndex; return Promise.reject({ error: error, handlerWithError: currentState.handlerInfos[errorHandlerIndex].handler, wasAborted: wasAborted, state: currentState }); } function proceed(resolvedHandlerInfo) { var wasAlreadyResolved = currentState.handlerInfos[payload.resolveIndex].isResolved; // Swap the previously unresolved handlerInfo with // the resolved handlerInfo currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo; if (!wasAlreadyResolved) { // Call the redirect hook. The reason we call it here // vs. afterModel is so that redirects into child // routes don't re-run the model hooks for this // already-resolved route. var handler = resolvedHandlerInfo.handler; callHook(handler, 'redirect', resolvedHandlerInfo.context, payload); } // Proceed after ensuring that the redirect hook // didn't abort this transition by transitioning elsewhere. return innerShouldContinue().then(resolveOneHandlerInfo, null, currentState.promiseLabel('Resolve handler')); } function resolveOneHandlerInfo() { if (payload.resolveIndex === currentState.handlerInfos.length) { // This is is the only possible // fulfill value of TransitionState#resolve return { error: null, state: currentState }; } var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; return handlerInfo.resolve(innerShouldContinue, payload) .then(proceed, null, currentState.promiseLabel('Proceed')); } } }; __exports__["default"] = TransitionState; }); enifed("router/transition", ["rsvp/promise","./handler-info","./utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; var ResolvedHandlerInfo = __dependency2__.ResolvedHandlerInfo; var trigger = __dependency3__.trigger; var slice = __dependency3__.slice; var log = __dependency3__.log; var promiseLabel = __dependency3__.promiseLabel; /** @private A Transition is a thennable (a promise-like object) that represents an attempt to transition to another route. It can be aborted, either explicitly via `abort` or by attempting another transition while a previous one is still underway. An aborted transition can also be `retry()`d later. */ function Transition(router, intent, state, error) { var transition = this; this.state = state || router.state; this.intent = intent; this.router = router; this.data = this.intent && this.intent.data || {}; this.resolvedModels = {}; this.queryParams = {}; if (error) { this.promise = Promise.reject(error); this.error = error; return; } if (state) { this.params = state.params; this.queryParams = state.queryParams; this.handlerInfos = state.handlerInfos; var len = state.handlerInfos.length; if (len) { this.targetName = state.handlerInfos[len-1].name; } for (var i = 0; i < len; ++i) { var handlerInfo = state.handlerInfos[i]; // TODO: this all seems hacky if (!handlerInfo.isResolved) { break; } this.pivotHandler = handlerInfo.handler; } this.sequence = Transition.currentSequence++; this.promise = state.resolve(checkForAbort, this)['catch'](function(result) { if (result.wasAborted || transition.isAborted) { return Promise.reject(logAbort(transition)); } else { transition.trigger('error', result.error, transition, result.handlerWithError); transition.abort(); return Promise.reject(result.error); } }, promiseLabel('Handle Abort')); } else { this.promise = Promise.resolve(this.state); this.params = {}; } function checkForAbort() { if (transition.isAborted) { return Promise.reject(undefined, promiseLabel("Transition aborted - reject")); } } } Transition.currentSequence = 0; Transition.prototype = { targetName: null, urlMethod: 'update', intent: null, params: null, pivotHandler: null, resolveIndex: 0, handlerInfos: null, resolvedModels: null, isActive: true, state: null, queryParamsOnly: false, isTransition: true, isExiting: function(handler) { var handlerInfos = this.handlerInfos; for (var i = 0, len = handlerInfos.length; i < len; ++i) { var handlerInfo = handlerInfos[i]; if (handlerInfo.name === handler || handlerInfo.handler === handler) { return false; } } return true; }, /** @public The Transition's internal promise. Calling `.then` on this property is that same as calling `.then` on the Transition object itself, but this property is exposed for when you want to pass around a Transition's promise, but not the Transition object itself, since Transition object can be externally `abort`ed, while the promise cannot. */ promise: null, /** @public Custom state can be stored on a Transition's `data` object. This can be useful for decorating a Transition within an earlier hook and shared with a later hook. Properties set on `data` will be copied to new transitions generated by calling `retry` on this transition. */ data: null, /** @public A standard promise hook that resolves if the transition succeeds and rejects if it fails/redirects/aborts. Forwards to the internal `promise` property which you can use in situations where you want to pass around a thennable, but not the Transition itself. @param {Function} onFulfilled @param {Function} onRejected @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} */ then: function(onFulfilled, onRejected, label) { return this.promise.then(onFulfilled, onRejected, label); }, /** @public Forwards to the internal `promise` property which you can use in situations where you want to pass around a thennable, but not the Transition itself. @method catch @param {Function} onRejection @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} */ "catch": function(onRejection, label) { return this.promise["catch"](onRejection, label); }, /** @public Forwards to the internal `promise` property which you can use in situations where you want to pass around a thennable, but not the Transition itself. @method finally @param {Function} callback @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} */ "finally": function(callback, label) { return this.promise["finally"](callback, label); }, /** @public Aborts the Transition. Note you can also implicitly abort a transition by initiating another transition while a previous one is underway. */ abort: function() { if (this.isAborted) { return this; } log(this.router, this.sequence, this.targetName + ": transition was aborted"); this.intent.preTransitionState = this.router.state; this.isAborted = true; this.isActive = false; this.router.activeTransition = null; return this; }, /** @public Retries a previously-aborted transition (making sure to abort the transition if it's still active). Returns a new transition that represents the new attempt to transition. */ retry: function() { // TODO: add tests for merged state retry()s this.abort(); return this.router.transitionByIntent(this.intent, false); }, /** @public Sets the URL-changing method to be employed at the end of a successful transition. By default, a new Transition will just use `updateURL`, but passing 'replace' to this method will cause the URL to update using 'replaceWith' instead. Omitting a parameter will disable the URL change, allowing for transitions that don't update the URL at completion (this is also used for handleURL, since the URL has already changed before the transition took place). @param {String} method the type of URL-changing method to use at the end of a transition. Accepted values are 'replace', falsy values, or any other non-falsy value (which is interpreted as an updateURL transition). @return {Transition} this transition */ method: function(method) { this.urlMethod = method; return this; }, /** @public Fires an event on the current list of resolved/resolving handlers within this transition. Useful for firing events on route hierarchies that haven't fully been entered yet. Note: This method is also aliased as `send` @param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error @param {String} name the name of the event to fire */ trigger: function (ignoreFailure) { var args = slice.call(arguments); if (typeof ignoreFailure === 'boolean') { args.shift(); } else { // Throw errors on unhandled trigger events by default ignoreFailure = false; } trigger(this.router, this.state.handlerInfos.slice(0, this.resolveIndex + 1), ignoreFailure, args); }, /** @public Transitions are aborted and their promises rejected when redirects occur; this method returns a promise that will follow any redirects that occur and fulfill with the value fulfilled by any redirecting transitions that occur. @return {Promise} a promise that fulfills with the same value that the final redirecting transition fulfills with */ followRedirects: function() { var router = this.router; return this.promise['catch'](function(reason) { if (router.activeTransition) { return router.activeTransition.followRedirects(); } return Promise.reject(reason); }); }, toString: function() { return "Transition (sequence " + this.sequence + ")"; }, /** @private */ log: function(message) { log(this.router, this.sequence, message); } }; // Alias 'trigger' as 'send' Transition.prototype.send = Transition.prototype.trigger; /** @private Logs and returns a TransitionAborted error. */ function logAbort(transition) { log(transition.router, transition.sequence, "detected abort."); return new TransitionAborted(); } function TransitionAborted(message) { this.message = (message || "TransitionAborted"); this.name = "TransitionAborted"; } __exports__.Transition = Transition; __exports__.logAbort = logAbort; __exports__.TransitionAborted = TransitionAborted; }); enifed("router/utils", ["exports"], function(__exports__) { "use strict"; var slice = Array.prototype.slice; var _isArray; if (!Array.isArray) { _isArray = function (x) { return Object.prototype.toString.call(x) === "[object Array]"; }; } else { _isArray = Array.isArray; } var isArray = _isArray; __exports__.isArray = isArray; function merge(hash, other) { for (var prop in other) { if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; } } } var oCreate = Object.create || function(proto) { function F() {} F.prototype = proto; return new F(); }; __exports__.oCreate = oCreate; /** @private Extracts query params from the end of an array **/ function extractQueryParams(array) { var len = (array && array.length), head, queryParams; if(len && len > 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) { queryParams = array[len - 1].queryParams; head = slice.call(array, 0, len - 1); return [head, queryParams]; } else { return [array, null]; } } __exports__.extractQueryParams = extractQueryParams;/** @private Coerces query param properties and array elements into strings. **/ function coerceQueryParamsToString(queryParams) { for (var key in queryParams) { if (typeof queryParams[key] === 'number') { queryParams[key] = '' + queryParams[key]; } else if (isArray(queryParams[key])) { for (var i = 0, l = queryParams[key].length; i < l; i++) { queryParams[key][i] = '' + queryParams[key][i]; } } } } /** @private */ function log(router, sequence, msg) { if (!router.log) { return; } if (arguments.length === 3) { router.log("Transition #" + sequence + ": " + msg); } else { msg = sequence; router.log(msg); } } __exports__.log = log;function bind(context, fn) { var boundArgs = arguments; return function(value) { var args = slice.call(boundArgs, 2); args.push(value); return fn.apply(context, args); }; } __exports__.bind = bind;function isParam(object) { return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number); } function forEach(array, callback) { for (var i=0, l=array.length; i=0; i--) { var handlerInfo = handlerInfos[i], handler = handlerInfo.handler; if (handler.events && handler.events[name]) { if (handler.events[name].apply(handler, args) === true) { eventWasHandled = true; } else { return; } } } if (!eventWasHandled && !ignoreFailure) { throw new Error("Nothing handled the event '" + name + "'."); } } __exports__.trigger = trigger;function getChangelist(oldObject, newObject) { var key; var results = { all: {}, changed: {}, removed: {} }; merge(results.all, newObject); var didChange = false; coerceQueryParamsToString(oldObject); coerceQueryParamsToString(newObject); // Calculate removals for (key in oldObject) { if (oldObject.hasOwnProperty(key)) { if (!newObject.hasOwnProperty(key)) { didChange = true; results.removed[key] = oldObject[key]; } } } // Calculate changes for (key in newObject) { if (newObject.hasOwnProperty(key)) { if (isArray(oldObject[key]) && isArray(newObject[key])) { if (oldObject[key].length !== newObject[key].length) { results.changed[key] = newObject[key]; didChange = true; } else { for (var i = 0, l = oldObject[key].length; i < l; i++) { if (oldObject[key][i] !== newObject[key][i]) { results.changed[key] = newObject[key]; didChange = true; } } } } else { if (oldObject[key] !== newObject[key]) { results.changed[key] = newObject[key]; didChange = true; } } } } return didChange && results; } __exports__.getChangelist = getChangelist;function promiseLabel(label) { return 'Router: ' + label; } __exports__.promiseLabel = promiseLabel;function subclass(parentConstructor, proto) { function C(props) { parentConstructor.call(this, props || {}); } C.prototype = oCreate(parentConstructor.prototype); merge(C.prototype, proto); return C; } __exports__.subclass = subclass;function resolveHook(obj, hookName) { if (!obj) { return; } var underscored = "_" + hookName; return obj[underscored] && underscored || obj[hookName] && hookName; } function callHook(obj, _hookName, arg1, arg2) { var hookName = resolveHook(obj, _hookName); return hookName && obj[hookName].call(obj, arg1, arg2); } function applyHook(obj, _hookName, args) { var hookName = resolveHook(obj, _hookName); if (hookName) { if (args.length === 0) { return obj[hookName].call(obj); } else if (args.length === 1) { return obj[hookName].call(obj, args[0]); } else if (args.length === 2) { return obj[hookName].call(obj, args[0], args[1]); } else { return obj[hookName].apply(obj, args); } } } __exports__.merge = merge; __exports__.slice = slice; __exports__.isParam = isParam; __exports__.coerceQueryParamsToString = coerceQueryParamsToString; __exports__.callHook = callHook; __exports__.resolveHook = resolveHook; __exports__.applyHook = applyHook; }); enifed("rsvp", ["./rsvp/promise","./rsvp/events","./rsvp/node","./rsvp/all","./rsvp/all-settled","./rsvp/race","./rsvp/hash","./rsvp/hash-settled","./rsvp/rethrow","./rsvp/defer","./rsvp/config","./rsvp/map","./rsvp/resolve","./rsvp/reject","./rsvp/filter","./rsvp/asap","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; var EventTarget = __dependency2__["default"]; var denodeify = __dependency3__["default"]; var all = __dependency4__["default"]; var allSettled = __dependency5__["default"]; var race = __dependency6__["default"]; var hash = __dependency7__["default"]; var hashSettled = __dependency8__["default"]; var rethrow = __dependency9__["default"]; var defer = __dependency10__["default"]; var config = __dependency11__.config; var configure = __dependency11__.configure; var map = __dependency12__["default"]; var resolve = __dependency13__["default"]; var reject = __dependency14__["default"]; var filter = __dependency15__["default"]; var asap = __dependency16__["default"]; config.async = asap; // default async is asap; var cast = resolve; function async(callback, arg) { config.async(callback, arg); } function on() { config.on.apply(config, arguments); } function off() { config.off.apply(config, arguments); } // Set up instrumentation through `window.__PROMISE_INTRUMENTATION__` if (typeof window !== 'undefined' && typeof window['__PROMISE_INSTRUMENTATION__'] === 'object') { var callbacks = window['__PROMISE_INSTRUMENTATION__']; configure('instrument', true); for (var eventName in callbacks) { if (callbacks.hasOwnProperty(eventName)) { on(eventName, callbacks[eventName]); } } } __exports__.cast = cast; __exports__.Promise = Promise; __exports__.EventTarget = EventTarget; __exports__.all = all; __exports__.allSettled = allSettled; __exports__.race = race; __exports__.hash = hash; __exports__.hashSettled = hashSettled; __exports__.rethrow = rethrow; __exports__.defer = defer; __exports__.denodeify = denodeify; __exports__.configure = configure; __exports__.on = on; __exports__.off = off; __exports__.resolve = resolve; __exports__.reject = reject; __exports__.async = async; __exports__.map = map; __exports__.filter = filter; }); enifed("rsvp.umd", ["./rsvp"], function(__dependency1__) { "use strict"; var Promise = __dependency1__.Promise; var allSettled = __dependency1__.allSettled; var hash = __dependency1__.hash; var hashSettled = __dependency1__.hashSettled; var denodeify = __dependency1__.denodeify; var on = __dependency1__.on; var off = __dependency1__.off; var map = __dependency1__.map; var filter = __dependency1__.filter; var resolve = __dependency1__.resolve; var reject = __dependency1__.reject; var rethrow = __dependency1__.rethrow; var all = __dependency1__.all; var defer = __dependency1__.defer; var EventTarget = __dependency1__.EventTarget; var configure = __dependency1__.configure; var race = __dependency1__.race; var async = __dependency1__.async; var RSVP = { 'race': race, 'Promise': Promise, 'allSettled': allSettled, 'hash': hash, 'hashSettled': hashSettled, 'denodeify': denodeify, 'on': on, 'off': off, 'map': map, 'filter': filter, 'resolve': resolve, 'reject': reject, 'all': all, 'rethrow': rethrow, 'defer': defer, 'EventTarget': EventTarget, 'configure': configure, 'async': async }; /* global define:true module:true window: true */ if (typeof enifed === 'function' && enifed['amd']) { enifed(function() { return RSVP; }); } else if (typeof module !== 'undefined' && module['exports']) { module['exports'] = RSVP; } else if (typeof this !== 'undefined') { this['RSVP'] = RSVP; } }); enifed("rsvp/-internal", ["./utils","./instrument","./config","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var objectOrFunction = __dependency1__.objectOrFunction; var isFunction = __dependency1__.isFunction; var instrument = __dependency2__["default"]; var config = __dependency3__.config; function withOwnPromise() { return new TypeError('A promises callback cannot return that same promise.'); } function noop() {} var PENDING = void 0; var FULFILLED = 1; var REJECTED = 2; var GET_THEN_ERROR = new ErrorObject(); function getThen(promise) { try { return promise.then; } catch(error) { GET_THEN_ERROR.error = error; return GET_THEN_ERROR; } } function tryThen(then, value, fulfillmentHandler, rejectionHandler) { try { then.call(value, fulfillmentHandler, rejectionHandler); } catch(e) { return e; } } function handleForeignThenable(promise, thenable, then) { config.async(function(promise) { var sealed = false; var error = tryThen(then, thenable, function(value) { if (sealed) { return; } sealed = true; if (thenable !== value) { resolve(promise, value); } else { fulfill(promise, value); } }, function(reason) { if (sealed) { return; } sealed = true; reject(promise, reason); }, 'Settle: ' + (promise._label || ' unknown promise')); if (!sealed && error) { sealed = true; reject(promise, error); } }, promise); } function handleOwnThenable(promise, thenable) { if (thenable._state === FULFILLED) { fulfill(promise, thenable._result); } else if (promise._state === REJECTED) { reject(promise, thenable._result); } else { subscribe(thenable, undefined, function(value) { if (thenable !== value) { resolve(promise, value); } else { fulfill(promise, value); } }, function(reason) { reject(promise, reason); }); } } function handleMaybeThenable(promise, maybeThenable) { if (maybeThenable.constructor === promise.constructor) { handleOwnThenable(promise, maybeThenable); } else { var then = getThen(maybeThenable); if (then === GET_THEN_ERROR) { reject(promise, GET_THEN_ERROR.error); } else if (then === undefined) { fulfill(promise, maybeThenable); } else if (isFunction(then)) { handleForeignThenable(promise, maybeThenable, then); } else { fulfill(promise, maybeThenable); } } } function resolve(promise, value) { if (promise === value) { fulfill(promise, value); } else if (objectOrFunction(value)) { handleMaybeThenable(promise, value); } else { fulfill(promise, value); } } function publishRejection(promise) { if (promise._onerror) { promise._onerror(promise._result); } publish(promise); } function fulfill(promise, value) { if (promise._state !== PENDING) { return; } promise._result = value; promise._state = FULFILLED; if (promise._subscribers.length === 0) { if (config.instrument) { instrument('fulfilled', promise); } } else { config.async(publish, promise); } } function reject(promise, reason) { if (promise._state !== PENDING) { return; } promise._state = REJECTED; promise._result = reason; config.async(publishRejection, promise); } function subscribe(parent, child, onFulfillment, onRejection) { var subscribers = parent._subscribers; var length = subscribers.length; parent._onerror = null; subscribers[length] = child; subscribers[length + FULFILLED] = onFulfillment; subscribers[length + REJECTED] = onRejection; if (length === 0 && parent._state) { config.async(publish, parent); } } function publish(promise) { var subscribers = promise._subscribers; var settled = promise._state; if (config.instrument) { instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); } if (subscribers.length === 0) { return; } var child, callback, detail = promise._result; for (var i = 0; i < subscribers.length; i += 3) { child = subscribers[i]; callback = subscribers[i + settled]; if (child) { invokeCallback(settled, child, callback, detail); } else { callback(detail); } } promise._subscribers.length = 0; } function ErrorObject() { this.error = null; } var TRY_CATCH_ERROR = new ErrorObject(); function tryCatch(callback, detail) { try { return callback(detail); } catch(e) { TRY_CATCH_ERROR.error = e; return TRY_CATCH_ERROR; } } function invokeCallback(settled, promise, callback, detail) { var hasCallback = isFunction(callback), value, error, succeeded, failed; if (hasCallback) { value = tryCatch(callback, detail); if (value === TRY_CATCH_ERROR) { failed = true; error = value.error; value = null; } else { succeeded = true; } if (promise === value) { reject(promise, withOwnPromise()); return; } } else { value = detail; succeeded = true; } if (promise._state !== PENDING) { // noop } else if (hasCallback && succeeded) { resolve(promise, value); } else if (failed) { reject(promise, error); } else if (settled === FULFILLED) { fulfill(promise, value); } else if (settled === REJECTED) { reject(promise, value); } } function initializePromise(promise, resolver) { try { resolver(function resolvePromise(value){ resolve(promise, value); }, function rejectPromise(reason) { reject(promise, reason); }); } catch(e) { reject(promise, e); } } __exports__.noop = noop; __exports__.resolve = resolve; __exports__.reject = reject; __exports__.fulfill = fulfill; __exports__.subscribe = subscribe; __exports__.publish = publish; __exports__.publishRejection = publishRejection; __exports__.initializePromise = initializePromise; __exports__.invokeCallback = invokeCallback; __exports__.FULFILLED = FULFILLED; __exports__.REJECTED = REJECTED; __exports__.PENDING = PENDING; }); enifed("rsvp/all-settled", ["./enumerator","./promise","./utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Enumerator = __dependency1__["default"]; var makeSettledResult = __dependency1__.makeSettledResult; var Promise = __dependency2__["default"]; var o_create = __dependency3__.o_create; function AllSettled(Constructor, entries, label) { this._superConstructor(Constructor, entries, false /* don't abort on reject */, label); } AllSettled.prototype = o_create(Enumerator.prototype); AllSettled.prototype._superConstructor = Enumerator; AllSettled.prototype._makeResult = makeSettledResult; AllSettled.prototype._validationError = function() { return new Error('allSettled must be called with an array'); }; /** `RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing a fail-fast method, it waits until all the promises have returned and shows you all the results. This is useful if you want to handle multiple promises' failure states together as a set. Returns a promise that is fulfilled when all the given promises have been settled. The return promise is fulfilled with an array of the states of the promises passed into the `promises` array argument. Each state object will either indicate fulfillment or rejection, and provide the corresponding value or reason. The states will take one of the following formats: ```javascript { state: 'fulfilled', value: value } or { state: 'rejected', reason: reason } ``` Example: ```javascript var promise1 = RSVP.Promise.resolve(1); var promise2 = RSVP.Promise.reject(new Error('2')); var promise3 = RSVP.Promise.reject(new Error('3')); var promises = [ promise1, promise2, promise3 ]; RSVP.allSettled(promises).then(function(array){ // array == [ // { state: 'fulfilled', value: 1 }, // { state: 'rejected', reason: Error }, // { state: 'rejected', reason: Error } // ] // Note that for the second item, reason.message will be '2', and for the // third item, reason.message will be '3'. }, function(error) { // Not run. (This block would only be called if allSettled had failed, // for instance if passed an incorrect argument type.) }); ``` @method allSettled @static @for RSVP @param {Array} promises @param {String} label - optional string that describes the promise. Useful for tooling. @return {Promise} promise that is fulfilled with an array of the settled states of the constituent promises. */ __exports__["default"] = function allSettled(entries, label) { return new AllSettled(Promise, entries, label).promise; } }); enifed("rsvp/all", ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; /** This is a convenient alias for `RSVP.Promise.all`. @method all @static @for RSVP @param {Array} array Array of promises. @param {String} label An optional label. This is useful for tooling. */ __exports__["default"] = function all(array, label) { return Promise.all(array, label); } }); enifed("rsvp/asap", ["exports"], function(__exports__) { "use strict"; var len = 0; __exports__["default"] = function asap(callback, arg) { queue[len] = callback; queue[len + 1] = arg; len += 2; if (len === 2) { // If len is 1, that means that we need to schedule an async flush. // If additional callbacks are queued before the queue is flushed, they // will be processed by this flush that we are scheduling. scheduleFlush(); } } var browserWindow = (typeof window !== 'undefined') ? window : undefined var browserGlobal = browserWindow || {}; var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; // test for web worker but not in IE10 var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; // node function useNextTick() { return function() { process.nextTick(flush); }; } // vertx function useVertxTimer() { return function() { vertxNext(flush); }; } function useMutationObserver() { var iterations = 0; var observer = new BrowserMutationObserver(flush); var node = document.createTextNode(''); observer.observe(node, { characterData: true }); return function() { node.data = (iterations = ++iterations % 2); }; } // web worker function useMessageChannel() { var channel = new MessageChannel(); channel.port1.onmessage = flush; return function () { channel.port2.postMessage(0); }; } function useSetTimeout() { return function() { setTimeout(flush, 1); }; } var queue = new Array(1000); function flush() { for (var i = 0; i < len; i+=2) { var callback = queue[i]; var arg = queue[i+1]; callback(arg); queue[i] = undefined; queue[i+1] = undefined; } len = 0; } function attemptVertex() { try { var vertx = eriuqer('vertx'); var vertxNext = vertx.runOnLoop || vertx.runOnContext; return useVertxTimer(); } catch(e) { return useSetTimeout(); } } var scheduleFlush; // Decide what async method to use to triggering processing of queued callbacks: if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { scheduleFlush = useNextTick(); } else if (BrowserMutationObserver) { scheduleFlush = useMutationObserver(); } else if (isWorker) { scheduleFlush = useMessageChannel(); } else if (browserWindow === undefined && typeof eriuqer === 'function') { scheduleFlush = attemptVertex(); } else { scheduleFlush = useSetTimeout(); } }); enifed("rsvp/config", ["./events","exports"], function(__dependency1__, __exports__) { "use strict"; var EventTarget = __dependency1__["default"]; var config = { instrument: false }; EventTarget.mixin(config); function configure(name, value) { if (name === 'onerror') { // handle for legacy users that expect the actual // error to be passed to their function added via // `RSVP.configure('onerror', someFunctionHere);` config.on('error', value); return; } if (arguments.length === 2) { config[name] = value; } else { return config[name]; } } __exports__.config = config; __exports__.configure = configure; }); enifed("rsvp/defer", ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; /** `RSVP.defer` returns an object similar to jQuery's `$.Deferred`. `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s interface. New code should use the `RSVP.Promise` constructor instead. The object returned from `RSVP.defer` is a plain object with three properties: * promise - an `RSVP.Promise`. * reject - a function that causes the `promise` property on this object to become rejected * resolve - a function that causes the `promise` property on this object to become fulfilled. Example: ```javascript var deferred = RSVP.defer(); deferred.resolve("Success!"); defered.promise.then(function(value){ // value here is "Success!" }); ``` @method defer @static @for RSVP @param {String} label optional string for labeling the promise. Useful for tooling. @return {Object} */ __exports__["default"] = function defer(label) { var deferred = { }; deferred['promise'] = new Promise(function(resolve, reject) { deferred['resolve'] = resolve; deferred['reject'] = reject; }, label); return deferred; } }); enifed("rsvp/enumerator", ["./utils","./-internal","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var isArray = __dependency1__.isArray; var isMaybeThenable = __dependency1__.isMaybeThenable; var noop = __dependency2__.noop; var reject = __dependency2__.reject; var fulfill = __dependency2__.fulfill; var subscribe = __dependency2__.subscribe; var FULFILLED = __dependency2__.FULFILLED; var REJECTED = __dependency2__.REJECTED; var PENDING = __dependency2__.PENDING; function makeSettledResult(state, position, value) { if (state === FULFILLED) { return { state: 'fulfilled', value: value }; } else { return { state: 'rejected', reason: value }; } } __exports__.makeSettledResult = makeSettledResult;function Enumerator(Constructor, input, abortOnReject, label) { this._instanceConstructor = Constructor; this.promise = new Constructor(noop, label); this._abortOnReject = abortOnReject; if (this._validateInput(input)) { this._input = input; this.length = input.length; this._remaining = input.length; this._init(); if (this.length === 0) { fulfill(this.promise, this._result); } else { this.length = this.length || 0; this._enumerate(); if (this._remaining === 0) { fulfill(this.promise, this._result); } } } else { reject(this.promise, this._validationError()); } } Enumerator.prototype._validateInput = function(input) { return isArray(input); }; Enumerator.prototype._validationError = function() { return new Error('Array Methods must be provided an Array'); }; Enumerator.prototype._init = function() { this._result = new Array(this.length); }; __exports__["default"] = Enumerator; Enumerator.prototype._enumerate = function() { var length = this.length; var promise = this.promise; var input = this._input; for (var i = 0; promise._state === PENDING && i < length; i++) { this._eachEntry(input[i], i); } }; Enumerator.prototype._eachEntry = function(entry, i) { var c = this._instanceConstructor; if (isMaybeThenable(entry)) { if (entry.constructor === c && entry._state !== PENDING) { entry._onerror = null; this._settledAt(entry._state, i, entry._result); } else { this._willSettleAt(c.resolve(entry), i); } } else { this._remaining--; this._result[i] = this._makeResult(FULFILLED, i, entry); } }; Enumerator.prototype._settledAt = function(state, i, value) { var promise = this.promise; if (promise._state === PENDING) { this._remaining--; if (this._abortOnReject && state === REJECTED) { reject(promise, value); } else { this._result[i] = this._makeResult(state, i, value); } } if (this._remaining === 0) { fulfill(promise, this._result); } }; Enumerator.prototype._makeResult = function(state, i, value) { return value; }; Enumerator.prototype._willSettleAt = function(promise, i) { var enumerator = this; subscribe(promise, undefined, function(value) { enumerator._settledAt(FULFILLED, i, value); }, function(reason) { enumerator._settledAt(REJECTED, i, reason); }); }; }); enifed("rsvp/events", ["exports"], function(__exports__) { "use strict"; function indexOf(callbacks, callback) { for (var i=0, l=callbacks.length; i 1; }; RSVP.filter(promises, filterFn).then(function(result){ // result is [ 2, 3 ] }); ``` If any of the `promises` given to `RSVP.filter` are rejected, the first promise that is rejected will be given as an argument to the returned promise's rejection handler. For example: ```javascript var promise1 = RSVP.resolve(1); var promise2 = RSVP.reject(new Error('2')); var promise3 = RSVP.reject(new Error('3')); var promises = [ promise1, promise2, promise3 ]; var filterFn = function(item){ return item > 1; }; RSVP.filter(promises, filterFn).then(function(array){ // Code here never runs because there are rejected promises! }, function(reason) { // reason.message === '2' }); ``` `RSVP.filter` will also wait for any promises returned from `filterFn`. For instance, you may want to fetch a list of users then return a subset of those users based on some asynchronous operation: ```javascript var alice = { name: 'alice' }; var bob = { name: 'bob' }; var users = [ alice, bob ]; var promises = users.map(function(user){ return RSVP.resolve(user); }); var filterFn = function(user){ // Here, Alice has permissions to create a blog post, but Bob does not. return getPrivilegesForUser(user).then(function(privs){ return privs.can_create_blog_post === true; }); }; RSVP.filter(promises, filterFn).then(function(users){ // true, because the server told us only Alice can create a blog post. users.length === 1; // false, because Alice is the only user present in `users` users[0] === bob; }); ``` @method filter @static @for RSVP @param {Array} promises @param {Function} filterFn - function to be called on each resolved value to filter the final results. @param {String} label optional string describing the promise. Useful for tooling. @return {Promise} */ __exports__["default"] = function filter(promises, filterFn, label) { return Promise.all(promises, label).then(function(values) { if (!isFunction(filterFn)) { throw new TypeError("You must pass a function as filter's second argument."); } var length = values.length; var filtered = new Array(length); for (var i = 0; i < length; i++) { filtered[i] = filterFn(values[i]); } return Promise.all(filtered, label).then(function(filtered) { var results = new Array(length); var newLength = 0; for (var i = 0; i < length; i++) { if (filtered[i]) { results[newLength] = values[i]; newLength++; } } results.length = newLength; return results; }); }); } }); enifed("rsvp/hash-settled", ["./promise","./enumerator","./promise-hash","./utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; var makeSettledResult = __dependency2__.makeSettledResult; var PromiseHash = __dependency3__["default"]; var Enumerator = __dependency2__["default"]; var o_create = __dependency4__.o_create; function HashSettled(Constructor, object, label) { this._superConstructor(Constructor, object, false, label); } HashSettled.prototype = o_create(PromiseHash.prototype); HashSettled.prototype._superConstructor = Enumerator; HashSettled.prototype._makeResult = makeSettledResult; HashSettled.prototype._validationError = function() { return new Error('hashSettled must be called with an object'); }; /** `RSVP.hashSettled` is similar to `RSVP.allSettled`, but takes an object instead of an array for its `promises` argument. Unlike `RSVP.all` or `RSVP.hash`, which implement a fail-fast method, but like `RSVP.allSettled`, `hashSettled` waits until all the constituent promises have returned and then shows you all the results with their states and values/reasons. This is useful if you want to handle multiple promises' failure states together as a set. Returns a promise that is fulfilled when all the given promises have been settled, or rejected if the passed parameters are invalid. The returned promise is fulfilled with a hash that has the same key names as the `promises` object argument. If any of the values in the object are not promises, they will be copied over to the fulfilled object and marked with state 'fulfilled'. Example: ```javascript var promises = { myPromise: RSVP.Promise.resolve(1), yourPromise: RSVP.Promise.resolve(2), theirPromise: RSVP.Promise.resolve(3), notAPromise: 4 }; RSVP.hashSettled(promises).then(function(hash){ // hash here is an object that looks like: // { // myPromise: { state: 'fulfilled', value: 1 }, // yourPromise: { state: 'fulfilled', value: 2 }, // theirPromise: { state: 'fulfilled', value: 3 }, // notAPromise: { state: 'fulfilled', value: 4 } // } }); ``` If any of the `promises` given to `RSVP.hash` are rejected, the state will be set to 'rejected' and the reason for rejection provided. Example: ```javascript var promises = { myPromise: RSVP.Promise.resolve(1), rejectedPromise: RSVP.Promise.reject(new Error('rejection')), anotherRejectedPromise: RSVP.Promise.reject(new Error('more rejection')), }; RSVP.hashSettled(promises).then(function(hash){ // hash here is an object that looks like: // { // myPromise: { state: 'fulfilled', value: 1 }, // rejectedPromise: { state: 'rejected', reason: Error }, // anotherRejectedPromise: { state: 'rejected', reason: Error }, // } // Note that for rejectedPromise, reason.message == 'rejection', // and for anotherRejectedPromise, reason.message == 'more rejection'. }); ``` An important note: `RSVP.hashSettled` is intended for plain JavaScript objects that are just a set of keys and values. `RSVP.hashSettled` will NOT preserve prototype chains. Example: ```javascript function MyConstructor(){ this.example = RSVP.Promise.resolve('Example'); } MyConstructor.prototype = { protoProperty: RSVP.Promise.resolve('Proto Property') }; var myObject = new MyConstructor(); RSVP.hashSettled(myObject).then(function(hash){ // protoProperty will not be present, instead you will just have an // object that looks like: // { // example: { state: 'fulfilled', value: 'Example' } // } // // hash.hasOwnProperty('protoProperty'); // false // 'undefined' === typeof hash.protoProperty }); ``` @method hashSettled @for RSVP @param {Object} promises @param {String} label optional string that describes the promise. Useful for tooling. @return {Promise} promise that is fulfilled when when all properties of `promises` have been settled. @static */ __exports__["default"] = function hashSettled(object, label) { return new HashSettled(Promise, object, label).promise; } }); enifed("rsvp/hash", ["./promise","./promise-hash","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; var PromiseHash = __dependency2__["default"]; /** `RSVP.hash` is similar to `RSVP.all`, but takes an object instead of an array for its `promises` argument. Returns a promise that is fulfilled when all the given promises have been fulfilled, or rejected if any of them become rejected. The returned promise is fulfilled with a hash that has the same key names as the `promises` object argument. If any of the values in the object are not promises, they will simply be copied over to the fulfilled object. Example: ```javascript var promises = { myPromise: RSVP.resolve(1), yourPromise: RSVP.resolve(2), theirPromise: RSVP.resolve(3), notAPromise: 4 }; RSVP.hash(promises).then(function(hash){ // hash here is an object that looks like: // { // myPromise: 1, // yourPromise: 2, // theirPromise: 3, // notAPromise: 4 // } }); ```` If any of the `promises` given to `RSVP.hash` are rejected, the first promise that is rejected will be given as the reason to the rejection handler. Example: ```javascript var promises = { myPromise: RSVP.resolve(1), rejectedPromise: RSVP.reject(new Error('rejectedPromise')), anotherRejectedPromise: RSVP.reject(new Error('anotherRejectedPromise')), }; RSVP.hash(promises).then(function(hash){ // Code here never runs because there are rejected promises! }, function(reason) { // reason.message === 'rejectedPromise' }); ``` An important note: `RSVP.hash` is intended for plain JavaScript objects that are just a set of keys and values. `RSVP.hash` will NOT preserve prototype chains. Example: ```javascript function MyConstructor(){ this.example = RSVP.resolve('Example'); } MyConstructor.prototype = { protoProperty: RSVP.resolve('Proto Property') }; var myObject = new MyConstructor(); RSVP.hash(myObject).then(function(hash){ // protoProperty will not be present, instead you will just have an // object that looks like: // { // example: 'Example' // } // // hash.hasOwnProperty('protoProperty'); // false // 'undefined' === typeof hash.protoProperty }); ``` @method hash @static @for RSVP @param {Object} promises @param {String} label optional string that describes the promise. Useful for tooling. @return {Promise} promise that is fulfilled when all properties of `promises` have been fulfilled, or rejected if any of them become rejected. */ __exports__["default"] = function hash(object, label) { return new PromiseHash(Promise, object, label).promise; } }); enifed("rsvp/instrument", ["./config","./utils","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var config = __dependency1__.config; var now = __dependency2__.now; var queue = []; function scheduleFlush() { setTimeout(function() { var entry; for (var i = 0; i < queue.length; i++) { entry = queue[i]; var payload = entry.payload; payload.guid = payload.key + payload.id; payload.childGuid = payload.key + payload.childId; if (payload.error) { payload.stack = payload.error.stack; } config.trigger(entry.name, entry.payload); } queue.length = 0; }, 50); } __exports__["default"] = function instrument(eventName, promise, child) { if (1 === queue.push({ name: eventName, payload: { key: promise._guidKey, id: promise._id, eventName: eventName, detail: promise._result, childId: child && child._id, label: promise._label, timeStamp: now(), error: config["instrument-with-stack"] ? new Error(promise._label) : null }})) { scheduleFlush(); } } }); enifed("rsvp/map", ["./promise","./utils","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; var isFunction = __dependency2__.isFunction; /** `RSVP.map` is similar to JavaScript's native `map` method, except that it waits for all promises to become fulfilled before running the `mapFn` on each item in given to `promises`. `RSVP.map` returns a promise that will become fulfilled with the result of running `mapFn` on the values the promises become fulfilled with. For example: ```javascript var promise1 = RSVP.resolve(1); var promise2 = RSVP.resolve(2); var promise3 = RSVP.resolve(3); var promises = [ promise1, promise2, promise3 ]; var mapFn = function(item){ return item + 1; }; RSVP.map(promises, mapFn).then(function(result){ // result is [ 2, 3, 4 ] }); ``` If any of the `promises` given to `RSVP.map` are rejected, the first promise that is rejected will be given as an argument to the returned promise's rejection handler. For example: ```javascript var promise1 = RSVP.resolve(1); var promise2 = RSVP.reject(new Error('2')); var promise3 = RSVP.reject(new Error('3')); var promises = [ promise1, promise2, promise3 ]; var mapFn = function(item){ return item + 1; }; RSVP.map(promises, mapFn).then(function(array){ // Code here never runs because there are rejected promises! }, function(reason) { // reason.message === '2' }); ``` `RSVP.map` will also wait if a promise is returned from `mapFn`. For example, say you want to get all comments from a set of blog posts, but you need the blog posts first because they contain a url to those comments. ```javscript var mapFn = function(blogPost){ // getComments does some ajax and returns an RSVP.Promise that is fulfilled // with some comments data return getComments(blogPost.comments_url); }; // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled // with some blog post data RSVP.map(getBlogPosts(), mapFn).then(function(comments){ // comments is the result of asking the server for the comments // of all blog posts returned from getBlogPosts() }); ``` @method map @static @for RSVP @param {Array} promises @param {Function} mapFn function to be called on each fulfilled promise. @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} promise that is fulfilled with the result of calling `mapFn` on each fulfilled promise or value when they become fulfilled. The promise will be rejected if any of the given `promises` become rejected. @static */ __exports__["default"] = function map(promises, mapFn, label) { return Promise.all(promises, label).then(function(values) { if (!isFunction(mapFn)) { throw new TypeError("You must pass a function as map's second argument."); } var length = values.length; var results = new Array(length); for (var i = 0; i < length; i++) { results[i] = mapFn(values[i]); } return Promise.all(results, label); }); } }); enifed("rsvp/node", ["./promise","./-internal","./utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; var noop = __dependency2__.noop; var resolve = __dependency2__.resolve; var reject = __dependency2__.reject; var isArray = __dependency3__.isArray; function Result() { this.value = undefined; } var ERROR = new Result(); var GET_THEN_ERROR = new Result(); function getThen(obj) { try { return obj.then; } catch(error) { ERROR.value= error; return ERROR; } } function tryApply(f, s, a) { try { f.apply(s, a); } catch(error) { ERROR.value = error; return ERROR; } } function makeObject(_, argumentNames) { var obj = {}; var name; var i; var length = _.length; var args = new Array(length); for (var x = 0; x < length; x++) { args[x] = _[x]; } for (i = 0; i < argumentNames.length; i++) { name = argumentNames[i]; obj[name] = args[i + 1]; } return obj; } function arrayResult(_) { var length = _.length; var args = new Array(length - 1); for (var i = 1; i < length; i++) { args[i - 1] = _[i]; } return args; } function wrapThenable(then, promise) { return { then: function(onFulFillment, onRejection) { return then.call(promise, onFulFillment, onRejection); } }; } /** `RSVP.denodeify` takes a 'node-style' function and returns a function that will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the browser when you'd prefer to use promises over using callbacks. For example, `denodeify` transforms the following: ```javascript var fs = require('fs'); fs.readFile('myfile.txt', function(err, data){ if (err) return handleError(err); handleData(data); }); ``` into: ```javascript var fs = require('fs'); var readFile = RSVP.denodeify(fs.readFile); readFile('myfile.txt').then(handleData, handleError); ``` If the node function has multiple success parameters, then `denodeify` just returns the first one: ```javascript var request = RSVP.denodeify(require('request')); request('http://example.com').then(function(res) { // ... }); ``` However, if you need all success parameters, setting `denodeify`'s second parameter to `true` causes it to return all success parameters as an array: ```javascript var request = RSVP.denodeify(require('request'), true); request('http://example.com').then(function(result) { // result[0] -> res // result[1] -> body }); ``` Or if you pass it an array with names it returns the parameters as a hash: ```javascript var request = RSVP.denodeify(require('request'), ['res', 'body']); request('http://example.com').then(function(result) { // result.res // result.body }); ``` Sometimes you need to retain the `this`: ```javascript var app = require('express')(); var render = RSVP.denodeify(app.render.bind(app)); ``` The denodified function inherits from the original function. It works in all environments, except IE 10 and below. Consequently all properties of the original function are available to you. However, any properties you change on the denodeified function won't be changed on the original function. Example: ```javascript var request = RSVP.denodeify(require('request')), cookieJar = request.jar(); // <- Inheritance is used here request('http://example.com', {jar: cookieJar}).then(function(res) { // cookieJar.cookies holds now the cookies returned by example.com }); ``` Using `denodeify` makes it easier to compose asynchronous operations instead of using callbacks. For example, instead of: ```javascript var fs = require('fs'); fs.readFile('myfile.txt', function(err, data){ if (err) { ... } // Handle error fs.writeFile('myfile2.txt', data, function(err){ if (err) { ... } // Handle error console.log('done') }); }); ``` you can chain the operations together using `then` from the returned promise: ```javascript var fs = require('fs'); var readFile = RSVP.denodeify(fs.readFile); var writeFile = RSVP.denodeify(fs.writeFile); readFile('myfile.txt').then(function(data){ return writeFile('myfile2.txt', data); }).then(function(){ console.log('done') }).catch(function(error){ // Handle error }); ``` @method denodeify @static @for RSVP @param {Function} nodeFunc a 'node-style' function that takes a callback as its last argument. The callback expects an error to be passed as its first argument (if an error occurred, otherwise null), and the value from the operation as its second argument ('function(err, value){ }'). @param {Boolean|Array} argumentNames An optional paramter that if set to `true` causes the promise to fulfill with the callback's success arguments as an array. This is useful if the node function has multiple success paramters. If you set this paramter to an array with names, the promise will fulfill with a hash with these names as keys and the success parameters as values. @return {Function} a function that wraps `nodeFunc` to return an `RSVP.Promise` @static */ __exports__["default"] = function denodeify(nodeFunc, options) { var fn = function() { var self = this; var l = arguments.length; var args = new Array(l + 1); var arg; var promiseInput = false; for (var i = 0; i < l; ++i) { arg = arguments[i]; if (!promiseInput) { // TODO: clean this up promiseInput = needsPromiseInput(arg); if (promiseInput === GET_THEN_ERROR) { var p = new Promise(noop); reject(p, GET_THEN_ERROR.value); return p; } else if (promiseInput && promiseInput !== true) { arg = wrapThenable(promiseInput, arg); } } args[i] = arg; } var promise = new Promise(noop); args[l] = function(err, val) { if (err) reject(promise, err); else if (options === undefined) resolve(promise, val); else if (options === true) resolve(promise, arrayResult(arguments)); else if (isArray(options)) resolve(promise, makeObject(arguments, options)); else resolve(promise, val); }; if (promiseInput) { return handlePromiseInput(promise, args, nodeFunc, self); } else { return handleValueInput(promise, args, nodeFunc, self); } }; fn.__proto__ = nodeFunc; return fn; } function handleValueInput(promise, args, nodeFunc, self) { var result = tryApply(nodeFunc, self, args); if (result === ERROR) { reject(promise, result.value); } return promise; } function handlePromiseInput(promise, args, nodeFunc, self){ return Promise.all(args).then(function(args){ var result = tryApply(nodeFunc, self, args); if (result === ERROR) { reject(promise, result.value); } return promise; }); } function needsPromiseInput(arg) { if (arg && typeof arg === 'object') { if (arg.constructor === Promise) { return true; } else { return getThen(arg); } } else { return false; } } }); enifed("rsvp/promise-hash", ["./enumerator","./-internal","./utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Enumerator = __dependency1__["default"]; var PENDING = __dependency2__.PENDING; var o_create = __dependency3__.o_create; function PromiseHash(Constructor, object, label) { this._superConstructor(Constructor, object, true, label); } __exports__["default"] = PromiseHash; PromiseHash.prototype = o_create(Enumerator.prototype); PromiseHash.prototype._superConstructor = Enumerator; PromiseHash.prototype._init = function() { this._result = {}; }; PromiseHash.prototype._validateInput = function(input) { return input && typeof input === 'object'; }; PromiseHash.prototype._validationError = function() { return new Error('Promise.hash must be called with an object'); }; PromiseHash.prototype._enumerate = function() { var promise = this.promise; var input = this._input; var results = []; for (var key in input) { if (promise._state === PENDING && input.hasOwnProperty(key)) { results.push({ position: key, entry: input[key] }); } } var length = results.length; this._remaining = length; var result; for (var i = 0; promise._state === PENDING && i < length; i++) { result = results[i]; this._eachEntry(result.entry, result.position); } }; }); enifed("rsvp/promise", ["./config","./instrument","./utils","./-internal","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; var config = __dependency1__.config; var instrument = __dependency2__["default"]; var isFunction = __dependency3__.isFunction; var now = __dependency3__.now; var noop = __dependency4__.noop; var subscribe = __dependency4__.subscribe; var initializePromise = __dependency4__.initializePromise; var invokeCallback = __dependency4__.invokeCallback; var FULFILLED = __dependency4__.FULFILLED; var REJECTED = __dependency4__.REJECTED; var all = __dependency5__["default"]; var race = __dependency6__["default"]; var Resolve = __dependency7__["default"]; var Reject = __dependency8__["default"]; var guidKey = 'rsvp_' + now() + '-'; var counter = 0; function needsResolver() { throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); } function needsNew() { throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); } __exports__["default"] = Promise; /** Promise objects represent the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled. Terminology ----------- - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - `thenable` is an object or function that defines a `then` method. - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - `exception` is a value that is thrown using the throw statement. - `reason` is a value that indicates why a promise was rejected. - `settled` the final resting state of a promise, fulfilled or rejected. A promise can be in one of three states: pending, fulfilled, or rejected. Promises that are fulfilled have a fulfillment value and are in the fulfilled state. Promises that are rejected have a rejection reason and are in the rejected state. A fulfillment value is never a thenable. Promises can also be said to *resolve* a value. If this value is also a promise, then the original promise's settled state will match the value's settled state. So a promise that *resolves* a promise that rejects will itself reject, and a promise that *resolves* a promise that fulfills will itself fulfill. Basic Usage: ------------ ```js var promise = new Promise(function(resolve, reject) { // on success resolve(value); // on failure reject(reason); }); promise.then(function(value) { // on fulfillment }, function(reason) { // on rejection }); ``` Advanced Usage: --------------- Promises shine when abstracting away asynchronous interactions such as `XMLHttpRequest`s. ```js function getJSON(url) { return new Promise(function(resolve, reject){ var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = handler; xhr.responseType = 'json'; xhr.setRequestHeader('Accept', 'application/json'); xhr.send(); function handler() { if (this.readyState === this.DONE) { if (this.status === 200) { resolve(this.response); } else { reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); } } }; }); } getJSON('/posts.json').then(function(json) { // on fulfillment }, function(reason) { // on rejection }); ``` Unlike callbacks, promises are great composable primitives. ```js Promise.all([ getJSON('/posts'), getJSON('/comments') ]).then(function(values){ values[0] // => postsJSON values[1] // => commentsJSON return values; }); ``` @class RSVP.Promise @param {function} resolver @param {String} label optional string for labeling the promise. Useful for tooling. @constructor */ function Promise(resolver, label) { this._id = counter++; this._label = label; this._state = undefined; this._result = undefined; this._subscribers = []; if (config.instrument) { instrument('created', this); } if (noop !== resolver) { if (!isFunction(resolver)) { needsResolver(); } if (!(this instanceof Promise)) { needsNew(); } initializePromise(this, resolver); } } Promise.cast = Resolve; // deprecated Promise.all = all; Promise.race = race; Promise.resolve = Resolve; Promise.reject = Reject; Promise.prototype = { constructor: Promise, _guidKey: guidKey, _onerror: function (reason) { config.trigger('error', reason); }, /** The primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled. ```js findUser().then(function(user){ // user is available }, function(reason){ // user is unavailable, and you are given the reason why }); ``` Chaining -------- The return value of `then` is itself a promise. This second, 'downstream' promise is resolved with the return value of the first promise's fulfillment or rejection handler, or rejected if the handler throws an exception. ```js findUser().then(function (user) { return user.name; }, function (reason) { return 'default name'; }).then(function (userName) { // If `findUser` fulfilled, `userName` will be the user's name, otherwise it // will be `'default name'` }); findUser().then(function (user) { throw new Error('Found user, but still unhappy'); }, function (reason) { throw new Error('`findUser` rejected and we're unhappy'); }).then(function (value) { // never reached }, function (reason) { // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. }); ``` If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. ```js findUser().then(function (user) { throw new PedagogicalException('Upstream error'); }).then(function (value) { // never reached }).then(function (value) { // never reached }, function (reason) { // The `PedgagocialException` is propagated all the way down to here }); ``` Assimilation ------------ Sometimes the value you want to propagate to a downstream promise can only be retrieved asynchronously. This can be achieved by returning a promise in the fulfillment or rejection handler. The downstream promise will then be pending until the returned promise is settled. This is called *assimilation*. ```js findUser().then(function (user) { return findCommentsByAuthor(user); }).then(function (comments) { // The user's comments are now available }); ``` If the assimliated promise rejects, then the downstream promise will also reject. ```js findUser().then(function (user) { return findCommentsByAuthor(user); }).then(function (comments) { // If `findCommentsByAuthor` fulfills, we'll have the value here }, function (reason) { // If `findCommentsByAuthor` rejects, we'll have the reason here }); ``` Simple Example -------------- Synchronous Example ```javascript var result; try { result = findResult(); // success } catch(reason) { // failure } ``` Errback Example ```js findResult(function(result, err){ if (err) { // failure } else { // success } }); ``` Promise Example; ```javascript findResult().then(function(result){ // success }, function(reason){ // failure }); ``` Advanced Example -------------- Synchronous Example ```javascript var author, books; try { author = findAuthor(); books = findBooksByAuthor(author); // success } catch(reason) { // failure } ``` Errback Example ```js function foundBooks(books) { } function failure(reason) { } findAuthor(function(author, err){ if (err) { failure(err); // failure } else { try { findBoooksByAuthor(author, function(books, err) { if (err) { failure(err); } else { try { foundBooks(books); } catch(reason) { failure(reason); } } }); } catch(error) { failure(err); } // success } }); ``` Promise Example; ```javascript findAuthor(). then(findBooksByAuthor). then(function(books){ // found books }).catch(function(reason){ // something went wrong }); ``` @method then @param {Function} onFulfilled @param {Function} onRejected @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} */ then: function(onFulfillment, onRejection, label) { var parent = this; var state = parent._state; if (state === FULFILLED && !onFulfillment || state === REJECTED && !onRejection) { if (config.instrument) { instrument('chained', this, this); } return this; } parent._onerror = null; var child = new this.constructor(noop, label); var result = parent._result; if (config.instrument) { instrument('chained', parent, child); } if (state) { var callback = arguments[state - 1]; config.async(function(){ invokeCallback(state, child, callback, result); }); } else { subscribe(parent, child, onFulfillment, onRejection); } return child; }, /** `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same as the catch block of a try/catch statement. ```js function findAuthor(){ throw new Error('couldn't find that author'); } // synchronous try { findAuthor(); } catch(reason) { // something went wrong } // async with promises findAuthor().catch(function(reason){ // something went wrong }); ``` @method catch @param {Function} onRejection @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} */ 'catch': function(onRejection, label) { return this.then(null, onRejection, label); }, /** `finally` will be invoked regardless of the promise's fate just as native try/catch/finally behaves Synchronous example: ```js findAuthor() { if (Math.random() > 0.5) { throw new Error(); } return new Author(); } try { return findAuthor(); // succeed or fail } catch(error) { return findOtherAuther(); } finally { // always runs // doesn't affect the return value } ``` Asynchronous example: ```js findAuthor().catch(function(reason){ return findOtherAuther(); }).finally(function(){ // author was either found, or not }); ``` @method finally @param {Function} callback @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} */ 'finally': function(callback, label) { var constructor = this.constructor; return this.then(function(value) { return constructor.resolve(callback()).then(function(){ return value; }); }, function(reason) { return constructor.resolve(callback()).then(function(){ throw reason; }); }, label); } }; }); enifed("rsvp/promise/all", ["../enumerator","exports"], function(__dependency1__, __exports__) { "use strict"; var Enumerator = __dependency1__["default"]; /** `RSVP.Promise.all` accepts an array of promises, and returns a new promise which is fulfilled with an array of fulfillment values for the passed promises, or rejected with the reason of the first passed promise to be rejected. It casts all elements of the passed iterable to promises as it runs this algorithm. Example: ```javascript var promise1 = RSVP.resolve(1); var promise2 = RSVP.resolve(2); var promise3 = RSVP.resolve(3); var promises = [ promise1, promise2, promise3 ]; RSVP.Promise.all(promises).then(function(array){ // The array here would be [ 1, 2, 3 ]; }); ``` If any of the `promises` given to `RSVP.all` are rejected, the first promise that is rejected will be given as an argument to the returned promises's rejection handler. For example: Example: ```javascript var promise1 = RSVP.resolve(1); var promise2 = RSVP.reject(new Error("2")); var promise3 = RSVP.reject(new Error("3")); var promises = [ promise1, promise2, promise3 ]; RSVP.Promise.all(promises).then(function(array){ // Code here never runs because there are rejected promises! }, function(error) { // error.message === "2" }); ``` @method all @static @param {Array} entries array of promises @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} promise that is fulfilled when all `promises` have been fulfilled, or rejected if any of them become rejected. @static */ __exports__["default"] = function all(entries, label) { return new Enumerator(this, entries, true /* abort on reject */, label).promise; } }); enifed("rsvp/promise/race", ["../utils","../-internal","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var isArray = __dependency1__.isArray; var noop = __dependency2__.noop; var resolve = __dependency2__.resolve; var reject = __dependency2__.reject; var subscribe = __dependency2__.subscribe; var PENDING = __dependency2__.PENDING; /** `RSVP.Promise.race` returns a new promise which is settled in the same way as the first passed promise to settle. Example: ```javascript var promise1 = new RSVP.Promise(function(resolve, reject){ setTimeout(function(){ resolve('promise 1'); }, 200); }); var promise2 = new RSVP.Promise(function(resolve, reject){ setTimeout(function(){ resolve('promise 2'); }, 100); }); RSVP.Promise.race([promise1, promise2]).then(function(result){ // result === 'promise 2' because it was resolved before promise1 // was resolved. }); ``` `RSVP.Promise.race` is deterministic in that only the state of the first settled promise matters. For example, even if other promises given to the `promises` array argument are resolved, but the first settled promise has become rejected before the other promises became fulfilled, the returned promise will become rejected: ```javascript var promise1 = new RSVP.Promise(function(resolve, reject){ setTimeout(function(){ resolve('promise 1'); }, 200); }); var promise2 = new RSVP.Promise(function(resolve, reject){ setTimeout(function(){ reject(new Error('promise 2')); }, 100); }); RSVP.Promise.race([promise1, promise2]).then(function(result){ // Code here never runs }, function(reason){ // reason.message === 'promise 2' because promise 2 became rejected before // promise 1 became fulfilled }); ``` An example real-world use case is implementing timeouts: ```javascript RSVP.Promise.race([ajax('foo.json'), timeout(5000)]) ``` @method race @static @param {Array} promises array of promises to observe @param {String} label optional string for describing the promise returned. Useful for tooling. @return {Promise} a promise which settles in the same way as the first passed promise to settle. */ __exports__["default"] = function race(entries, label) { /*jshint validthis:true */ var Constructor = this; var promise = new Constructor(noop, label); if (!isArray(entries)) { reject(promise, new TypeError('You must pass an array to race.')); return promise; } var length = entries.length; function onFulfillment(value) { resolve(promise, value); } function onRejection(reason) { reject(promise, reason); } for (var i = 0; promise._state === PENDING && i < length; i++) { subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); } return promise; } }); enifed("rsvp/promise/reject", ["../-internal","exports"], function(__dependency1__, __exports__) { "use strict"; var noop = __dependency1__.noop; var _reject = __dependency1__.reject; /** `RSVP.Promise.reject` returns a promise rejected with the passed `reason`. It is shorthand for the following: ```javascript var promise = new RSVP.Promise(function(resolve, reject){ reject(new Error('WHOOPS')); }); promise.then(function(value){ // Code here doesn't run because the promise is rejected! }, function(reason){ // reason.message === 'WHOOPS' }); ``` Instead of writing the above, your code now simply becomes the following: ```javascript var promise = RSVP.Promise.reject(new Error('WHOOPS')); promise.then(function(value){ // Code here doesn't run because the promise is rejected! }, function(reason){ // reason.message === 'WHOOPS' }); ``` @method reject @static @param {Any} reason value that the returned promise will be rejected with. @param {String} label optional string for identifying the returned promise. Useful for tooling. @return {Promise} a promise rejected with the given `reason`. */ __exports__["default"] = function reject(reason, label) { /*jshint validthis:true */ var Constructor = this; var promise = new Constructor(noop, label); _reject(promise, reason); return promise; } }); enifed("rsvp/promise/resolve", ["../-internal","exports"], function(__dependency1__, __exports__) { "use strict"; var noop = __dependency1__.noop; var _resolve = __dependency1__.resolve; /** `RSVP.Promise.resolve` returns a promise that will become resolved with the passed `value`. It is shorthand for the following: ```javascript var promise = new RSVP.Promise(function(resolve, reject){ resolve(1); }); promise.then(function(value){ // value === 1 }); ``` Instead of writing the above, your code now simply becomes the following: ```javascript var promise = RSVP.Promise.resolve(1); promise.then(function(value){ // value === 1 }); ``` @method resolve @static @param {Any} value value that the returned promise will be resolved with @param {String} label optional string for identifying the returned promise. Useful for tooling. @return {Promise} a promise that will become fulfilled with the given `value` */ __exports__["default"] = function resolve(object, label) { /*jshint validthis:true */ var Constructor = this; if (object && typeof object === 'object' && object.constructor === Constructor) { return object; } var promise = new Constructor(noop, label); _resolve(promise, object); return promise; } }); enifed("rsvp/race", ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; /** This is a convenient alias for `RSVP.Promise.race`. @method race @static @for RSVP @param {Array} array Array of promises. @param {String} label An optional label. This is useful for tooling. */ __exports__["default"] = function race(array, label) { return Promise.race(array, label); } }); enifed("rsvp/reject", ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; /** This is a convenient alias for `RSVP.Promise.reject`. @method reject @static @for RSVP @param {Any} reason value that the returned promise will be rejected with. @param {String} label optional string for identifying the returned promise. Useful for tooling. @return {Promise} a promise rejected with the given `reason`. */ __exports__["default"] = function reject(reason, label) { return Promise.reject(reason, label); } }); enifed("rsvp/resolve", ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; var Promise = __dependency1__["default"]; /** This is a convenient alias for `RSVP.Promise.resolve`. @method resolve @static @for RSVP @param {Any} value value that the returned promise will be resolved with @param {String} label optional string for identifying the returned promise. Useful for tooling. @return {Promise} a promise that will become fulfilled with the given `value` */ __exports__["default"] = function resolve(value, label) { return Promise.resolve(value, label); } }); enifed("rsvp/rethrow", ["exports"], function(__exports__) { "use strict"; /** `RSVP.rethrow` will rethrow an error on the next turn of the JavaScript event loop in order to aid debugging. Promises A+ specifies that any exceptions that occur with a promise must be caught by the promises implementation and bubbled to the last handler. For this reason, it is recommended that you always specify a second rejection handler function to `then`. However, `RSVP.rethrow` will throw the exception outside of the promise, so it bubbles up to your console if in the browser, or domain/cause uncaught exception in Node. `rethrow` will also throw the error again so the error can be handled by the promise per the spec. ```javascript function throws(){ throw new Error('Whoops!'); } var promise = new RSVP.Promise(function(resolve, reject){ throws(); }); promise.catch(RSVP.rethrow).then(function(){ // Code here doesn't run because the promise became rejected due to an // error! }, function (err){ // handle the error here }); ``` The 'Whoops' error will be thrown on the next turn of the event loop and you can watch for it in your console. You can also handle it using a rejection handler given to `.then` or `.catch` on the returned promise. @method rethrow @static @for RSVP @param {Error} reason reason the promise became rejected. @throws Error @static */ __exports__["default"] = function rethrow(reason) { setTimeout(function() { throw reason; }); throw reason; } }); enifed("rsvp/utils", ["exports"], function(__exports__) { "use strict"; function objectOrFunction(x) { return typeof x === 'function' || (typeof x === 'object' && x !== null); } __exports__.objectOrFunction = objectOrFunction;function isFunction(x) { return typeof x === 'function'; } __exports__.isFunction = isFunction;function isMaybeThenable(x) { return typeof x === 'object' && x !== null; } __exports__.isMaybeThenable = isMaybeThenable;var _isArray; if (!Array.isArray) { _isArray = function (x) { return Object.prototype.toString.call(x) === '[object Array]'; }; } else { _isArray = Array.isArray; } var isArray = _isArray; __exports__.isArray = isArray; // Date.now is not available in browsers < IE9 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility var now = Date.now || function() { return new Date().getTime(); }; __exports__.now = now; function F() { } var o_create = (Object.create || function (o) { if (arguments.length > 1) { throw new Error('Second argument not supported'); } if (typeof o !== 'object') { throw new TypeError('Argument must be an object'); } F.prototype = o; return new F(); }); __exports__.o_create = o_create; }); requireModule("ember"); })();