/*! * @overview Ember - JavaScript Application Framework * @copyright Copyright 2011-2014 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.7.0-beta.5 */ (function() { var define, requireModule, require, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; if (typeof Ember === 'undefined') { Ember = {} }; if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; define = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; requirejs = require = requireModule = function(name) { if (seen.hasOwnProperty(name)) { return seen[name]; } seen[name] = {}; if (!registry[name]) { throw new Error("Could not find module " + name); } var mod = registry[name], deps = mod.deps, callback = mod.callback, reified = [], exports; for (var i=0, l=deps.length; i"; }; Klass.create = create; Klass.extend = extend; Klass.reopen = extend; Klass.reopenClass = reopenClass; return Klass; function create(options) { var passedOptions = 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.prototype, options); Child.create = create; Child.extend = extend; Child.reopen = extend; Child.reopenClass = reopenClass; return Child; } }; __exports__.factory = factory; __exports__.o_create = o_create; __exports__.setProperties = setProperties; }); define("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.'); }); }); define("container/tests/container_test", ["container/tests/container_helper","container"], function(__dependency1__, __dependency2__) { "use strict"; var factory = __dependency1__.factory; var o_create = __dependency1__.o_create; var setProperties = __dependency1__.setProperties; var Container = __dependency2__["default"]; var originalModelInjections; QUnit.module("Container", { setup: function() { originalModelInjections = Ember.MODEL_FACTORY_INJECTIONS; }, teardown: function() { Ember.MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); test("A registered factory returns the same instance each time", function() { var container = new Container(); var PostController = factory(); container.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')); }); test("A registered factory is returned from lookupFactory", function() { var container = new Container(); var PostController = factory(); container.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"); }); test("A registered factory is returned from lookupFactory is the same factory each time", function() { var container = new Container(); var PostController = factory(); container.register('controller:post', PostController); deepEqual(container.lookupFactory('controller:post'), container.lookupFactory('controller:post'), 'The return of lookupFactory is always the same'); }); test("A factory returned from lookupFactory has a debugkey", function() { var container = new Container(); var PostController = factory(); var instance; container.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'); }); test("fallback for to create time injections if factory has no extend", function() { var container = new Container(); var AppleController = factory(); var PostController = factory(); PostController.extend = undefined; // remove extend var instance; container.register('controller:apple', AppleController); container.register('controller:post', PostController); container.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'); }); test("The descendants of a factory returned from lookupFactory have a container and debugkey", function(){ var container = new Container(); var PostController = factory(); var instance; container.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'); }); test("A registered factory returns a fresh instance if singleton: false is passed as an option", function() { var container = new Container(); var PostController = factory(); container.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"); }); test("A registered factory returns true for `has` if an item is registered", function() { var container = new Container(); var PostController = factory(); container.register('controller:post', PostController); equal(container.has('controller:post'), true, "The `has` method returned true for registered factories"); equal(container.has('controller:posts'), false, "The `has` method returned false for unregistered factories"); }); test("A Registered factory can be unregistered, and all cached instances are removed", function() { var container = new Container(); var PostController = factory(); container.register('controller:post', PostController); equal(container.has('controller:post'), true, "container is aware of the PostController"); ok(container.lookup('controller:post') instanceof PostController, "lookup is correct instance"); container.unregister("controller:post"); equal(container.has('controller:post'), false, "container is no-longer aware of the PostController"); equal(container.lookup('controller:post'), undefined, "lookup no longer returns a controller"); // re-registration continues to work container.register('controller:post', PostController); equal(container.has('controller:post'), true, "container is aware of the PostController"); ok(container.lookup('controller:post') instanceof PostController, "lookup is correct instance"); }); test("A container lookup has access to the container", function() { var container = new Container(); var PostController = factory(); container.register('controller:post', PostController); var postController = container.lookup('controller:post'); equal(postController.container, container); }); test("Throw exception when trying to inject `type:thing` on all type(s)", function(){ var container = new Container(), PostController = factory(); container.register('controller:post', PostController); throws(function(){ container.typeInjection('controller', 'injected', 'controller:post'); }, 'Cannot inject a `controller:post` on other controller(s). Register the `controller:post` as a different type and perform the typeInjection.'); }); test("A factory type with a registered injection's instances receive that injection", function() { var container = new Container(); var PostController = factory(); var Store = factory(); container.register('controller:post', PostController); container.register('store:main', Store); container.typeInjection('controller', 'store', 'store:main'); var postController = container.lookup('controller:post'); var store = container.lookup('store:main'); equal(postController.store, store); }); test("An individual factory with a registered injection receives the injection", function() { var container = new Container(); var PostController = factory(); var Store = factory(); container.register('controller:post', PostController); container.register('store:main', Store); container.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'); }); test("A factory with both type and individual injections", function() { var container = new Container(); var PostController = factory(); var Store = factory(); var Router = factory(); container.register('controller:post', PostController); container.register('store:main', Store); container.register('router:main', Router); container.injection('controller:post', 'store', 'store:main'); container.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); }); test("A factory with both type and individual factoryInjections", function() { var container = new Container(); var PostController = factory(); var Store = factory(); var Router = factory(); container.register('controller:post', PostController); container.register('store:main', Store); container.register('router:main', Router); container.factoryInjection('controller:post', 'store', 'store:main'); container.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'); }); test("A non-singleton instance is never cached", function() { var container = new Container(); var PostView = factory(); container.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"); }); test("A non-instantiated property is not instantiated", function() { var container = new Container(); var template = function() {}; container.register('template:foo', template, { instantiate: false }); equal(container.lookup('template:foo'), template); }); test("A failed lookup returns undefined", function() { var container = new Container(); equal(container.lookup('doesnot:exist'), undefined); }); test("An invalid factory throws an error", function() { var container = new Container(); container.register('controller:foo', {}); throws(function() { container.lookup('controller:foo'); }, /Failed to create an instance of \'controller:foo\'/); }); test("Injecting a failed lookup raises an error", function() { Ember.MODEL_FACTORY_INJECTIONS = true; var container = new Container(); var fooInstance = {}; var fooFactory = {}; var Foo = { create: function(args) { return fooInstance; }, extend: function(args) { return fooFactory; } }; container.register('model:foo', Foo); container.injection('model:foo', 'store', 'store:main'); throws(function() { container.lookup('model:foo'); }); }); test("Injecting a falsy value does not raise an error", function() { var container = new Container(); var ApplicationController = factory(); container.register('controller:application', ApplicationController); container.register('user:current', null, { instantiate: false }); container.injection('controller:application', 'currentUser', 'user:current'); equal(container.lookup('controller:application').currentUser, null); }); test("Destroying the container destroys any cached singletons", function() { var container = new Container(); var PostController = factory(); var PostView = factory(); var template = function() {}; container.register('controller:post', PostController); container.register('view:post', PostView, { singleton: false }); container.register('template:post', template, { instantiate: false }); container.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"); }); test("The container can take a hook to resolve factories lazily", function() { var container = new Container(); var PostController = factory(); container.resolver = function(fullName) { if (fullName === 'controller:post') { return PostController; } }; var postController = container.lookup('controller:post'); ok(postController instanceof PostController, "The correct factory was provided"); }); test("The container respect the resolver hook for `has`", function() { var container = new Container(); var PostController = factory(); container.resolver = function(fullName) { if (fullName === 'controller:post') { return PostController; } }; ok(container.has('controller:post'), "the `has` method uses the resolver hook"); }); test("The container normalizes names before resolving", function() { var container = new Container(); var PostController = factory(); container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); var postController = container.lookup('controller:normalized'); ok(postController instanceof PostController, "Normalizes the name before resolving"); }); test("The container normalizes names when unregistering", function() { var container = new Container(); var PostController = factory(); container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); var postController = container.lookup('controller:normalized'); ok(postController instanceof PostController, "Normalizes the name before resolving"); container.unregister('controller:post'); postController = container.lookup('controller:normalized'); equal(postController, undefined); }); test("The container normalizes names when resolving", function() { var container = new Container(); var PostController = factory(); container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); var type = container.resolve('controller:normalized'); equal(type === PostController, true, "Normalizes the name when resolving"); }); test("The container normalizes names when looking factory up", function() { var container = new Container(); var PostController = factory(); container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); var fact = container.lookupFactory('controller:normalized'); equal(fact.toString() === PostController.extend().toString(), true, "Normalizes the name when looking factory up"); }); test("The container normalizes names when checking if the factory or instance is present", function() { var container = new Container(); var PostController = factory(); container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); var isPresent = container.has('controller:normalized'); equal(isPresent, true, "Normalizes the name when checking if the factory or instance is present"); }); test("validateFullName throws an error if name is incorrect", function() { var container = new Container(); var PostController = factory(); container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); throws(function() { container.lookupFactory('post'); }, 'TypeError: Invalid Fullname, expected: `type:name` got: post'); }); test("The container normalizes names when injecting", function() { var container = new Container(); var PostController = factory(); var user = { name: 'Stef' }; container.normalize = function(fullName) { return 'controller:post'; }; container.register('controller:post', PostController); container.register('user:post', user, { instantiate: false }); container.injection('controller:post', 'user', 'controller:normalized'); deepEqual(container.lookup('controller:post'), user, "Normalizes the name when injecting"); }); test("The container can get options that should be applied to all factories for a given type", function() { var container = new Container(); var PostView = factory(); container.resolver = function(fullName) { if (fullName === 'view:post') { return PostView; } }; container.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"); }); test("cannot register an `undefined` factory", function(){ var container = new Container(); throws(function(){ container.register('controller:apple', undefined); }, ''); }); test("can re-register a factory", function(){ var container = new Container(), FirstApple = factory('first'), SecondApple = factory('second'); container.register('controller:apple', FirstApple); container.register('controller:apple', SecondApple); ok(container.lookup('controller:apple') instanceof SecondApple); }); test("cannot re-register a factory if has been looked up", function(){ var container = new Container(), FirstApple = factory('first'), SecondApple = factory('second'); container.register('controller:apple', FirstApple); ok(container.lookup('controller:apple') instanceof FirstApple); throws(function(){ container.register('controller:apple', SecondApple); }, 'Cannot re-register: `controller:apple`, as it has already been looked up.'); ok(container.lookup('controller:apple') instanceof FirstApple); }); test('container.has should not accidentally cause injections on that factory to be run. (Mitigate merely on observing)', function(){ expect(1); var container = new Container(), FirstApple = factory('first'), SecondApple = factory('second'); SecondApple.extend = function(a,b,c) { ok(false, 'should not extend or touch the injected model, merely to inspect existence of another'); }; container.register('controller:apple', FirstApple); container.register('controller:second-apple', SecondApple); container.injection('controller:apple', 'badApple', 'controller:second-apple'); ok(container.has('controller:apple')); }); test('once resolved, always return the same result', function() { expect(1); var container = new Container(); container.resolver = function() { return 'bar'; }; var Bar = container.resolve('models:bar'); container.resolver = function() { return 'not bar'; }; equal(container.resolve('models:bar'), Bar); }); test('once looked up, assert if an injection is registered for the entry', function() { expect(1); var container = new Container(), Apple = factory(), Worm = factory(); container.register('apple:main', Apple); container.register('worm:main', Worm); var apple = container.lookup('apple:main'); throws(function() { container.injection('apple:main', 'worm', 'worm:main'); }, "Attempted to register an injection for a type that has already been looked up. ('apple:main', 'worm', 'worm:main')"); }); test("Once looked up, assert if a factoryInjection is registered for the factory", function() { expect(1); var container = new Container(), Apple = factory(), Worm = factory(); container.register('apple:main', Apple); container.register('worm:main', Worm); var AppleFactory = container.lookupFactory('apple:main'); throws(function() { container.factoryInjection('apple:main', 'worm', 'worm:main'); }, "Attempted to register a factoryInjection for a type that has already been looked up. ('apple:main', 'worm', 'worm:main')"); }); }); define("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.'); }); }); define("container/tests/sub_container_test", ["container/tests/container_helper","container"], function(__dependency1__, __dependency2__) { "use strict"; var factory = __dependency1__.factory; var o_create = __dependency1__.o_create; var setProperties = __dependency1__.setProperties; var Container = __dependency2__["default"]; var container; QUnit.module("Container (sub-containers)", { setup: function() { container = new Container(); var PostController = factory(); container.register('controller:post', PostController); }, teardown: function() { if (!container.isDestroyed) { container.destroy(); } } }); test("Singletons already found on the parent container will be found again on the sub-container", function() { var postController = container.lookup('controller:post'); var subContainer = container.child(); equal(postController, subContainer.lookup('controller:post')); }); test("Destroying a sub-container doesn't destroy any singletons on the parent", function() { var postController = container.lookup('controller:post'); var subContainer = container.child(); subContainer.destroy(); equal(postController.isDestroyed, undefined, "The parent's singletons are not destroyed"); }); test("Looking up a singleton that wasn't yet looked up on a child container will cache it on the child", function() { var subContainer1 = container.child(); var subContainer2 = container.child(); var postController1 = subContainer1.lookup('controller:post'); var postController2 = subContainer2.lookup('controller:post'); notEqual(postController1, postController2); }); test("Destroying a parent container destroys the sub-containers", function() { var subContainer1 = container.child(); var subContainer2 = container.child(); var postController1 = subContainer1.lookup('controller:post'); var postController2 = subContainer2.lookup('controller:post'); container.destroy(); equal(postController1.isDestroyed, true, "The child's singleton is destroyed"); equal(postController2.isDestroyed, true, "The child's singleton is destroyed"); }); test("Resolver is inherited from parent container", function() { var otherController = factory(); container.resolver = function(fullName) { return otherController; }; var subContainer = container.child(); equal(subContainer.resolve('controller:post'), otherController, 'should use parent resolver'); equal(container.resolve('controller:post'), otherController, 'should use resolver'); }); test("Type injections should be inherited", function() { var container = new Container(); var PostController = factory(); var Store = factory(); container.register('controller:post', PostController); container.register('store:main', Store); container.typeInjection('controller', 'store', 'store:main'); var store = container.lookup('store:main'); var childContainer = container.child(); var postController = childContainer.lookup('controller:post'); equal(postController.store, store); }); }); define("container/tests/sub_container_test.jshint", [], function() { "use strict"; module('JSHint - container/tests'); test('container/tests/sub_container_test.js should pass jshint', function() { ok(true, 'container/tests/sub_container_test.js should pass jshint.'); }); }); define("ember-application.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-application.js should pass jshint', function() { ok(true, 'ember-application.js should pass jshint.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-application/system/dag.jshint", [], function() { "use strict"; module('JSHint - ember-application/system'); test('ember-application/system/dag.js should pass jshint', function() { ok(true, 'ember-application/system/dag.js should pass jshint.'); }); }); define("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.'); }); }); define("ember-application/tests/system/application_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/array","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-handlebars","ember-runtime/system/object","ember-routing-handlebars/helpers/outlet","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__) { "use strict"; /*globals EmberDev */ var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var forEach = __dependency4__.forEach; var run = __dependency5__["default"]; var Application = __dependency6__["default"]; var DefaultResolver = __dependency7__["default"]; var Router = __dependency8__["default"]; var View = __dependency9__["default"]; var Controller = __dependency10__["default"]; var NoneLocation = __dependency11__["default"]; var EmberHandlebars = __dependency12__["default"]; var EmberObject = __dependency13__["default"]; var outletHelper = __dependency14__.outletHelper; var jQuery = __dependency15__["default"]; var trim = jQuery.trim; var view, app, application, originalLookup, originalDebug; QUnit.module("Ember.Application", { setup: function() { originalLookup = Ember.lookup; originalDebug = Ember.debug; jQuery("#qunit-fixture").html("
HI
HI
"); run(function() { application = Application.create({ rootElement: '#one', router: null }); }); }, teardown: function() { jQuery("#qunit-fixture").empty(); Ember.debug = originalDebug; Ember.lookup = originalLookup; if (application) { run(application, 'destroy'); } if (app) { run(app, 'destroy'); } } }); test("you can make a new application in a non-overlapping element", function() { run(function() { app = Application.create({ rootElement: '#two', router: null }); }); run(app, 'destroy'); ok(true, "should not raise"); }); test("you cannot make a new application that is a parent of an existing application", function() { expectAssertion(function() { run(function() { Application.create({ rootElement: '#qunit-fixture' }); }); }); }); test("you cannot make a new application that is a descendent of an existing application", function() { expectAssertion(function() { run(function() { Application.create({ rootElement: '#one-child' }); }); }); }); test("you cannot make a new application that is a duplicate of an existing application", function() { expectAssertion(function() { run(function() { Application.create({ rootElement: '#one' }); }); }); }); test("you cannot make two default applications without a rootElement error", function() { expectAssertion(function() { run(function() { Application.create({ router: false }); }); }); }); test("acts like a namespace", function() { var lookup = Ember.lookup = {}, app; run(function() { app = lookup.TestApp = Application.create({ rootElement: '#two', router: false }); }); Ember.BOOTED = false; app.Foo = EmberObject.extend(); equal(app.Foo.toString(), "TestApp.Foo", "Classes pick up their parent namespace"); }); QUnit.module("Ember.Application initialization", { teardown: function() { if (app) { run(app, 'destroy'); } Ember.TEMPLATES = {}; } }); test('initialized application go to initial route', function() { run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); app.register('template:application', EmberHandlebars.compile("{{outlet}}") ); Ember.TEMPLATES.index = EmberHandlebars.compile( "

Hi from index

" ); }); equal(jQuery('#qunit-fixture h1').text(), "Hi from index"); }); test("initialize application via initialize call", function() { run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); app.ApplicationView = View.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, true, "Router was set from initialize call"); equal(router.location instanceof NoneLocation, true, "Location was set from location implementation name"); }); test("initialize application with stateManager via initialize call from Router class", function() { run(function() { app = Application.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, true, "Router was set from initialize call"); equal(jQuery("#qunit-fixture h1").text(), "Hello!"); }); test("ApplicationView is inserted into the page", function() { run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); app.ApplicationView = View.extend({ render: function(buffer) { buffer.push("

Hello!

"); } }); app.ApplicationController = Controller.extend(); app.Router.reopen({ location: 'none' }); }); equal(jQuery("#qunit-fixture h1").text(), "Hello!"); }); test("Minimal Application initialized with just an application template", function() { jQuery('#qunit-fixture').html(''); run(function () { app = Application.create({ rootElement: '#qunit-fixture' }); }); equal(trim(jQuery('#qunit-fixture').text()), 'Hello World'); }); 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.debug; var messages = []; Ember.LOG_VERSION = true; Ember.debug = function(message) { messages.push(message); }; Ember.libraries.register("my-lib", "2.0.0a"); run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); }); equal(messages[1], "Ember : " + Ember.VERSION); equal(messages[2], "Handlebars : " + EmberHandlebars.VERSION); equal(messages[3], "jQuery : " + jQuery().jquery); equal(messages[4], "my-lib : " + "2.0.0a"); Ember.libraries.deRegister("my-lib"); Ember.LOG_VERSION = false; Ember.debug = debug; }); test('disable log version of libraries with an ENV var', function() { var logged = false; Ember.LOG_VERSION = false; Ember.debug = function(message) { logged = true; }; jQuery("#qunit-fixture").empty(); run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); app.Router.reopen({ location: 'none' }); }); ok(!logged, 'library version logging skipped'); }); test("can resolve custom router", function(){ var CustomRouter = Router.extend(); var CustomResolver = DefaultResolver.extend({ resolveOther: function(parsedName){ if (parsedName.type === "router") { return CustomRouter; } else { return this._super(parsedName); } } }); app = run(function(){ return Application.create({ Resolver: CustomResolver }); }); ok(app.__container__.lookup('router:main') instanceof CustomRouter, 'application resolved the correct router'); }); test("throws helpful error if `app.then` is used", function() { run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); }); expectDeprecation(function() { run(app, 'then', Ember.K); }, /Do not use `.then` on an instance of Ember.Application. Please use the `.ready` hook instead./); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; /*jshint newcap:false */ var Controller = __dependency1__["default"]; var Container = __dependency3__["default"]; var A = __dependency4__.A; var ArrayController = __dependency5__["default"]; var computed = __dependency6__.computed; QUnit.module("Controller dependencies"); test("If a controller specifies a dependency, but does not have a container it should error", function(){ var AController = Controller.extend({ needs: 'posts' }); expectAssertion(function(){ AController.create(); }, /specifies `needs`, but does not have a container. Please ensure this controller was instantiated with a container./); }); test("If a controller specifies a dependency, it is accessible", function() { var container = new Container(); container.register('controller:post', Controller.extend({ needs: 'posts' })); container.register('controller:posts', Controller.extend()); var postController = container.lookup('controller:post'), postsController = container.lookup('controller:posts'); equal(postsController, postController.get('controllers.posts'), "controller.posts must be auto synthesized"); }); test("If a controller specifies an unavailable dependency, it raises", function() { var container = new Container(); container.register('controller:post', Controller.extend({ needs: ['comments'] })); throws(function() { container.lookup('controller:post'); }, /controller:comments/); container.register('controller:blog', Controller.extend({ needs: ['posts', 'comments'] })); throws(function() { container.lookup('controller:blog'); }, /controller:posts, controller:comments/); }); test("Mixin sets up controllers if there is needs before calling super", function() { var container = new Container(); container.register('controller:other', ArrayController.extend({ needs: 'posts', model: computed.alias('controllers.posts') })); container.register('controller:another', ArrayController.extend({ needs: 'posts', modelBinding: 'controllers.posts' })); container.register('controller:posts', ArrayController.extend()); container.lookup('controller:posts').set('model', A(['a','b','c'])); deepEqual(['a','b','c'], container.lookup('controller:other').toArray()); deepEqual(['a','b','c'], container.lookup('controller:another').toArray()); }); test("raises if trying to get a controller that was not pre-defined in `needs`", function() { var container = new Container(); container.register('controller:foo', Controller.extend()); container.register('controller:bar', Controller.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'); }); test ("setting the value of a controller dependency should not be possible", function(){ var container = new Container(); container.register('controller:post', Controller.extend({ needs: 'posts' })); container.register('controller:posts', Controller.extend()); var postController = container.lookup('controller:post'), postsController = 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"); }); test("raises if a dependency with a period is requested", function() { var container = new Container(); container.register('controller:big.bird', Controller.extend()); container.register('controller:foo', Controller.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'); }); test("can unit test controllers with `needs` dependencies by stubbing their `controllers` properties", function() { expect(1); var BrotherController = Controller.extend({ needs: 'sister', foo: computed.alias('controllers.sister.foo') }); var broController = BrotherController.create({ controllers: { sister: { foo: 5 } } }); equal(broController.get('foo'), 5, "`needs` dependencies can be stubbed"); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var jQuery = __dependency1__["default"]; var run = __dependency2__["default"]; var Application = __dependency3__["default"]; var DefaultResolver = __dependency4__["default"]; var application; QUnit.module("Ember.Application Depedency Injection – customResolver",{ setup: function() { function fallbackTemplate() { return "

Fallback

"; } var Resolver = DefaultResolver.extend({ resolveTemplate: function(resolvable) { var resolvedTemplate = this._super(resolvable); if (resolvedTemplate) { return resolvedTemplate; } return fallbackTemplate; } }); application = run(function() { return Application.create({ Resolver: Resolver, rootElement: '#qunit-fixture' }); }); }, teardown: function() { run(application, 'destroy'); } }); test("a resolver can be supplied to application", function() { equal(jQuery("h1", application.rootElement).text(), "Fallback"); }); }); define("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.'); }); }); define("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-handlebars","ember-runtime/system/namespace","ember-application/system/application"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.TEMPLATES var run = __dependency2__["default"]; var Logger = __dependency3__["default"]; var Controller = __dependency4__["default"]; var EmberObject = __dependency5__["default"]; var EmberHandlebars = __dependency6__["default"]; var Namespace = __dependency7__["default"]; var Application = __dependency8__["default"]; var locator, application, lookup, originalLookup, originalLoggerInfo; QUnit.module("Ember.Application Depedency Injection", { setup: function() { originalLookup = Ember.lookup; application = run(Application, 'create'); locator = application.__container__; originalLoggerInfo = Logger.info; }, teardown: function() { Ember.TEMPLATES = {}; Ember.lookup = originalLookup; run(application, 'destroy'); var UserInterfaceNamespace = Namespace.NAMESPACES_BY_ID['UserInterface']; if (UserInterfaceNamespace) { run(UserInterfaceNamespace, 'destroy'); } Logger.info = originalLoggerInfo; } }); test('the default resolver can look things up in other namespaces', function() { var UserInterface = Ember.lookup.UserInterface = Namespace.create(); UserInterface.NavigationController = Controller.extend(); var nav = locator.lookup('controller:userInterface/navigation'); ok(nav instanceof UserInterface.NavigationController, "the result should be an instance of the specified class"); }); test('the default resolver looks up templates in Ember.TEMPLATES', function() { function fooTemplate() {} function fooBarTemplate() {} function fooBarBazTemplate() {} Ember.TEMPLATES['foo'] = fooTemplate; Ember.TEMPLATES['fooBar'] = fooBarTemplate; Ember.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"); }); test('the default resolver looks up basic name as no prefix', function() { ok(Controller.detect(locator.lookup('controller:basic')), 'locator looksup correct controller'); }); function detectEqual(first, second, message) { ok(first.detect(second), message); } test('the default resolver looks up arbitrary types on the namespace', function() { application.FooManager = EmberObject.extend({}); detectEqual(application.FooManager, locator.resolver('manager:foo'),"looks up FooManager on application"); }); test("the default resolver resolves models on the namespace", function() { application.Post = EmberObject.extend({}); detectEqual(application.Post, locator.lookupFactory('model:post'), "looks up Post model on application"); }); test("the default resolver resolves helpers from EmberHandlebars.helpers", function(){ function fooresolvertestHelper(){ return 'FOO'; } function barBazResolverTestHelper(){ return 'BAZ'; } EmberHandlebars.registerHelper('fooresolvertest', fooresolvertestHelper); EmberHandlebars.registerHelper('bar-baz-resolver-test', barBazResolverTestHelper); equal(fooresolvertestHelper, locator.lookup('helper:fooresolvertest'), "looks up fooresolvertestHelper helper"); equal(barBazResolverTestHelper, locator.lookup('helper:bar-baz-resolver-test'), "looks up barBazResolverTestHelper helper"); }); 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"); }); test("the default resolver throws an error if the fullName to resolve is invalid", function(){ raises(function(){ locator.resolve(undefined);}, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve(null); }, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve(''); }, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve(''); }, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve(':'); }, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve('model'); }, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve('model:'); }, TypeError, /Invalid fullName/ ); raises(function(){ locator.resolve(':type'); }, TypeError, /Invalid fullName/ ); }); test("the default resolver logs hits if `LOG_RESOLVER` is set", function() { expect(3); application.LOG_RESOLVER = true; application.ScoobyDoo = EmberObject.extend(); application.toString = function() { return 'App'; }; Logger.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'); }; locator.resolve('doo:scooby'); }); test("the default resolver logs misses if `LOG_RESOLVER` is set", function() { expect(3); application.LOG_RESOLVER = true; application.toString = function() { return 'App'; }; Logger.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'); }; locator.resolve('doo:scooby'); }); test("doesn't log without LOG_RESOLVER", function(){ var infoCount = 0; application.ScoobyDoo = EmberObject.extend(); Logger.info = function(symbol, name) { infoCount = infoCount + 1; }; locator.resolve('doo:scooby'); locator.resolve('doo:scrappy'); equal(infoCount, 0, 'Logger.info should not be called if LOG_RESOLVER is not set'); }); }); define("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.'); }); }); define("ember-application/tests/system/dependency_injection/normalization_test", ["ember-metal/run_loop","ember-metal/array","ember-application/system/application"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var run = __dependency1__["default"]; var forEach = __dependency2__.forEach; var Application = __dependency3__["default"]; var application, locator; QUnit.module("Ember.Application Depedency Injection – normalization", { setup: function() { application = run(Application, 'create'); locator = application.__container__; }, teardown: function() { run(application, 'destroy'); } }); test('normalization', function() { ok(locator.normalize, 'locator#normalize is present'); equal(locator.normalize('foo:bar'), 'foo:bar'); equal(locator.normalize('controller:posts'), 'controller:posts'); equal(locator.normalize('controller:posts_index'), 'controller:postsIndex'); equal(locator.normalize('controller:posts.index'), 'controller:postsIndex'); equal(locator.normalize('controller:posts.post.index'), 'controller:postsPostIndex'); equal(locator.normalize('controller:posts_post.index'), 'controller:postsPostIndex'); equal(locator.normalize('controller:posts.post_index'), 'controller:postsPostIndex'); equal(locator.normalize('controller:postsIndex'), 'controller:postsIndex'); equal(locator.normalize('controller:blogPosts.index'), 'controller:blogPostsIndex'); equal(locator.normalize('controller:blog/posts.index'), 'controller:blog/postsIndex'); equal(locator.normalize('controller:blog/posts.post.index'), 'controller:blog/postsPostIndex'); equal(locator.normalize('controller:blog/posts_post.index'), 'controller:blog/postsPostIndex'); equal(locator.normalize('template:blog/posts_index'), 'template:blog/posts_index'); }); test('normalization is indempotent', function() { var examples = ['controller:posts', 'controller:posts.post.index', 'controller:blog/posts.post_index', 'template:foo_bar']; forEach.call(examples, function (example) { equal(locator.normalize(locator.normalize(example)), locator.normalize(example)); }); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; // lookup, etc var run = __dependency2__["default"]; var Application = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var DefaultResolver = __dependency5__["default"]; var guidFor = __dependency6__.guidFor; var originalLookup, App, originalModelInjections; QUnit.module("Ember.Application Dependency Injection – toString",{ setup: function() { originalModelInjections = Ember.MODEL_FACTORY_INJECTIONS; Ember.MODEL_FACTORY_INJECTIONS = true; originalLookup = Ember.lookup; run(function(){ App = Application.create(); Ember.lookup = { App: App }; }); App.Post = EmberObject.extend(); }, teardown: function() { Ember.lookup = originalLookup; run(App, 'destroy'); Ember.MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); test("factories", function() { var PostFactory = App.__container__.lookupFactory('model:post'); equal(PostFactory.toString(), 'App.Post', 'expecting the model to be post'); }); test("instances", function() { var post = App.__container__.lookup('model:post'); var guid = guidFor(post); equal(post.toString(), '', 'expecting the model to be post'); }); test("with a custom resolver", function() { run(App,'destroy'); run(function(){ App = Application.create({ Resolver: DefaultResolver.extend({ makeToString: function(factory, fullName) { return fullName; } }) }); }); App.__container__.register('model:peter', EmberObject.extend()); var peter = App.__container__.lookup('model:peter'); var guid = guidFor(peter); equal(peter.toString(), '', 'expecting the supermodel to be peter'); }); }); define("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.'); }); }); define("ember-application/tests/system/dependency_injection_test", ["ember-metal/run_loop","ember-metal/property_get","ember-metal/property_set","ember-metal/array","ember-runtime/system/object","ember-application/system/application","ember-runtime/system/container"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var run = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var forEach = __dependency4__.forEach; var EmberObject = __dependency5__["default"]; var Application = __dependency6__["default"]; var Container = __dependency7__["default"]; var EmberApplication = Application; var locator, originalLookup = Ember.lookup, lookup, application, originalModelInjections; QUnit.module("Ember.Application Dependency Injection", { setup: function() { originalModelInjections = Ember.MODEL_FACTORY_INJECTIONS; Ember.MODEL_FACTORY_INJECTIONS = true; application = run(EmberApplication, 'create'); application.Person = EmberObject.extend({}); application.Orange = EmberObject.extend({}); application.Email = EmberObject.extend({}); application.User = EmberObject.extend({}); application.PostIndexController = EmberObject.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}); locator = application.__container__; lookup = Ember.lookup = {}; }, teardown: function() { run(application, 'destroy'); application = locator = null; Ember.lookup = originalLookup; Ember.MODEL_FACTORY_INJECTIONS = originalModelInjections; } }); 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); }); test('registered entities can be looked up later', function() { equal(locator.resolve('model:person'), application.Person); equal(locator.resolve('model:user'), application.User); equal(locator.resolve('fruit:favorite'), application.Orange); equal(locator.resolve('communication:main'), application.Email); equal(locator.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'); }); test('injections', function() { application.inject('model', 'fruit', 'fruit:favorite'); application.inject('model:user', 'communication', 'communication:main'); var user = locator.lookup('model:user'), person = locator.lookup('model:person'), fruit = locator.lookup('fruit:favorite'); equal(user.get('fruit'), fruit); equal(person.get('fruit'), fruit); ok(application.Email.detectInstance(user.get('communication'))); }); }); define("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.'); }); }); define("ember-application/tests/system/initializers_test", ["ember-metal/run_loop","ember-application/system/application","ember-metal/array","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var run = __dependency1__["default"]; var Application = __dependency2__["default"]; var indexOf = __dependency3__.indexOf; var jQuery = __dependency4__["default"]; var oldInitializers, app; QUnit.module("Ember.Application initializers", { setup: function() { }, teardown: function() { if (app) { run(function() { app.destroy(); }); } } }); test("initializers can be registered in a specified order", function() { var order = []; var MyApplication = Application.extend(); MyApplication.initializer({ name: 'fourth', after: 'third', initialize: function(container) { order.push('fourth'); } }); MyApplication.initializer({ name: 'second', after: 'first', before: 'third', initialize: function(container) { order.push('second'); } }); MyApplication.initializer({ name: 'fifth', after: 'fourth', before: 'sixth', initialize: function(container) { order.push('fifth'); } }); MyApplication.initializer({ name: 'first', before: 'second', initialize: function(container) { order.push('first'); } }); MyApplication.initializer({ name: 'third', initialize: function(container) { order.push('third'); } }); MyApplication.initializer({ name: 'sixth', initialize: function(container) { order.push('sixth'); } }); run(function() { app = MyApplication.create({ router: false, rootElement: '#qunit-fixture' }); }); deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']); }); test("initializers can have multiple dependencies", function () { var order = [], a = { name: "a", before: "b", initialize: function(container) { order.push('a'); } }, b = { name: "b", initialize: function(container) { order.push('b'); } }, c = { name: "c", after: "b", initialize: function(container) { order.push('c'); } }, afterB = { name: "after b", after: "b", initialize: function(container) { order.push("after b"); } }, afterC = { name: "after c", after: "c", initialize: function(container) { order.push("after c"); } }; Application.initializer(b); Application.initializer(a); Application.initializer(afterC); Application.initializer(afterB); Application.initializer(c); run(function() { app = Application.create({ router: false, rootElement: '#qunit-fixture' }); }); ok(indexOf.call(order, a.name) < indexOf.call(order, b.name), 'a < b'); ok(indexOf.call(order, b.name) < indexOf.call(order, c.name), 'b < c'); ok(indexOf.call(order, b.name) < indexOf.call(order, afterB.name), 'b < afterB'); ok(indexOf.call(order, c.name) < indexOf.call(order, afterC.name), 'c < afterC'); }); test("initializers set on Application subclasses should not be shared between apps", function(){ var firstInitializerRunCount = 0, secondInitializerRunCount = 0; var FirstApp = Application.extend(); FirstApp.initializer({ name: 'first', initialize: function(container) { firstInitializerRunCount++; } }); var SecondApp = Application.extend(); SecondApp.initializer({ name: 'second', initialize: function(container) { secondInitializerRunCount++; } }); jQuery('#qunit-fixture').html('
'); run(function() { var firstApp = 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(function() { var secondApp = SecondApp.create({ router: false, rootElement: '#qunit-fixture #second' }); }); equal(firstInitializerRunCount, 1, 'second initializer only was run'); equal(secondInitializerRunCount, 1, 'second initializer only was run'); }); test("initializers are concatenated", function(){ var firstInitializerRunCount = 0, secondInitializerRunCount = 0; var FirstApp = Application.extend(); FirstApp.initializer({ name: 'first', initialize: function(container) { firstInitializerRunCount++; } }); var SecondApp = FirstApp.extend(); SecondApp.initializer({ name: 'second', initialize: function(container) { secondInitializerRunCount++; } }); jQuery('#qunit-fixture').html('
'); run(function() { var firstApp = 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(function() { var secondApp = 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'); }); test("initializers are per-app", function(){ expect(0); var FirstApp = Application.extend(); FirstApp.initializer({ name: 'shouldNotCollide', initialize: function(container) {} }); var SecondApp = Application.extend(); SecondApp.initializer({ name: 'shouldNotCollide', initialize: function(container) {} }); }); }); define("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.'); }); }); define("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-runtime/keys","ember-routing"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; /*globals EmberDev */ var run = __dependency1__["default"]; var Application = __dependency2__["default"]; var View = __dependency3__["default"]; var Controller = __dependency4__["default"]; var Route = __dependency5__["default"]; var RSVP = __dependency6__["default"]; var keys = __dependency7__["default"]; 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(function() { App = Application.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(App, 'destroy'); logs = App = null; } }); function visit(path) { QUnit.stop(); var promise = run(function(){ return new RSVP.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(promise, 'then', resolve, reject); } }; } test("log class generation if logging enabled", function() { if (EmberDev && EmberDev.runningProdBuild){ ok(true, 'Logging does not occur in production builds'); return; } run(App, 'advanceReadiness'); visit('/posts').then(function() { equal(Ember.keys(logs).length, 6, 'expected logs'); }); }); test("do NOT log class generation if logging disabled", function() { App.reopen({ LOG_ACTIVE_GENERATION: false }); run(App, 'advanceReadiness'); visit('/posts').then(function() { equal(keys(logs).length, 0, 'expected no logs'); }); }); test("actively generated classes get logged", function() { if (EmberDev && EmberDev.runningProdBuild){ ok(true, 'Logging does not occur in production builds'); return; } run(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'); }); }); test("predefined classes do not get logged", function() { App.ApplicationController = Controller.extend(); App.PostsController = Controller.extend(); App.ApplicationRoute = Route.extend(); App.PostsRoute = Route.extend(); run(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(function() { App = Application.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(App, 'destroy'); logs = App = null; } }); 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(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.'); }); }); test("do not log when template and view are missing when flag is not true", function() { App.reopen({ LOG_VIEW_LOOKUPS: false }); run(App, 'advanceReadiness'); visit('/posts').then(function() { equal(keys(logs).length, 0, 'expected no logs'); }); }); 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.extend({templateName: 'foo'})); run(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.'); }); }); test("do not log which views are used with templates when flag is not true", function() { App.reopen({ LOG_VIEW_LOOKUPS: false }); run(App, 'advanceReadiness'); visit('/posts').then(function() { equal(keys(logs).length, 0, 'expected no logs'); }); }); }); define("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.'); }); }); define("ember-application/tests/system/readiness_test", ["ember-metal/run_loop","ember-application/system/application"], function(__dependency1__, __dependency2__) { "use strict"; var run = __dependency1__["default"]; var Application = __dependency2__["default"]; var EmberApplication = Application; var jQuery, 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; itemplate was called")); view = EmberView.create({ container: container, templateName: 'testTemplate' }); appendView(); ok(view.$('#twas-called').length, "the named template was called"); }); test("template view should call the function of the associated template with itself as the context", function() { container.register('template:testTemplate', EmberHandlebars.compile("

template was called for {{view.personName}}. Yea {{view.personName}}

")); view = EmberView.createWithMixins({ container: container, templateName: 'testTemplate', _personName: "Tom DAAAALE", _i: 0, personName: computed(function() { this._i++; return this._personName + this._i; }) }); appendView(); equal("template was called for Tom DAAAALE1. Yea Tom DAAAALE1", view.$('#twas-called').text(), "the named template was called with the view as the data source"); }); test("should allow values from normal JavaScript hash objects to be used", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#with view.person}}{{firstName}} {{lastName}} (and {{pet.name}}){{/with}}'), person: { firstName: 'Señor', lastName: 'CFC', pet: { name: 'Fido' } } }); appendView(); equal(view.$().text(), "Señor CFC (and Fido)", "prints out values from a hash"); }); test("htmlSafe should return an instance of Handlebars.SafeString", function() { var safeString = htmlSafe("you need to be more bold"); ok(safeString instanceof Handlebars.SafeString, "should return SafeString"); }); test("should escape HTML in normal mustaches", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{view.output}}'), output: "you need to be more bold" }); appendView(); equal(view.$('b').length, 0, "does not create an element"); equal(view.$().text(), 'you need to be more bold', "inserts entities, not elements"); run(function() { 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"); }); test("should not escape HTML in triple mustaches", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{{view.output}}}'), output: "you need to be more bold" }); appendView(); equal(view.$('b').length, 1, "creates an element"); run(function() { set(view, 'output', "you are so super"); }); equal(view.$('i').length, 1, "creates an element when value is updated"); }); test("should not escape HTML if string is a Handlebars.SafeString", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{view.output}}'), output: new Handlebars.SafeString("you need to be more bold") }); appendView(); equal(view.$('b').length, 1, "creates an element"); run(function() { set(view, 'output', new Handlebars.SafeString("you are so super")); }); equal(view.$('i').length, 1, "creates an element when value is updated"); }); test("child views can be inserted using the {{view}} Handlebars helper", function() { container.register('template:nester', EmberHandlebars.compile("

Hello {{world}}

{{view \"TemplateTests.LabelView\"}}")); container.register('template:nested', EmberHandlebars.compile("
Goodbye {{cruel}} {{world}}
")); var context = { world: "world!" }; TemplateTests.LabelView = EmberView.extend({ container: container, tagName: "aside", templateName: 'nested' }); view = EmberView.create({ container: container, templateName: 'nester', context: context }); set(context, 'cruel', "cruel"); appendView(); 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"); }); test("should accept relative paths to views", function() { view = EmberView.create({ template: EmberHandlebars.compile('Hey look, at {{view "view.myCool.view"}}'), myCool: EmberObject.create({ view: EmberView.extend({ template: EmberHandlebars.compile("my cool view") }) }) }); appendView(); equal(view.$().text(), "Hey look, at my cool view"); }); test("child views can be inserted inside a bind block", function() { container.register('template:nester', EmberHandlebars.compile("

Hello {{world}}

{{view \"TemplateTests.BQView\"}}")); container.register('template:nested', EmberHandlebars.compile("
Goodbye {{#with content}}{{blah}} {{view \"TemplateTests.OtherView\"}}{{/with}} {{world}}
")); container.register('template:other', EmberHandlebars.compile("cruel")); var context = { world: "world!" }; TemplateTests.BQView = EmberView.extend({ container: container, tagName: "blockquote", templateName: 'nested' }); TemplateTests.OtherView = EmberView.extend({ container: container, templateName: 'other' }); view = EmberView.create({ container: container, context: context, templateName: 'nester' }); set(context, 'content', EmberObject.create({ blah: "wot" })); appendView(); 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"); }); test("View should bind properties in the parent context", function() { var context = { content: EmberObject.create({ wham: 'bam' }), blam: "shazam" }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('

{{#with content}}{{wham}}-{{../blam}}{{/with}}

') }); appendView(); equal(view.$('#first').text(), "bam-shazam", "renders parent properties"); }); test("using Handlebars helper that doesn't exist should result in an error", function() { var names = [{ name: 'Alex' }, { name: 'Stef' }], context = { content: A(names) }; throws(function() { view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{#group}}{{#each name in content}}{{name}}{{/each}}{{/group}}') }); appendView(); }, "Missing helper: 'group'"); }); test("View should bind properties in the grandparent context", function() { var context = { content: EmberObject.create({ wham: 'bam', thankYou: EmberObject.create({ value: "ma'am" }) }), blam: "shazam" }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('

{{#with content}}{{#with thankYou}}{{value}}-{{../wham}}-{{../../blam}}{{/with}}{{/with}}

') }); appendView(); equal(view.$('#first').text(), "ma'am-bam-shazam", "renders grandparent properties"); }); test("View should update when a property changes and the bind helper is used", function() { container.register('template:foo', EmberHandlebars.compile('

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

')); view = EmberView.create({ container: container, templateName: 'foo', content: EmberObject.create({ wham: 'bam', thankYou: "ma'am" }) }); appendView(); equal(view.$('#first').text(), "bam", "precond - view renders Handlebars template"); run(function() { set(get(view, 'content'), 'wham', 'bazam'); }); equal(view.$('#first').text(), "bazam", "view updates when a bound property changes"); }); test("View should not use keyword incorrectly - Issue #1315", function() { container.register('template:foo', EmberHandlebars.compile('{{#each value in view.content}}{{value}}-{{#each option in view.options}}{{option.value}}:{{option.label}} {{/each}}{{/each}}')); view = EmberView.create({ container: container, templateName: 'foo', content: A(['X', 'Y']), options: A([ { label: 'One', value: 1 }, { label: 'Two', value: 2 } ]) }); appendView(); equal(view.$().text(), 'X-1:One 2:Two Y-1:One 2:Two '); }); test("View should update when a property changes and no bind helper is used", function() { container.register('template:foo', EmberHandlebars.compile('

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

')); var templates = EmberObject.create({ foo: EmberHandlebars.compile('

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

') }); view = EmberView.create({ container: container, templateName: 'foo', content: EmberObject.create({ wham: 'bam', thankYou: "ma'am" }) }); appendView(); equal(view.$('#first').text(), "bam", "precond - view renders Handlebars template"); run(function() { set(get(view, 'content'), 'wham', 'bazam'); }); equal(view.$('#first').text(), "bazam", "view updates when a bound property changes"); }); test("View should update when the property used with the #with helper changes", function() { container.register('template:foo', EmberHandlebars.compile('

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

')); view = EmberView.create({ container: container, templateName: 'foo', content: EmberObject.create({ wham: 'bam', thankYou: "ma'am" }) }); appendView(); equal(view.$('#first').text(), "bam", "precond - view renders Handlebars template"); run(function() { set(view, 'content', EmberObject.create({ wham: 'bazam' })); }); equal(view.$('#first').text(), "bazam", "view updates when a bound property changes"); }); test("should not update when a property is removed from the view", function() { container.register('template:foo', EmberHandlebars.compile('

{{#bind "view.content"}}{{#bind "foo"}}{{bind "baz"}}{{/bind}}{{/bind}}

')); view = EmberView.create({ container: container, templateName: 'foo', content: EmberObject.create({ foo: EmberObject.create({ baz: "unicorns" }) }) }); appendView(); equal(view.$('#first').text(), "unicorns", "precond - renders the bound value"); var oldContent = get(view, 'content'); run(function() { set(view, 'content', EmberObject.create({ foo: EmberObject.create({ baz: "ninjas" }) })); }); equal(view.$('#first').text(), 'ninjas', "updates to new content value"); run(function() { set(oldContent, 'foo.baz', 'rockstars'); }); run(function() { set(oldContent, 'foo.baz', 'ewoks'); }); equal(view.$('#first').text(), "ninjas", "does not update removed object"); }); test("Handlebars templates update properties if a content object changes", function() { container.register('template:menu', EmberHandlebars.compile('

Today\'s Menu

{{#bind "view.coffee"}}

{{color}} coffee

{{bind "price"}}{{/bind}}')); run(function() { view = EmberView.create({ container: container, templateName: 'menu', coffee: EmberObject.create({ color: 'brown', price: '$4' }) }); }); appendView(); equal(view.$('h2').text(), "brown coffee", "precond - renders color correctly"); equal(view.$('#price').text(), '$4', "precond - renders price correctly"); run(function() { set(view, 'coffee', EmberObject.create({ color: "mauve", price: "$4.50" })); }); equal(view.$('h2').text(), "mauve coffee", "should update name field when content changes"); equal(view.$('#price').text(), "$4.50", "should update price field when content changes"); run(function() { set(view, 'coffee', EmberObject.create({ color: "mauve", price: "$5.50" })); }); equal(view.$('h2').text(), "mauve coffee", "should update name field when content changes"); equal(view.$('#price').text(), "$5.50", "should update price field when content changes"); run(function() { set(view, 'coffee.price', "$5"); }); equal(view.$('#price').text(), "$5", "should update price field when price property is changed"); run(function() { view.destroy(); }); }); test("Template updates correctly if a path is passed to the bind helper", function() { container.register('template:menu', EmberHandlebars.compile('

{{bind "view.coffee.price"}}

')); view = EmberView.create({ container: container, templateName: 'menu', coffee: EmberObject.create({ price: '$4' }) }); appendView(); equal(view.$('h1').text(), "$4", "precond - renders price"); run(function() { set(view, 'coffee.price', "$5"); }); equal(view.$('h1').text(), "$5", "updates when property changes"); run(function() { set(view, 'coffee', { price: "$6" }); }); equal(view.$('h1').text(), "$6", "updates when parent property changes"); }); test("Template updates correctly if a path is passed to the bind helper and the context object is an ObjectController", function() { container.register('template:menu', EmberHandlebars.compile('

{{bind "view.coffee.price"}}

')); var controller = ObjectController.create(); var realObject = EmberObject.create({ price: "$4" }); set(controller, 'model', realObject); view = EmberView.create({ container: container, templateName: 'menu', coffee: controller }); appendView(); equal(view.$('h1').text(), "$4", "precond - renders price"); run(function() { set(realObject, 'price', "$5"); }); equal(view.$('h1').text(), "$5", "updates when property is set on real object"); run(function() { set(controller, 'price', "$6" ); }); equal(view.$('h1').text(), "$6", "updates when property is set on object controller"); }); test("should update the block when object passed to #if helper changes", function() { container.register('template:menu', EmberHandlebars.compile('

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

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

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

')); view = EmberView.create({ container: container, templateName: 'advice', onDrugs: true, doWellInSchool: "Eat your vegetables" }); appendView(); equal(view.$('h1').text(), "", "hides block if true"); var tests = [false, null, undefined, [], '', 0]; forEach(tests, function(val) { run(function() { set(view, 'onDrugs', val); }); equal(view.$('h1').text(), 'Eat your vegetables', fmt("renders block when conditional is '%@'; %@", [String(val), typeOf(val)])); run(function() { set(view, 'onDrugs', true); }); equal(view.$('h1').text(), "", "precond - hides block when conditional is true"); }); }); test("should update the block when object passed to #if helper changes and an inverse is supplied", function() { container.register('template:menu', EmberHandlebars.compile('

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

')); view = EmberView.create({ container: container, templateName: 'menu', INCEPTION: "BOOOOOOOONG doodoodoodoodooodoodoodoo", inception: false, SAD: 'BOONG?' }); appendView(); equal(view.$('h1').text(), "BOONG?", "renders alternate if false"); run(function() { set(view, 'inception', true); }); var tests = [false, null, undefined, [], '', 0]; forEach(tests, function(val) { run(function() { set(view, 'inception', val); }); equal(view.$('h1').text(), 'BOONG?', fmt("renders alternate if %@", [String(val)])); run(function() { set(view, 'inception', true); }); equal(view.$('h1').text(), "BOOOOOOOONG doodoodoodoodooodoodoodoo", "precond - renders block when conditional is true"); }); }); test("edge case: child conditional should not render children if parent conditional becomes false", function() { var childCreated = false; view = EmberView.create({ cond1: true, cond2: false, viewClass: EmberView.extend({ init: function() { this._super(); childCreated = true; } }), template: EmberHandlebars.compile('{{#if view.cond1}}{{#if view.cond2}}{{#view view.viewClass}}test{{/view}}{{/if}}{{/if}}') }); appendView(); run(function() { // The order of these sets is important for the test view.set('cond2', true); view.set('cond1', false); }); ok(!childCreated, 'child should not be created'); }); test("Template views return throw if their template cannot be found", function() { view = EmberView.create({ templateName: 'cantBeFound', container: { lookup: function() { }} }); expectAssertion(function() { get(view, 'template'); }, /cantBeFound/); }); test("Layout views return throw if their layout cannot be found", function() { view = EmberView.create({ layoutName: 'cantBeFound', container: { lookup: function() { }} }); expectAssertion(function() { get(view, 'layout'); }, /cantBeFound/); }); test("Template views add an elementId to child views created using the view helper", function() { container.register('template:parent', EmberHandlebars.compile('
{{view "TemplateTests.ChildView"}}
')); container.register('template:child', EmberHandlebars.compile("I can't believe it's not butter.")); TemplateTests.ChildView = EmberView.extend({ container: container, templateName: 'child' }); view = EmberView.create({ container: container, templateName: 'parent' }); appendView(); var childView = get(view, 'childViews.firstObject'); equal(view.$().children().first().children().first().attr('id'), get(childView, 'elementId')); }); test("views set the template of their children to a passed block", function() { container.register('template:parent', EmberHandlebars.compile('

{{#view "TemplateTests.NoTemplateView"}}It worked!{{/view}}

')); TemplateTests.NoTemplateView = EmberView.extend(); view = EmberView.create({ container: container, templateName: 'parent' }); appendView(); ok(view.$('h1:has(span)').length === 1, "renders the passed template inside the parent template"); }); test("views render their template in the context of the parent view's context", function() { container.register('template:parent', EmberHandlebars.compile('

{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}

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

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

')); view = EmberView.create({ container: container, templateName: 'parent', content: { subview: EmberView.extend({ firstName: "Brodele" }), firstName: "Lana", lastName: "del Heeeyyyyyy" } }); appendView(); equal(view.$('h1').text(), "Brodele del Heeeyyyyyy", "renders properties from parent context"); }); test("a view helper's bindings are to the parent context", function() { var Subview = EmberView.extend({ classNameBindings: ['color'], controller: EmberObject.create({ color: 'green', name: "bar" }), template: EmberHandlebars.compile('{{view.someController.name}} {{name}}') }); var View = EmberView.extend({ controller: EmberObject.create({ color: "mauve", name: 'foo' }), Subview: Subview, template: EmberHandlebars.compile('

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

') }); view = View.create(); appendView(); 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"); }); // test("should warn if setting a template on a view with a templateName already specified", function() { // view = EmberView.create({ // childView: EmberView.extend({ // templateName: 'foo' // }), // template: EmberHandlebars.compile('{{#view childView}}test{{/view}}') // }); // expectAssertion(function() { // appendView(); // }, "Unable to find view at path 'childView'"); // run(function() { // view.destroy(); // }); // view = EmberView.create({ // childView: EmberView.extend(), // template: EmberHandlebars.compile('{{#view childView templateName="foo"}}test{{/view}}') // }); // expectAssertion(function() { // appendView(); // }, "Unable to find view at path 'childView'"); // }); test("Child views created using the view helper should have their parent view set properly", function() { TemplateTests = {}; var template = '{{#view "Ember.View"}}{{#view "Ember.View"}}{{view "Ember.View"}}{{/view}}{{/view}}'; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(); var childView = firstGrandchild(view); equal(childView, get(firstChild(childView), 'parentView'), 'parent view is correct'); }); test("Child views created using the view helper should have their IDs registered for events", function() { TemplateTests = {}; var template = '{{view "Ember.View"}}{{view "Ember.View" id="templateViewTest"}}'; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(); var childView = firstChild(view); var id = childView.$()[0].id; equal(EmberView.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.views[id], childView, 'childView with passed ID is registered with View.views so that it can properly receive events from EventDispatcher'); }); test("Child views created using the view helper and that have a viewName should be registered as properties on their parentView", function() { TemplateTests = {}; var template = '{{#view Ember.View}}{{view Ember.View viewName="ohai"}}{{/view}}'; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(); var parentView = firstChild(view), childView = firstGrandchild(view); equal(get(parentView, 'ohai'), childView); }); test("Collection views that specify an example view class have their children be of that class", function() { TemplateTests.ExampleViewCollection = CollectionView.extend({ itemViewClass: EmberView.extend({ isCustom: true }), content: A(['foo']) }); var parentView = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.ExampleViewCollection"}}OHAI{{/collection}}') }); run(function() { parentView.append(); }); ok(firstGrandchild(parentView).isCustom, "uses the example view class"); run(function() { parentView.destroy(); }); }); test("itemViewClass works in the #collection helper", function() { TemplateTests.ExampleController = ArrayProxy.create({ content: A(['alpha']) }); TemplateTests.ExampleItemView = EmberView.extend({ isAlsoCustom: true }); var parentView = EmberView.create({ template: EmberHandlebars.compile('{{#collection contentBinding="TemplateTests.ExampleController" itemViewClass="TemplateTests.ExampleItemView"}}beta{{/collection}}') }); run(function() { parentView.append(); }); ok(firstGrandchild(parentView).isAlsoCustom, "uses the example view class specified in the #collection helper"); run(function() { parentView.destroy(); }); }); test("itemViewClass works in the #collection helper relatively", function() { TemplateTests.ExampleController = ArrayProxy.create({ content: A(['alpha']) }); TemplateTests.ExampleItemView = EmberView.extend({ isAlsoCustom: true }); TemplateTests.CollectionView = CollectionView.extend({ possibleItemView: TemplateTests.ExampleItemView }); var parentView = EmberView.create({ template: EmberHandlebars.compile('{{#collection TemplateTests.CollectionView contentBinding="TemplateTests.ExampleController" itemViewClass="possibleItemView"}}beta{{/collection}}') }); run(function() { parentView.append(); }); ok(firstGrandchild(parentView).isAlsoCustom, "uses the example view class specified in the #collection helper"); run(function() { parentView.destroy(); }); }); test("should update boundIf blocks if the conditional changes", function() { container.register('template:foo', EmberHandlebars.compile('

{{#boundIf "view.content.myApp.isEnabled"}}{{view.content.wham}}{{/boundIf}}

')); view = EmberView.create({ container: container, templateName: 'foo', content: EmberObject.create({ wham: 'bam', thankYou: "ma'am", myApp: EmberObject.create({ isEnabled: true }) }) }); appendView(); equal(view.$('#first').text(), "bam", "renders block when condition is true"); run(function() { set(get(view, 'content'), 'myApp.isEnabled', false); }); equal(view.$('#first').text(), "", "re-renders without block when condition is false"); run(function() { set(get(view, 'content'), 'myApp.isEnabled', true); }); equal(view.$('#first').text(), "bam", "re-renders block when condition changes to true"); }); test("should not update boundIf if truthiness does not change", function() { var renderCount = 0; view = EmberView.create({ template: EmberHandlebars.compile('

{{#boundIf "view.shouldDisplay"}}{{view view.InnerViewClass}}{{/boundIf}}

'), shouldDisplay: true, InnerViewClass: EmberView.extend({ template: EmberHandlebars.compile("bam"), render: function() { renderCount++; return this._super.apply(this, arguments); } }) }); appendView(); equal(renderCount, 1, "precond - should have rendered once"); equal(view.$('#first').text(), "bam", "renders block when condition is true"); run(function() { set(view, 'shouldDisplay', 1); }); equal(renderCount, 1, "should not have rerendered"); equal(view.$('#first').text(), "bam", "renders block when condition is true"); }); test("boundIf should support parent access", function() { view = EmberView.create({ template: EmberHandlebars.compile( '

{{#with view.content}}{{#with thankYou}}'+ '{{#boundIf ../view.show}}parent{{/boundIf}}-{{#boundIf ../../view.show}}grandparent{{/boundIf}}'+ '{{/with}}{{/with}}

' ), content: EmberObject.create({ show: true, thankYou: EmberObject.create() }), show: true }); appendView(); equal(view.$('#first').text(), "parent-grandparent", "renders boundIfs using .."); }); test("{{view}} id attribute should set id on layer", function() { container.register('template:foo', EmberHandlebars.compile('{{#view "TemplateTests.IdView" id="bar"}}baz{{/view}}')); TemplateTests.IdView = EmberView; view = EmberView.create({ container: container, templateName: 'foo' }); appendView(); equal(view.$('#bar').length, 1, "adds id attribute to layer"); equal(view.$('#bar').text(), 'baz', "emits content"); }); test("{{view}} tag attribute should set tagName of the view", function() { container.register('template:foo', EmberHandlebars.compile('{{#view "TemplateTests.TagView" tag="span"}}baz{{/view}}')); TemplateTests.TagView = EmberView; view = EmberView.create({ container: container, templateName: 'foo' }); appendView(); equal(view.$('span').length, 1, "renders with tag name"); equal(view.$('span').text(), 'baz', "emits content"); }); test("{{view}} class attribute should set class on layer", function() { container.register('template:foo', EmberHandlebars.compile('{{#view "TemplateTests.IdView" class="bar"}}baz{{/view}}')); TemplateTests.IdView = EmberView; view = EmberView.create({ container: container, templateName: 'foo' }); appendView(); equal(view.$('.bar').length, 1, "adds class attribute to layer"); equal(view.$('.bar').text(), 'baz', "emits content"); }); test("{{view}} should not allow attributeBindings to be set", function() { expectAssertion(function() { view = EmberView.create({ template: EmberHandlebars.compile('{{view "Ember.View" attributeBindings="one two"}}') }); appendView(); }, /Setting 'attributeBindings' via Handlebars is not allowed/); }); test("{{view}} should be able to point to a local view", function() { view = EmberView.create({ template: EmberHandlebars.compile("{{view view.common}}"), common: EmberView.extend({ template: EmberHandlebars.compile("common") }) }); appendView(); equal(view.$().text(), "common", "tries to look up view name locally"); }); test("{{view}} should evaluate class bindings set to global paths", function() { var App; run(function() { lookup.App = App = Namespace.create({ isApp: true, isGreat: true, directClass: "app-direct", isEnabled: true }); }); view = EmberView.create({ template: EmberHandlebars.compile('{{view Ember.TextField class="unbound" classBinding="App.isGreat:great App.directClass App.isApp App.isEnabled:enabled:disabled"}}') }); appendView(); 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(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"); run(function() { lookup.App.destroy(); }); }); test("{{view}} should evaluate class bindings set in the current context", function() { view = EmberView.create({ isView: true, isEditable: true, directClass: "view-direct", isEnabled: true, template: EmberHandlebars.compile('{{view Ember.TextField class="unbound" classBinding="view.isEditable:editable view.directClass view.isView view.isEnabled:enabled:disabled"}}') }); appendView(); 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(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"); }); test("{{view}} should evaluate class bindings set with either classBinding or classNameBindings", function() { var App; run(function() { lookup.App = App = Namespace.create({ isGreat: true, isEnabled: true }); }); view = EmberView.create({ template: EmberHandlebars.compile('{{view Ember.TextField class="unbound" classBinding="App.isGreat:great App.isEnabled:enabled:disabled" classNameBindings="App.isGreat:really-great App.isEnabled:really-enabled:really-disabled"}}') }); appendView(); 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(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"); run(function() { lookup.App.destroy(); }); }); test("{{view}} should evaluate other attribute bindings set to global paths", function() { run(function() { lookup.App = Namespace.create({ name: "myApp" }); }); view = EmberView.create({ template: EmberHandlebars.compile('{{view Ember.TextField valueBinding="App.name"}}') }); appendView(); equal(view.$('input').attr('value'), "myApp", "evaluates attributes bound to global paths"); run(function() { lookup.App.destroy(); }); }); test("{{view}} should evaluate other attributes bindings set in the current context", function() { view = EmberView.create({ name: "myView", template: EmberHandlebars.compile('{{view Ember.TextField valueBinding="view.name"}}') }); appendView(); equal(view.$('input').attr('value'), "myView", "evaluates attributes bound in the current context"); }); test("{{view}} should be able to bind class names to truthy properties", function() { container.register('template:template', EmberHandlebars.compile('{{#view "TemplateTests.classBindingView" classBinding="view.number:is-truthy"}}foo{{/view}}')); TemplateTests.classBindingView = EmberView.extend(); view = EmberView.create({ container: container, number: 5, templateName: 'template' }); appendView(); equal(view.$('.is-truthy').length, 1, "sets class name"); run(function() { set(view, 'number', 0); }); equal(view.$('.is-truthy').length, 0, "removes class name if bound property is set to falsey"); }); test("{{view}} should be able to bind class names to truthy or falsy properties", function() { container.register('template:template', EmberHandlebars.compile('{{#view "TemplateTests.classBindingView" classBinding="view.number:is-truthy:is-falsy"}}foo{{/view}}')); TemplateTests.classBindingView = EmberView.extend(); view = EmberView.create({ container: container, number: 5, templateName: 'template' }); appendView(); 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(function() { 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"); }); test("should be able to bind element attributes using {{bind-attr}}", function() { var template = EmberHandlebars.compile('view.content.title'); view = EmberView.create({ template: template, content: EmberObject.create({ url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }) }); appendView(); 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(function() { 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(function() { set(view, 'content', EmberObject.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(function() { 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(function() { set(view, 'content', EmberObject.createWithMixins({ url: "http://www.emberjs.com/assets/images/logo.png", title: computed(function() { return "Nanananana Ember!"; }) })); }); equal(view.$('img').attr('alt'), "Nanananana Ember!", "updates alt attribute when title property is computed"); }); test("should be able to bind to view attributes with {{bind-attr}}", function() { view = EmberView.create({ value: 'Test', template: EmberHandlebars.compile('view.value') }); appendView(); equal(view.$('img').attr('alt'), "Test", "renders initial value"); run(function() { view.set('value', 'Updated'); }); equal(view.$('img').attr('alt'), "Updated", "updates value"); }); test("should be able to bind to globals with {{bind-attr}}", function() { TemplateTests.set('value', 'Test'); view = EmberView.create({ template: EmberHandlebars.compile('TemplateTests.value') }); appendView(); equal(view.$('img').attr('alt'), "Test", "renders initial value"); run(function() { TemplateTests.set('value', 'Updated'); }); equal(view.$('img').attr('alt'), "Updated", "updates value"); }); test("should not allow XSS injection via {{bind-attr}}", function() { view = EmberView.create({ template: EmberHandlebars.compile('view.content.value'), content: { value: 'Trololol" onmouseover="alert(\'HAX!\');' } }); appendView(); 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!\');'); }); test("should be able to bind use {{bind-attr}} more than once on an element", function() { var template = EmberHandlebars.compile('view.content.title'); view = EmberView.create({ template: template, content: EmberObject.create({ url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }) }); appendView(); 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(function() { 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(function() { set(view, 'content', EmberObject.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(function() { 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(function() { set(view, 'content', EmberObject.createWithMixins({ url: "http://www.emberjs.com/assets/images/logo.png", title: computed(function() { return "Nanananana Ember!"; }) })); }); equal(view.$('img').attr('alt'), "Nanananana Ember!", "updates alt attribute when title property is computed"); }); test("{{bindAttr}} is aliased to {{bind-attr}}", function() { var originalBindAttr = EmberHandlebars.helpers['bind-attr'], originalWarn = Ember.warn; Ember.warn = function(msg) { equal(msg, "The 'bindAttr' view helper is deprecated in favor of 'bind-attr'", 'Warning called'); }; EmberHandlebars.helpers['bind-attr'] = function() { equal(arguments[0], 'foo', 'First arg match'); equal(arguments[1], 'bar', 'Second arg match'); return 'result'; }; var result = EmberHandlebars.helpers.bindAttr('foo', 'bar'); equal(result, 'result', 'Result match'); EmberHandlebars.helpers['bind-attr'] = originalBindAttr; Ember.warn = originalWarn; }); test("should not reset cursor position when text field receives keyUp event", function() { view = TextField.create({ value: "Broseidon, King of the Brocean" }); run(function() { view.append(); }); view.$().val('Brosiedoon, King of the Brocean'); setCaretPosition(view.$(), 5); run(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"); run(function() { view.destroy(); }); }); test("should be able to bind element attributes using {{bind-attr}} inside a block", function() { var template = EmberHandlebars.compile('{{#with view.content}}title{{/with}}'); view = EmberView.create({ template: template, content: EmberObject.create({ url: "http://www.emberjs.com/assets/images/logo.png", title: "The SproutCore Logo" }) }); appendView(); 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(function() { 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"); }); test("should be able to bind class attribute with {{bind-attr}}", function() { var template = EmberHandlebars.compile(''); view = EmberView.create({ template: template, foo: 'bar' }); appendView(); equal(view.$('img').attr('class'), 'bar', "renders class"); run(function() { set(view, 'foo', 'baz'); }); equal(view.$('img').attr('class'), 'baz', "updates class"); }); test("should be able to bind class attribute via a truthy property with {{bind-attr}}", function() { var template = EmberHandlebars.compile(''); view = EmberView.create({ template: template, isNumber: 5 }); appendView(); equal(view.$('.is-truthy').length, 1, "sets class name"); run(function() { set(view, 'isNumber', 0); }); equal(view.$('.is-truthy').length, 0, "removes class name if bound property is set to something non-truthy"); }); test("should be able to bind class to view attribute with {{bind-attr}}", function() { var template = EmberHandlebars.compile(''); view = EmberView.create({ template: template, foo: 'bar' }); appendView(); equal(view.$('img').attr('class'), 'bar', "renders class"); run(function() { set(view, 'foo', 'baz'); }); equal(view.$('img').attr('class'), 'baz', "updates class"); }); test("should not allow XSS injection via {{bind-attr}} with class", function() { view = EmberView.create({ template: EmberHandlebars.compile(''), foo: '" onmouseover="alert(\'I am in your classes hacking your app\');' }); appendView(); equal(view.$('img').attr('onmouseover'), undefined); // If the whole string is here, then it means we got properly escaped equal(view.$('img').attr('class'), '" onmouseover="alert(\'I am in your classes hacking your app\');'); }); test("should be able to bind class attribute using ternary operator in {{bind-attr}}", function() { var template = EmberHandlebars.compile(''); var content = EmberObject.create({ isDisabled: true }); view = EmberView.create({ template: template, content: content }); appendView(); ok(view.$('img').hasClass('disabled'), 'disabled class is rendered'); ok(!view.$('img').hasClass('enabled'), 'enabled class is not rendered'); run(function() { set(content, 'isDisabled', false); }); ok(!view.$('img').hasClass('disabled'), 'disabled class is not rendered'); ok(view.$('img').hasClass('enabled'), 'enabled class is rendered'); }); test("should be able to add multiple classes using {{bind-attr class}}", function() { var template = EmberHandlebars.compile('
'); var content = EmberObject.create({ isAwesomeSauce: true, isAlsoCool: true, isAmazing: true, isEnabled: true }); view = EmberView.create({ template: template, content: content }); appendView(); 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(function() { set(content, 'isAwesomeSauce', false); set(content, 'isAmazing', false); 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"); }); test("should be able to bind classes to globals with {{bind-attr class}}", function() { TemplateTests.set('isOpen', true); view = EmberView.create({ template: EmberHandlebars.compile('') }); appendView(); ok(view.$('img').hasClass('is-open'), "sets classname to the dasherized value of the global property"); run(function() { TemplateTests.set('isOpen', false); }); ok(!view.$('img').hasClass('is-open'), "removes the classname when the global property has changed"); }); test("should be able to bind-attr to 'this' in an {{#each}} block", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.images}}{{/each}}'), images: A(['one.png', 'two.jpg', 'three.gif']) }); appendView(); 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)); }); test("should be able to bind classes to 'this' in an {{#each}} block with {{bind-attr class}}", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.items}}
  • Item
  • {{/each}}'), items: A(['a', 'b', 'c']) }); appendView(); 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"); }); test("should be able to bind-attr to var in {{#each var in list}} block", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each image in view.images}}{{/each}}'), images: A(['one.png', 'two.jpg', 'three.gif']) }); appendView(); 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(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)); }); test("should be able to output a property without binding", function() { var context = { content: EmberObject.create({ anUnboundString: "No spans here, son." }), anotherUnboundString: "Not here, either." }; view = EmberView.create({ context: context, template: EmberHandlebars.compile( '
    {{unbound content.anUnboundString}}
    '+ '{{#with content}}
    {{unbound ../anotherUnboundString}}
    {{/with}}' ) }); appendView(); equal(view.$('#first').html(), "No spans here, son."); equal(view.$('#second').html(), "Not here, either."); }); test("should allow standard Handlebars template usage", function() { view = EmberView.create({ context: { name: "Erik" }, template: Handlebars.compile("Hello, {{name}}") }); appendView(); equal(view.$().text(), "Hello, Erik"); }); test("should be able to use standard Handlebars #each helper", function() { view = EmberView.create({ context: { items: ['a', 'b', 'c'] }, template: Handlebars.compile("{{#each items}}{{this}}{{/each}}") }); appendView(); equal(view.$().html(), "abc"); }); test("should be able to use unbound helper in #each helper", function() { view = EmberView.create({ items: A(['a', 'b', 'c', 1, 2, 3]), template: EmberHandlebars.compile( "
      {{#each view.items}}
    • {{unbound this}}
    • {{/each}}
    ") }); appendView(); equal(view.$().text(), "abc123"); equal(view.$('li').children().length, 0, "No markers"); }); test("should be able to use unbound helper in #each helper (with objects)", function() { view = EmberView.create({ items: A([{wham: 'bam'}, {wham: 1}]), template: EmberHandlebars.compile( "
      {{#each view.items}}
    • {{unbound wham}}
    • {{/each}}
    ") }); appendView(); equal(view.$().text(), "bam1"); equal(view.$('li').children().length, 0, "No markers"); }); test("should work with precompiled templates", function() { var templateString = EmberHandlebars.precompile("{{view.value}}"), compiledTemplate = EmberHandlebars.template(eval(templateString)); view = EmberView.create({ value: "rendered", template: compiledTemplate }); appendView(); equal(view.$().text(), "rendered", "the precompiled template was rendered"); run(function() { view.set('value', 'updated'); }); equal(view.$().text(), "updated", "the precompiled template was updated"); }); test("should expose a controller keyword when present on the view", function() { var templateString = "{{controller.foo}}{{#view}}{{controller.baz}}{{/view}}"; view = EmberView.create({ container: container, controller: EmberObject.create({ foo: "bar", baz: "bang" }), template: EmberHandlebars.compile(templateString) }); appendView(); equal(view.$().text(), "barbang", "renders values from controller and parent controller"); var controller = get(view, 'controller'); run(function() { controller.set('foo', "BAR"); controller.set('baz', "BLARGH"); }); equal(view.$().text(), "BARBLARGH", "updates the DOM when a bound value is updated"); run(function() { view.destroy(); }); view = EmberView.create({ controller: "aString", template: EmberHandlebars.compile("{{controller}}") }); appendView(); equal(view.$().text(), "aString", "renders the controller itself if no additional path is specified"); }); test("should expose a controller keyword that can be used in conditionals", function() { var templateString = "{{#view}}{{#if controller}}{{controller.foo}}{{/if}}{{/view}}"; view = EmberView.create({ container: container, controller: EmberObject.create({ foo: "bar" }), template: EmberHandlebars.compile(templateString) }); appendView(); equal(view.$().text(), "bar", "renders values from controller and parent controller"); run(function() { view.set('controller', null); }); equal(view.$().text(), "", "updates the DOM when the controller is changed"); }); test("should expose a controller keyword that persists through Ember.ContainerView", function() { var templateString = "{{view Ember.ContainerView}}"; view = EmberView.create({ container: container, controller: EmberObject.create({ foo: "bar" }), template: EmberHandlebars.compile(templateString) }); appendView(); var containerView = get(view, 'childViews.firstObject'); var viewInstanceToBeInserted = EmberView.create({ template: EmberHandlebars.compile('{{controller.foo}}') }); run(function() { containerView.pushObject(viewInstanceToBeInserted); }); equal(trim(viewInstanceToBeInserted.$().text()), "bar", "renders value from parent's controller"); }); test("should expose a view keyword", function() { var templateString = '{{#with view.differentContent}}{{view.foo}}{{#view baz="bang"}}{{view.baz}}{{/view}}{{/with}}'; view = EmberView.create({ container: container, differentContent: { view: { foo: "WRONG", baz: "WRONG" } }, foo: "bar", template: EmberHandlebars.compile(templateString) }); appendView(); equal(view.$().text(), "barbang", "renders values from view and child view"); }); test("should be able to explicitly set a view's context", function() { var context = EmberObject.create({ test: 'test' }); TemplateTests.CustomContextView = EmberView.extend({ context: context, template: EmberHandlebars.compile("{{test}}") }); view = EmberView.create({ template: EmberHandlebars.compile("{{view TemplateTests.CustomContextView}}") }); appendView(); equal(view.$().text(), "test"); }); test("should escape HTML in primitive value contexts when using normal mustaches", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.kiddos}}{{this}}{{/each}}'), kiddos: A(['Max', 'James']) }); appendView(); equal(view.$('b').length, 0, "does not create an element"); equal(view.$().text(), 'MaxJames', "inserts entities, not elements"); run(function() { set(view, 'kiddos', A(['Max','James'])); }); equal(view.$().text(), 'MaxJames', "updates with entities, not elements"); equal(view.$('i').length, 0, "does not create an element when value is updated"); }); test("should not escape HTML in primitive value contexts when using triple mustaches", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.kiddos}}{{{this}}}{{/each}}'), kiddos: A(['Max', 'James']) }); appendView(); equal(view.$('b').length, 2, "creates an element"); run(function() { set(view, 'kiddos', A(['Max','James'])); }); equal(view.$('i').length, 2, "creates an element when value is updated"); }); QUnit.module("Ember.View - handlebars integration", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; originalLog = Ember.Logger.log; logCalls = []; Ember.Logger.log = function(arg) { logCalls.push(arg); }; }, teardown: function() { if (view) { run(function() { view.destroy(); }); view = null; } Ember.Logger.log = originalLog; Ember.lookup = originalLookup; } }); test("should be able to log a property", function() { var context = { value: 'one', valueTwo: 'two', content: EmberObject.create({}) }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{log value}}{{#with content}}{{log ../valueTwo}}{{/with}}') }); appendView(); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one', "should call log with value"); equal(logCalls[1], 'two', "should call log with valueTwo"); }); test("should be able to log a view property", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{log view.value}}'), value: 'one' }); appendView(); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one', "should call log with value"); }); test("should be able to log `this`", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.items}}{{log this}}{{/each}}'), items: A(['one', 'two']) }); appendView(); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one', "should call log with item one"); equal(logCalls[1], 'two', "should call log with item two"); }); var MyApp; QUnit.module("Templates redrawing and bindings", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; MyApp = lookup.MyApp = EmberObject.create({}); }, teardown: function() { run(function() { if (view) view.destroy(); }); Ember.lookup = originalLookup; } }); test("should be able to update when bound property updates", function() { MyApp.set('controller', EmberObject.create({name: 'first'})); var View = EmberView.extend({ template: EmberHandlebars.compile('{{view.value.name}}, {{view.computed}}'), valueBinding: 'MyApp.controller', computed: computed(function() { return this.get('value.name') + ' - computed'; }).property('value') }); run(function() { view = View.create(); }); appendView(); run(function() { MyApp.set('controller', EmberObject.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"); }); test("properties within an if statement should not fail on re-render", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#if view.value}}{{view.value}}{{/if}}'), value: null }); appendView(); equal(view.$().text(), ''); run(function() { view.set('value', 'test'); }); equal(view.$().text(), 'test'); run(function() { view.set('value', null); }); equal(view.$().text(), ''); }); test('should cleanup bound properties on rerender', function() { view = EmberView.create({ controller: EmberObject.create({name: 'wycats'}), template: EmberHandlebars.compile('{{name}}') }); appendView(); equal(view.$().text(), 'wycats', 'rendered binding'); run(view, 'rerender'); equal(view._childViews.length, 1); }); test("views within an if statement should be sane on re-render", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#if view.display}}{{view Ember.TextField}}{{/if}}'), display: false }); appendView(); equal(view.$('input').length, 0); run(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.views[textfield.attr('id')]); }); test("the {{this}} helper should not fail on removal", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#if view.show}}{{#each view.list}}{{this}}{{/each}}{{/if}}'), show: true, list: A(['a', 'b', 'c']) }); appendView(); equal(view.$().text(), 'abc', "should start property - precond"); run(function() { view.set('show', false); }); equal(view.$().text(), ''); }); test("bindings should be relative to the current context", function() { view = EmberView.create({ museumOpen: true, museumDetails: EmberObject.create({ name: "SFMoMA", price: 20 }), museumView: EmberView.extend({ template: EmberHandlebars.compile('Name: {{view.name}} Price: ${{view.dollars}}') }), template: EmberHandlebars.compile('{{#if view.museumOpen}} {{view view.museumView nameBinding="view.museumDetails.name" dollarsBinding="view.museumDetails.price"}} {{/if}}') }); appendView(); equal(trim(view.$().text()), "Name: SFMoMA Price: $20", "should print baz twice"); }); test("bindings should respect keywords", function() { view = EmberView.create({ museumOpen: true, controller: { museumOpen: true, museumDetails: EmberObject.create({ name: "SFMoMA", price: 20 }) }, museumView: EmberView.extend({ template: EmberHandlebars.compile('Name: {{view.name}} Price: ${{view.dollars}}') }), template: EmberHandlebars.compile('{{#if view.museumOpen}}{{view view.museumView nameBinding="controller.museumDetails.name" dollarsBinding="controller.museumDetails.price"}}{{/if}}') }); appendView(); equal(trim(view.$().text()), "Name: SFMoMA Price: $20", "should print baz twice"); }); test("bindings can be 'this', in which case they *are* the current context", function() { view = EmberView.create({ museumOpen: true, museumDetails: EmberObject.create({ name: "SFMoMA", price: 20, museumView: EmberView.extend({ template: EmberHandlebars.compile('Name: {{view.museum.name}} Price: ${{view.museum.price}}') }) }), template: EmberHandlebars.compile('{{#if view.museumOpen}} {{#with view.museumDetails}}{{view museumView museumBinding="this"}} {{/with}}{{/if}}') }); appendView(); equal(trim(view.$().text()), "Name: SFMoMA Price: $20", "should print baz twice"); }); // https://github.com/emberjs/ember.js/issues/120 test("should not enter an infinite loop when binding an attribute in Handlebars", function() { var App; run(function() { lookup.App = App = Namespace.create(); }); App.test = EmberObject.create({ href: 'test' }); App.Link = EmberView.extend({ classNames: ['app-link'], tagName: 'a', attributeBindings: ['href'], href: '#none', click: function() { return false; } }); var parentView = EmberView.create({ template: EmberHandlebars.compile('{{#view App.Link hrefBinding="App.test.href"}} Test {{/view}}') }); run(function() { parentView.appendTo('#qunit-fixture'); }); // 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+"'"); run(function() { parentView.destroy(); }); run(function() { lookup.App.destroy(); }); }); test("should update bound values after the view is removed and then re-appended", function() { view = EmberView.create({ template: EmberHandlebars.compile("{{#if view.showStuff}}{{view.boundValue}}{{else}}Not true.{{/if}}"), showStuff: true, boundValue: "foo" }); appendView(); equal(trim(view.$().text()), "foo"); run(function() { set(view, 'showStuff', false); }); equal(trim(view.$().text()), "Not true."); run(function() { set(view, 'showStuff', true); }); equal(trim(view.$().text()), "foo"); run(function() { view.remove(); set(view, 'showStuff', false); }); run(function() { set(view, 'showStuff', true); }); appendView(); run(function() { set(view, 'boundValue', "bar"); }); equal(trim(view.$().text()), "bar"); }); test("should update bound values after view's parent is removed and then re-appended", function() { var controller = EmberObject.create(); var parentView = ContainerView.create({ childViews: ['testView'], controller: controller, testView: EmberView.create({ template: EmberHandlebars.compile("{{#if showStuff}}{{boundValue}}{{else}}Not true.{{/if}}") }) }); controller.setProperties({ showStuff: true, boundValue: "foo" }); run(function() { parentView.appendTo('#qunit-fixture'); }); view = parentView.get('testView'); equal(trim(view.$().text()), "foo"); run(function() { set(controller, 'showStuff', false); }); equal(trim(view.$().text()), "Not true."); run(function() { set(controller, 'showStuff', true); }); equal(trim(view.$().text()), "foo"); run(function() { parentView.remove(); set(controller, 'showStuff', false); }); run(function() { set(controller, 'showStuff', true); }); run(function() { parentView.appendTo('#qunit-fixture'); }); run(function() { set(controller, 'boundValue', "bar"); }); equal(trim(view.$().text()), "bar"); run(function() { parentView.destroy(); }); }); test("should call a registered helper for mustache without parameters", function() { EmberHandlebars.registerHelper('foobar', function() { return 'foobar'; }); view = EmberView.create({ template: EmberHandlebars.compile("{{foobar}}") }); appendView(); ok(view.$().text() === 'foobar', "Regular helper was invoked correctly"); }); test("should bind to the property if no registered helper found for a mustache without parameters", function() { view = EmberView.createWithMixins({ template: EmberHandlebars.compile("{{view.foobarProperty}}"), foobarProperty: computed(function() { return 'foobarProperty'; }) }); appendView(); ok(view.$().text() === 'foobarProperty', "Property was bound to correctly"); }); test("should accept bindings as a string or an Ember.Binding", function() { var viewClass = EmberView.extend({ template: EmberHandlebars.compile("binding: {{view.bindingTest}}, string: {{view.stringTest}}") }); EmberHandlebars.registerHelper('boogie', function(id, options) { options.hash = options.hash || {}; options.hash.bindingTestBinding = Binding.oneWay('context.' + id); options.hash.stringTestBinding = id; return EmberHandlebars.ViewHelper.helper(this, viewClass, options); }); view = EmberView.create({ context: EmberObject.create({ direction: 'down' }), template: EmberHandlebars.compile("{{boogie direction}}") }); appendView(); equal(trim(view.$().text()), "binding: down, string: down"); }); test("should teardown observers from bound properties on rerender", function() { view = EmberView.create({ template: EmberHandlebars.compile("{{view.foo}}"), foo: 'bar' }); appendView(); equal(observersFor(view, 'foo').length, 1); run(function() { view.rerender(); }); equal(observersFor(view, 'foo').length, 1); }); test("should teardown observers from bind-attr on rerender", function() { view = EmberView.create({ template: EmberHandlebars.compile('wat'), foo: 'bar' }); appendView(); equal(observersFor(view, 'foo').length, 2); run(function() { view.rerender(); }); equal(observersFor(view, 'foo').length, 2); }); }); define("ember-handlebars/tests/handlebars_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests'); test('ember-handlebars/tests/handlebars_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/handlebars_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/bound_helper_test", ["ember-views/views/view","ember-metal/run_loop","ember-runtime/system/object","ember-runtime/system/namespace","ember-runtime/system/native_array","ember-metal/property_get","ember-metal/property_set","ember-handlebars-compiler"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; /*globals TemplateTests*/ /*jshint newcap:false*/ var EmberView = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var Namespace = __dependency4__["default"]; var A = __dependency5__.A; // import {expectAssertion} from "ember-metal/tests/debug_helpers"; var get = __dependency6__.get; var set = __dependency7__.set; var EmberHandlebars = __dependency8__["default"]; var compile = EmberHandlebars.compile; var view; function appendView() { run(function() { view.appendTo('#qunit-fixture'); }); } function registerRepeatHelper() { EmberHandlebars.helper('repeat', function(value, options) { var count = options.hash.count; var a = []; while(a.length < count) { a.push(value); } return a.join(''); }); } QUnit.module("Handlebars bound helpers", { setup: function() { window.TemplateTests = Namespace.create(); }, teardown: function() { run(function() { if (view) { view.destroy(); } }); window.TemplateTests = undefined; } }); test("should update bound helpers when properties change", function() { EmberHandlebars.helper('capitalize', function(value) { return value.toUpperCase(); }); view = EmberView.create({ controller: EmberObject.create({name: "Brogrammer"}), template: EmberHandlebars.compile("{{capitalize name}}") }); appendView(); equal(view.$().text(), 'BROGRAMMER', "helper output is correct"); run(function() { set(view.controller, 'name', 'wes'); }); equal(view.$().text(), 'WES', "helper output updated"); }); test("should allow for computed properties with dependencies", function() { EmberHandlebars.helper('capitalizeName', function(value) { return get(value, 'name').toUpperCase(); }, 'name'); view = EmberView.create({ controller: EmberObject.create({ person: EmberObject.create({ name: 'Brogrammer' }) }), template: EmberHandlebars.compile("{{capitalizeName person}}") }); appendView(); equal(view.$().text(), 'BROGRAMMER', "helper output is correct"); run(function() { set(view.controller.person, 'name', 'wes'); }); equal(view.$().text(), 'WES', "helper output updated"); }); test("bound helpers should support options", function() { registerRepeatHelper(); view = EmberView.create({ controller: EmberObject.create({text: 'ab'}), template: EmberHandlebars.compile("{{repeat text count=3}}") }); appendView(); ok(view.$().text() === 'ababab', "helper output is correct"); }); test("bound helpers should support keywords", function() { EmberHandlebars.helper('capitalize', function(value) { return value.toUpperCase(); }); view = EmberView.create({ text: 'ab', template: EmberHandlebars.compile("{{capitalize view.text}}") }); appendView(); ok(view.$().text() === 'AB', "helper output is correct"); }); test("bound helpers should support global paths", function() { EmberHandlebars.helper('capitalize', function(value) { return value.toUpperCase(); }); TemplateTests.text = 'ab'; view = EmberView.create({ template: EmberHandlebars.compile("{{capitalize TemplateTests.text}}") }); appendView(); ok(view.$().text() === 'AB', "helper output is correct"); }); test("bound helper should support this keyword", function() { EmberHandlebars.helper('capitalize', function(value) { return get(value, 'text').toUpperCase(); }); view = EmberView.create({ controller: EmberObject.create({text: 'ab'}), template: EmberHandlebars.compile("{{capitalize this}}") }); appendView(); ok(view.$().text() === 'AB', "helper output is correct"); }); test("bound helpers should support bound options", function() { registerRepeatHelper(); view = EmberView.create({ controller: EmberObject.create({text: 'ab', numRepeats: 3}), template: EmberHandlebars.compile('{{repeat text countBinding="numRepeats"}}') }); appendView(); equal(view.$().text(), 'ababab', "helper output is correct"); run(function() { view.set('controller.numRepeats', 4); }); equal(view.$().text(), 'abababab', "helper correctly re-rendered after bound option was changed"); run(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"); }); test("bound helpers should support unquoted values as bound options", function() { registerRepeatHelper(); view = EmberView.create({ controller: EmberObject.create({text: 'ab', numRepeats: 3}), template: EmberHandlebars.compile('{{repeat text count=numRepeats}}') }); appendView(); equal(view.$().text(), 'ababab', "helper output is correct"); run(function() { view.set('controller.numRepeats', 4); }); equal(view.$().text(), 'abababab', "helper correctly re-rendered after bound option was changed"); run(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"); }); test("bound helpers should support multiple bound properties", function() { EmberHandlebars.helper('concat', function() { return [].slice.call(arguments, 0, -1).join(''); }); view = EmberView.create({ controller: EmberObject.create({thing1: 'ZOID', thing2: 'BERG'}), template: EmberHandlebars.compile('{{concat thing1 thing2}}') }); appendView(); equal(view.$().text(), 'ZOIDBERG', "helper output is correct"); run(function() { view.set('controller.thing2', "NERD"); }); equal(view.$().text(), 'ZOIDNERD', "helper correctly re-rendered after second bound helper property changed"); run(function() { view.controller.setProperties({ thing1: "WOOT", thing2: "YEAH" }); }); equal(view.$().text(), 'WOOTYEAH', "helper correctly re-rendered after both bound helper properties changed"); }); test("bound helpers should expose property names in options.data.properties", function() { EmberHandlebars.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.create({ controller: EmberObject.create({ thing1: 'ZOID', thing2: 'BERG', thing3: EmberObject.create({ foo: 123 }) }), template: EmberHandlebars.compile('{{echo thing1 thing2 thing3.foo}}') }); appendView(); equal(view.$().text(), 'thing1 thing2 thing3.foo', "helper output is correct"); }); test("bound helpers can be invoked with zero args", function() { EmberHandlebars.helper('troll', function(options) { return options.hash.text || "TROLOLOL"; }); view = EmberView.create({ controller: EmberObject.create({trollText: "yumad"}), template: EmberHandlebars.compile('{{troll}} and {{troll text="bork"}}') }); appendView(); equal(view.$().text(), 'TROLOLOL and bork', "helper output is correct"); }); test("bound helpers should not be invoked with blocks", function() { registerRepeatHelper(); view = EmberView.create({ controller: EmberObject.create({}), template: EmberHandlebars.compile("{{#repeat}}Sorry, Charlie{{/repeat}}") }); expectAssertion(function() { appendView(); }, /registerBoundHelper-generated helpers do not support use with Handlebars blocks/i); }); test("should observe dependent keys passed to registerBoundHelper", function() { try { expect(2); var SimplyObject = EmberObject.create({ firstName: 'Jim', lastName: 'Owen' }); EmberHandlebars.registerBoundHelper('fullName', function(value){ return value.get('firstName') + ' ' + value.get('lastName'); }, 'firstName', 'lastName'); view = EmberView.create({ template: EmberHandlebars.compile('{{fullName this}}'), context: SimplyObject }); appendView(view); equal(view.$().text(), 'Jim Owen', 'simply render the helper'); run(SimplyObject, SimplyObject.set, 'firstName', 'Tom'); equal(view.$().text(), 'Tom Owen', 'simply render the helper'); } finally { delete EmberHandlebars.helpers['fullName']; } }); test("shouldn't treat raw numbers as bound paths", function() { EmberHandlebars.helper('sum', function(a, b) { return a + b; }); view = EmberView.create({ controller: EmberObject.create({aNumber: 1}), template: EmberHandlebars.compile("{{sum aNumber 1}} {{sum 0 aNumber}} {{sum 5 6}}") }); appendView(); equal(view.$().text(), '2 1 11', "helper output is correct"); run(view.controller, 'set', 'aNumber', 5); equal(view.$().text(), '6 5 11', "helper still updates as expected"); }); test("shouldn't treat quoted strings as bound paths", function() { var helperCount = 0; EmberHandlebars.helper('concat', function(a, b, opt) { helperCount++; return a + b; }); view = EmberView.create({ controller: EmberObject.create({word: "jerkwater", loo: "unused"}), template: EmberHandlebars.compile("{{concat word 'loo'}} {{concat '' word}} {{concat 'will' \"didi\"}}") }); appendView(); equal(view.$().text(), 'jerkwaterloo jerkwater willdidi', "helper output is correct"); run(view.controller, 'set', 'word', 'bird'); equal(view.$().text(), 'birdloo bird willdidi', "helper still updates as expected"); run(view.controller, 'set', '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"); }); test("bound helpers can handle nulls in array (with primitives)", function() { EmberHandlebars.helper('reverse', function(val) { return val ? val.split('').reverse().join('') : "NOPE"; }); view = EmberView.create({ controller: EmberObject.create({ things: A([ null, 0, undefined, false, "OMG" ]) }), template: EmberHandlebars.compile("{{#each things}}{{this}}|{{reverse this}} {{/each}}{{#each thing in things}}{{thing}}|{{reverse thing}} {{/each}}") }); appendView(); equal(view.$().text(), '|NOPE 0|NOPE |NOPE false|NOPE OMG|GMO |NOPE 0|NOPE |NOPE false|NOPE OMG|GMO ', "helper output is correct"); run(function() { view.controller.things.pushObject('blorg'); view.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"); }); test("bound helpers can handle nulls in array (with objects)", function() { EmberHandlebars.helper('print-foo', function(val) { return val ? get(val, 'foo') : "NOPE"; }); view = EmberView.create({ controller: EmberObject.create({ things: A([ null, { foo: 5 } ]) }), template: EmberHandlebars.compile("{{#each things}}{{foo}}|{{print-foo this}} {{/each}}{{#each thing in things}}{{thing.foo}}|{{print-foo thing}} {{/each}}") }); appendView(); equal(view.$().text(), '|NOPE 5|5 |NOPE 5|5 ', "helper output is correct"); run(view.controller.things, 'pushObject', { foo: 6 }); equal(view.$().text(), '|NOPE 5|5 6|6 |NOPE 5|5 6|6 ', "helper output is correct"); }); test("bound helpers can handle `this` keyword when it's a non-object", function() { EmberHandlebars.helper("shout", function(value) { return value + '!'; }); view = EmberView.create({ controller: EmberObject.create({ things: A(['alex']) }), template: EmberHandlebars.compile("{{#each things}}{{shout this}}{{/each}}") }); appendView(); equal(view.$().text(), 'alex!', "helper output is correct"); run(view.controller.things, 'shiftObject'); equal(view.$().text(), '', "helper output is correct"); run(view.controller.things, 'pushObject', 'wallace'); equal(view.$().text(), 'wallace!', "helper output is correct"); }); }); define("ember-handlebars/tests/helpers/bound_helper_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/bound_helper_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/bound_helper_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/custom_view_helper_test", ["ember-views/views/view","ember-metal/run_loop","ember-runtime/system/object","ember-runtime/system/namespace","ember-handlebars-compiler","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; /*globals TemplateTests*/ var EmberView = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var Namespace = __dependency4__["default"]; var EmberHandlebars = __dependency5__["default"]; var get = __dependency6__.get; var set = __dependency7__.set; function appendView() { run(function() { view.appendTo('#qunit-fixture'); }); } var view; QUnit.module("Handlebars custom view helpers", { setup: function() { window.TemplateTests = Namespace.create(); }, teardown: function() { run(function() { if (view) { view.destroy(); } }); window.TemplateTests = undefined; } }); test("should render an instance of the specified view", function() { TemplateTests.OceanView = EmberView.extend({ template: EmberHandlebars.compile('zomg, nice view') }); EmberHandlebars.helper('oceanView', TemplateTests.OceanView); view = EmberView.create({ controller: EmberObject.create(), template: EmberHandlebars.compile('{{oceanView tagName="strong"}}') }); appendView(); var oceanViews = view.$().find("strong:contains('zomg, nice view')"); equal(oceanViews.length, 1, "helper rendered an instance of the view"); }); test("Should bind to this keyword", function() { TemplateTests.OceanView = EmberView.extend({ model: null, template: EmberHandlebars.compile('{{view.model}}') }); EmberHandlebars.helper('oceanView', TemplateTests.OceanView); view = EmberView.create({ context: 'foo', controller: EmberObject.create(), template: EmberHandlebars.compile('{{oceanView tagName="strong" viewName="ocean" model=this}}') }); appendView(); var oceanViews = view.$().find("strong:contains('foo')"); equal(oceanViews.length, 1, "helper rendered an instance of the view"); run(function() { set(view, 'ocean.model', 'bar'); }); oceanViews = view.$().find("strong:contains('bar')"); equal(oceanViews.length, 1, "helper rendered an instance of the view"); }); }); define("ember-handlebars/tests/helpers/custom_view_helper_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/custom_view_helper_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/custom_view_helper_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/debug_test", ["ember-metal/core","ember-metal/logger","ember-metal/run_loop","ember-views/views/view","ember-handlebars-compiler","ember-handlebars/helpers/debug"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.lookup var EmberLogger = __dependency2__["default"]; var run = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var EmberHandlebars = __dependency5__["default"]; var logHelper = __dependency6__.logHelper; var originalLookup = Ember.lookup, lookup; var originalLog, logCalls; var originalLogHelper; var view; function appendView() { run(function() { view.appendTo('#qunit-fixture'); }); } QUnit.module("Handlebars {{log}} helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; originalLogHelper = EmberHandlebars.helpers.log; EmberHandlebars.registerHelper("log", logHelper); originalLog = EmberLogger.log; logCalls = []; EmberLogger.log = function() { logCalls.push.apply(logCalls, arguments); }; }, teardown: function() { if (view) { run(function() { view.destroy(); }); view = null; } EmberLogger.log = originalLog; EmberHandlebars.helpers.log = originalLogHelper; Ember.lookup = originalLookup; } }); test("should be able to log multiple properties", function() { var context = { value: 'one', valueTwo: 'two' }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{log value valueTwo}}') }); appendView(); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one'); equal(logCalls[1], 'two'); }); test("should be able to log primitives", function() { var context = { value: 'one', valueTwo: 'two' }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{log value "foo" 0 valueTwo true}}') }); appendView(); 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); }); }); define("ember-handlebars/tests/helpers/debug_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/debug_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/debug_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/each_test", ["ember-metal/core","ember-runtime/system/object","ember-metal/run_loop","ember-views/views/view","ember-handlebars/views/metamorph_view","ember-metal/computed","ember-runtime/controllers/array_controller","ember-handlebars-compiler","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"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__) { "use strict"; /*jshint newcap:false*/ var Ember = __dependency1__["default"]; // Ember.lookup; var EmberObject = __dependency2__["default"]; var run = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var _MetamorphView = __dependency5__["default"]; var computed = __dependency6__.computed; var ArrayController = __dependency7__["default"]; var EmberHandlebars = __dependency8__["default"]; // import {expectAssertion} from "ember-metal/tests/debug_helpers"; var A = __dependency9__.A; var EmberController = __dependency10__["default"]; var ObjectController = __dependency11__["default"]; var Container = __dependency12__["default"]; var get = __dependency13__.get; var set = __dependency14__.set; var people, view, container; var template, templateMyView; function templateFor(template) { return EmberHandlebars.compile(template); } var originalLookup = Ember.lookup, lookup; QUnit.module("the #each helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; template = templateFor("{{#each view.people}}{{name}}{{/each}}"); people = A([{ name: "Steve Holt" }, { name: "Annabelle" }]); container = new Container(); container.register('view:default', _MetamorphView); container.register('view:toplevel', EmberView.extend()); view = EmberView.create({ container: container, template: template, people: people }); templateMyView = templateFor("{{name}}"); lookup.MyView = EmberView.extend({ template: templateMyView }); append(view); }, teardown: function() { run(function() { if (container) { container.destroy(); } if (view) { view.destroy(); } container = view = null; }); Ember.lookup = originalLookup; } }); var append = function(view) { run(function() { view.appendTo('#qunit-fixture'); }); }; 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); }; test("it renders the template for each item in an array", function() { assertHTML(view, "Steve HoltAnnabelle"); }); test("it updates the view if an item is added", function() { run(function() { people.pushObject({ name: "Tom Dale" }); }); assertHTML(view, "Steve HoltAnnabelleTom Dale"); }); test("it allows you to access the current context using {{this}}", function() { run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor("{{#each view.people}}{{this}}{{/each}}"), people: A(['Black Francis', 'Joey Santiago', 'Kim Deal', 'David Lovering']) }); append(view); assertHTML(view, "Black FrancisJoey SantiagoKim DealDavid Lovering"); }); test("it updates the view if an item is removed", function() { run(function() { people.removeAt(0); }); assertHTML(view, "Annabelle"); }); test("it updates the view if an item is replaced", function() { run(function() { people.removeAt(0); people.insertAt(0, { name: "Kazuki" }); }); assertHTML(view, "KazukiAnnabelle"); }); test("can add and replace in the same runloop", function() { run(function() { people.pushObject({ name: "Tom Dale" }); people.removeAt(0); people.insertAt(0, { name: "Kazuki" }); }); assertHTML(view, "KazukiAnnabelleTom Dale"); }); test("can add and replace the object before the add in the same runloop", function() { run(function() { people.pushObject({ name: "Tom Dale" }); people.removeAt(1); people.insertAt(1, { name: "Kazuki" }); }); assertHTML(view, "Steve HoltKazukiTom Dale"); }); test("can add and replace complicatedly", function() { run(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"); }); test("can add and replace complicatedly harder", function() { run(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"); }); test("it works inside a ul element", function() { var ulView = EmberView.create({ template: templateFor('
      {{#each view.people}}
    • {{name}}
    • {{/each}}
    '), people: people }); append(ulView); equal(ulView.$('li').length, 2, "renders two
  • elements"); run(function() { people.pushObject({name: "Black Francis"}); }); equal(ulView.$('li').length, 3, "renders an additional
  • element when an object is added"); run(function() { ulView.destroy(); }); }); test("it works inside a table element", function() { var tableView = EmberView.create({ template: templateFor('{{#each view.people}}{{/each}}
    {{name}}
    '), people: people }); append(tableView); equal(tableView.$('td').length, 2, "renders two elements"); run(function() { people.pushObject({name: "Black Francis"}); }); equal(tableView.$('td').length, 3, "renders an additional element when an object is added"); run(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"); run(function() { tableView.destroy(); }); }); test("it supports itemController", function() { var Controller = EmberController.extend({ controllerName: computed(function() { return "controller:"+this.get('model.name'); }) }); run(function() { view.destroy(); }); // destroy existing view var parentController = { container: container }; container.register('controller:array', ArrayController.extend()); view = EmberView.create({ container: container, template: templateFor('{{#each view.people itemController="person"}}{{controllerName}}{{/each}}'), people: people, controller: parentController }); container.register('controller:person', Controller); append(view); equal(view.$().text(), "controller:Steve Holtcontroller:Annabelle"); run(function() { view.rerender(); }); assertText(view, "controller:Steve Holtcontroller:Annabelle"); run(function() { people.pushObject({ name: "Yehuda Katz" }); }); assertText(view, "controller:Steve Holtcontroller:Annabellecontroller:Yehuda Katz"); run(function() { set(view, 'people', A([{ name: "Trek Glowacki" }, { name: "Geoffrey Grosenbach" }])); }); assertText(view, "controller:Trek Glowackicontroller:Geoffrey Grosenbach"); var controller = view.get('_childViews')[0].get('controller'); strictEqual(view.get('_childViews')[0].get('_arrayController.target'), parentController, "the target property of the child controllers are set correctly"); }); 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.extend({ controllerName: computed(function() { return "controller:" + get(this, 'model.name') + ' of ' + get(this, 'parentController.company'); }) }), parentController = { container: container, company: 'Yapp' }; container.register('controller:array', ArrayController.extend()); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ container: container, template: templateFor('{{#each view.people itemController="person"}}{{controllerName}}{{/each}}'), people: people, controller: parentController }); container.register('controller:person', Controller); append(view); equal(view.$().text(), "controller:Steve Holt of Yappcontroller:Annabelle of Yapp"); }); test("itemController specified in ArrayController gets a parentController property", function() { var PersonController = ObjectController.extend({ controllerName: computed(function() { return "controller:" + get(this, 'model.name') + ' of ' + get(this, 'parentController.company'); }) }), PeopleController = ArrayController.extend({ model: people, itemController: 'person', company: 'Yapp' }); container.register('controller:people', PeopleController); container.register('controller:person', PersonController); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ container: container, template: templateFor('{{#each}}{{controllerName}}{{/each}}'), controller: container.lookup('controller:people') }); append(view); equal(view.$().text(), "controller:Steve Holt of Yappcontroller:Annabelle of Yapp"); }); test("itemController's parentController property, when the ArrayController has a parentController", function() { var PersonController = ObjectController.extend({ controllerName: computed(function() { return "controller:" + get(this, 'model.name') + ' of ' + get(this, 'parentController.company'); }) }), PeopleController = ArrayController.extend({ model: people, itemController: 'person', parentController: computed(function(){ return this.container.lookup('controller:company'); }), company: 'Yapp' }), CompanyController = EmberController.extend(); container.register('controller:company', CompanyController); container.register('controller:people', PeopleController); container.register('controller:person', PersonController); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ container: container, template: templateFor('{{#each}}{{controllerName}}{{/each}}'), controller: container.lookup('controller:people') }); append(view); equal(view.$().text(), "controller:Steve Holt of Yappcontroller:Annabelle of Yapp"); }); test("it supports itemController when using a custom keyword", function() { var Controller = EmberController.extend({ controllerName: computed(function() { return "controller:"+this.get('model.name'); }) }); container.register('controller:array', ArrayController.extend()); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ container: container, template: templateFor('{{#each person in view.people itemController="person"}}{{person.controllerName}}{{/each}}'), people: people, controller: { container: container } }); container.register('controller:person', Controller); append(view); equal(view.$().text(), "controller:Steve Holtcontroller:Annabelle"); run(function() { view.rerender(); }); equal(view.$().text(), "controller:Steve Holtcontroller:Annabelle"); }); test("it supports {{itemView=}}", function() { var itemView = EmberView.extend({ template: templateFor('itemView:{{name}}') }); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor('{{each view.people itemView="anItemView"}}'), people: people, controller: { container: container } }); container.register('view:anItemView', itemView); append(view); assertText(view, "itemView:Steve HoltitemView:Annabelle"); }); test("it defers all normalization of itemView names to the resolver", function() { var itemView = EmberView.extend({ template: templateFor('itemView:{{name}}') }); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor('{{each view.people itemView="an-item-view"}}'), people: people, controller: { container: container } }); container.register('view:an-item-view', itemView); container.resolve = function(fullname) { equal(fullname, "view:an-item-view", "leaves fullname untouched"); return Container.prototype.resolve.call(this, fullname); }; append(view); }); test("it supports {{itemViewClass=}}", function() { run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor('{{each view.people itemViewClass="MyView"}}'), people: people }); append(view); assertText(view, "Steve HoltAnnabelle"); }); test("it supports {{itemViewClass=}} with tagName (DEPRECATED)", function() { run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor('{{each view.people itemViewClass="MyView" tagName="ul"}}'), people: people }); expectDeprecation(/Supplying a tagName to Metamorph views is unreliable and is deprecated./); append(view); var html = view.$().html(); // IE 8 (and prior?) adds the \r\n html = html.replace(/]*><\/script>/ig, '').replace(/[\r\n]/g, ''); html = html.replace(/]*><\/div>/ig, '').replace(/[\r\n]/g, ''); html = html.replace(/]*/ig, '
  • steve holt
  • annabelle
  • "); }); test("it supports {{itemViewClass=}} with in format", function() { lookup.MyView = EmberView.extend({ template: templateFor("{{person.name}}") }); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor('{{each person in view.people itemViewClass="MyView"}}'), people: people }); append(view); assertText(view, "Steve HoltAnnabelle"); }); test("it supports {{else}}", function() { run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ template: templateFor("{{#each view.items}}{{this}}{{else}}Nothing{{/each}}"), items: A(['one', 'two']) }); append(view); assertHTML(view, "onetwo"); run(function() { view.set('items', A()); }); assertHTML(view, "Nothing"); }); test("it works with the controller keyword", function() { var controller = ArrayController.create({ model: A(["foo", "bar", "baz"]) }); run(function() { view.destroy(); }); // destroy existing view view = EmberView.create({ container: container, controller: controller, template: templateFor("{{#view}}{{#each controller}}{{this}}{{/each}}{{/view}}") }); append(view); equal(view.$().text(), "foobarbaz"); }); QUnit.module("{{#each foo in bar}}", { setup: function() { container = new Container(); container.register('view:default', _MetamorphView); container.register('view:toplevel', EmberView.extend()); }, teardown: function() { run(function() { if (container) { container.destroy(); } if (view) { view.destroy(); } container = view = null; }); } }); test("#each accepts a name binding", function() { view = EmberView.create({ template: templateFor("{{#each item in view.items}}{{view.title}} {{item}}{{/each}}"), title: "My Cool Each Test", items: A([1, 2]) }); append(view); equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2"); }); test("#each accepts a name binding and does not change the context", function() { var controller = EmberController.create({ name: 'bob the controller' }), obj = EmberObject.create({ name: 'henry the item' }); view = EmberView.create({ template: templateFor("{{#each item in view.items}}{{name}}{{/each}}"), title: "My Cool Each Test", items: A([obj]), controller: controller }); append(view); equal(view.$().text(), "bob the controller"); }); test("#each accepts a name binding and can display child properties", function() { view = EmberView.create({ template: templateFor("{{#each item in view.items}}{{view.title}} {{item.name}}{{/each}}"), title: "My Cool Each Test", items: A([{ name: 1 }, { name: 2 }]) }); append(view); equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2"); }); test("#each accepts 'this' as the right hand side", function() { view = EmberView.create({ template: templateFor("{{#each item in this}}{{view.title}} {{item.name}}{{/each}}"), title: "My Cool Each Test", controller: A([{ name: 1 }, { name: 2 }]) }); append(view); equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2"); }); test("views inside #each preserve the new context", function() { var controller = A([ { name: "Adam" }, { name: "Steve" } ]); view = EmberView.create({ container: container, controller: controller, template: templateFor('{{#each controller}}{{#view}}{{name}}{{/view}}{{/each}}') }); append(view); equal(view.$().text(), "AdamSteve"); }); test("controller is assignable inside an #each", function() { var controller = ArrayController.create({ model: A([ { name: "Adam" }, { name: "Steve" } ]) }); view = EmberView.create({ container: container, controller: controller, template: templateFor('{{#each personController in this}}{{#view controllerBinding="personController"}}{{name}}{{/view}}{{/each}}') }); append(view); equal(view.$().text(), "AdamSteve"); }); test("single-arg each defaults to current context", function() { view = EmberView.create({ context: A([ { name: "Adam" }, { name: "Steve" } ]), template: templateFor('{{#each}}{{name}}{{/each}}') }); append(view); equal(view.$().text(), "AdamSteve"); }); test("single-arg each will iterate over controller if present", function() { view = EmberView.create({ controller: A([ { name: "Adam" }, { name: "Steve" } ]), template: templateFor('{{#each}}{{name}}{{/each}}') }); append(view); equal(view.$().text(), "AdamSteve"); }); test("it asserts when the morph tags disagree on their parentage", function() { view = EmberView.create({ controller: A(['Cyril', 'David']), template: templateFor('{{#each}}{{/each}}
    {{this}}
    ') }); expectAssertion(function() { append(view); }, /The metamorph tags, metamorph-\d+-start and metamorph-\d+-end, have different parents.\nThe browser has fixed your template to output valid HTML \(for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '\{\{#each\}\}'\)/); }); test("it doesn't assert when the morph tags have the same parent", function() { view = EmberView.create({ controller: A(['Cyril', 'David']), template: templateFor('{{#each}}{{/each}}
    {{this}}
    ') }); append(view); ok(true, "No assertion from valid template"); }); test("itemController specified in template with name binding does not change context", function() { var Controller = EmberController.extend({ controllerName: computed(function() { return "controller:"+this.get('model.name'); }) }); var container = new Container(); people = A([{ name: "Steve Holt" }, { name: "Annabelle" }]); var parentController = { container: container, people: people, controllerName: 'controller:parentController' }; container.register('controller:array', ArrayController.extend()); view = EmberView.create({ container: container, template: templateFor('{{#each person in people itemController="person"}}{{controllerName}} - {{person.controllerName}} - {{/each}}'), controller: parentController }); container.register('controller:person', Controller); append(view); equal(view.$().text(), "controller:parentController - controller:Steve Holt - controller:parentController - controller:Annabelle - "); run(function() { people.pushObject({ name: "Yehuda Katz" }); }); assertText(view, "controller:parentController - controller:Steve Holt - controller:parentController - controller:Annabelle - controller:parentController - controller:Yehuda Katz - "); run(function() { set(parentController, 'people', A([{ name: "Trek Glowacki" }, { name: "Geoffrey Grosenbach" }])); }); assertText(view, "controller:parentController - controller:Trek Glowacki - controller:parentController - controller:Geoffrey Grosenbach - "); var controller = view.get('_childViews')[0].get('controller'); strictEqual(view.get('_childViews')[0].get('_arrayController.target'), parentController, "the target property of the child controllers are set correctly"); }); test("itemController specified in ArrayController with name binding does not change context", function() { people = A([{ name: "Steve Holt" }, { name: "Annabelle" }]); var PersonController = ObjectController.extend({ controllerName: computed(function() { return "controller:" + get(this, 'model.name') + ' of ' + get(this, 'parentController.company'); }) }), PeopleController = ArrayController.extend({ model: people, itemController: 'person', company: 'Yapp', controllerName: 'controller:people' }), container = new Container(); container.register('controller:people', PeopleController); container.register('controller:person', PersonController); view = EmberView.create({ container: container, template: templateFor('{{#each person in this}}{{controllerName}} - {{person.controllerName}} - {{/each}}'), controller: container.lookup('controller:people') }); append(view); equal(view.$().text(), "controller:people - controller:Steve Holt of Yapp - controller:people - controller:Annabelle of Yapp - "); }); }); define("ember-handlebars/tests/helpers/each_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/each_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/each_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/group_test", ["ember-metal/run_loop","ember-views/system/jquery","ember-views/views/view","ember-handlebars/views/metamorph_view","ember-handlebars-compiler","ember-runtime/system/array_proxy","ember-runtime/system/native_array","ember-runtime/system/container","ember-metal/property_get","ember-metal/property_set","ember-views/views/component"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; /*jshint newcap:false*/ var run = __dependency1__["default"]; var jQuery = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var _MetamorphView = __dependency4__["default"]; var EmberHandlebars = __dependency5__["default"]; var ArrayProxy = __dependency6__["default"]; var A = __dependency7__.A; var Container = __dependency8__["default"]; var get = __dependency9__.get; var set = __dependency10__.set; var Component = __dependency11__["default"]; var trim = jQuery.trim; var container, view; function appendView() { run(function() { view.appendTo('#qunit-fixture'); }); } QUnit.module("EmberHandlebars - group flag", { setup: function() { container = new Container(); container.register('view:default', _MetamorphView); container.register('view:toplevel', EmberView.extend()); }, teardown: function() { run(function() { if (view) { view.destroy(); } if (container) { container.destroy(); } container = view = null; }); run.cancelTimers(); } }); function createGroupedView(template, context) { var options = { container: container, context: context, template: EmberHandlebars.compile(template), templateData: {insideGroup: true, keywords: {}} }; run(function() { view = EmberView.create(options); }); } test("should properly modify behavior inside the block", function() { createGroupedView("{{msg}}", {msg: 'ohai'}); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(view.$().text(), 'ohai', 'Original value was rendered'); run(function() { view.set('context.msg', 'ohbai'); }); equal(view.$().text(), 'ohbai', 'Updated value was rendered'); run(function() { view.set('context.msg', null); }); equal(view.$().text(), '', 'null value properly rendered as a blank'); run(function() { view.set('context.msg', undefined); }); equal(view.$().text(), '', 'undefined value properly rendered as a blank'); }); test("property changes inside views should only rerender their view", function() { createGroupedView( '{{#view}}{{msg}}{{/view}}', {msg: 'ohai'} ); var rerenderWasCalled = false; view.reopen({ rerender: function() { rerenderWasCalled = true; this._super(); } }); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(trim(view.$().text()), 'ohai', 'Original value was rendered'); run(function() { view.set('context.msg', 'ohbai'); }); ok(!rerenderWasCalled, "The GroupView rerender method was not called"); equal(trim(view.$().text()), 'ohbai', "The updated value was rendered"); }); test("should work with bind-attr", function() { createGroupedView( '', {innerClass: 'magic'} ); appendView(); equal(view.$('.magic').length, 1); run(function() { view.set('context.innerClass', 'bindings'); }); equal(view.$('.bindings').length, 1); run(function() { view.rerender(); }); equal(view.$('.bindings').length, 1); }); test("should work with the #if helper", function() { createGroupedView( '{{#if something}}hooray{{else}}boo{{/if}}', {something: true} ); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(trim(view.$().text()), 'hooray', 'Truthy text was rendered'); run(function() { view.set('context.something', false); }); equal(trim(view.$().text()), 'boo', "The falsy value was rendered"); }); test("#each with no content", function() { expect(0); createGroupedView( "{{#each missing}}{{this}}{{/each}}" ); appendView(); }); test("#each's content can be changed right before a destroy", function() { expect(0); createGroupedView( "{{#each numbers}}{{this}}{{/each}}", {numbers: A([1,2,3])} ); appendView(); run(function() { view.set('context.numbers', A([3,2,1])); view.destroy(); }); }); test("#each can be nested", function() { createGroupedView( "{{#each numbers}}{{this}}{{/each}}", {numbers: A([1, 2, 3])} ); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(view.$().text(), '123', "The content was rendered"); run(function() { view.get('context.numbers').pushObject(4); }); equal(view.$().text(), '1234', "The array observer properly updated the rendered output"); run(function() { view.set('context.numbers', A(['a', 'b', 'c'])); }); equal(view.$().text(), 'abc', "Replacing the array properly updated the rendered output"); }); test("#each can be used with an ArrayProxy", function() { createGroupedView( "{{#each numbers}}{{this}}{{/each}}", {numbers: ArrayProxy.create({content: A([1, 2, 3])})} ); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(view.$().text(), '123', "The content was rendered"); }); test("should allow `#each item in array` format", function() { var yehuda = {name: 'Yehuda'}; createGroupedView( '{{#each person in people}}{{person.name}}{{/each}}', {people: A([yehuda, {name: 'Tom'}])} ); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(view.$().text(), 'YehudaTom', "The content was rendered"); run(function() { set(yehuda, 'name', 'Erik'); }); equal(view.$().text(), 'ErikTom', "The updated object value was rendered"); run(function() { view.get('context.people').pushObject({name: 'Alex'}); view.get('context.people').removeObject(yehuda); }); equal(view.$().text(), 'TomAlex', "The updated array content was rendered"); run(function() { view.set('context.people', A([{name: 'Sarah'},{name: 'Gavin'}])); }); equal(view.$().text(), 'SarahGavin', "The replaced array content was rendered"); }); test("an #each can be nested with a view inside", function() { var yehuda = {name: 'Yehuda'}; createGroupedView( '{{#each people}}{{#view}}{{name}}{{/view}}{{/each}}', {people: A([yehuda, {name: 'Tom'}])} ); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(view.$().text(), 'YehudaTom', "The content was rendered"); run(function() { set(yehuda, 'name', 'Erik'); }); equal(view.$().text(), 'ErikTom', "The updated object's view was rerendered"); }); test("an #each can be nested with a component inside", function() { var yehuda = {name: 'Yehuda'}; container.register('view:test', Component.extend()); createGroupedView( '{{#each people}}{{#view "test"}}{{name}}{{/view}}{{/each}}', {people: A([yehuda, {name: 'Tom'}])} ); appendView(); equal(view.$('script').length, 0, "No Metamorph markers are output"); equal(view.$().text(), 'YehudaTom', "The content was rendered"); run(function() { set(yehuda, 'name', 'Erik'); }); equal(view.$().text(), 'ErikTom', "The updated object's view was rerendered"); }); test("#each with groupedRows=true behaves like a normal bound #each", function() { createGroupedView( '{{#each numbers groupedRows=true}}{{this}}{{/each}}', {numbers: A([1, 2, 3])} ); appendView(); equal(view.$('script').length, 8, "Correct number of Metamorph markers are output"); equal(view.$().text(), '123'); run(function() { view.get('context.numbers').pushObject(4); }); equal(view.$('script').length, 10, "Correct number of Metamorph markers are output"); equal(view.$().text(), '1234'); }); test("#each with itemViewClass behaves like a normal bound #each", function() { createGroupedView( '{{#each people itemViewClass="Ember.View"}}{{name}}{{/each}}', {people: A([{name: 'Erik'}, {name: 'Peter'}])} ); appendView(); equal(view.$('script').length, 2, "Correct number of Metamorph markers are output"); equal(view.$('.ember-view').length, 2, "Correct number of views are output"); equal(view.$().text(), 'ErikPeter'); run(function() { view.get('context.people').pushObject({name: 'Tom'}); }); equal(view.$('script').length, 2, "Correct number of Metamorph markers are output"); equal(view.$('.ember-view').length, 3, "Correct number of views are output"); // IE likes to add newlines equal(trim(view.$().text()), 'ErikPeterTom'); }); test("should escape HTML in normal mustaches", function() { createGroupedView( '{{msg}}', {msg: 'you need to be more bold'} ); appendView(); equal(view.$('b').length, 0, "does not create an element"); equal(view.$().text(), 'you need to be more bold', "inserts entities, not elements"); }); test("should not escape HTML in triple mustaches", function() { createGroupedView( '{{{msg}}}', {msg: 'you need to be more bold'} ); appendView(); equal(view.$('b').length, 1, "creates an element"); }); }); define("ember-handlebars/tests/helpers/group_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/group_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/group_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/if_unless_test", ["ember-runtime/system/object","ember-metal/run_loop","ember-views/views/view","ember-runtime/system/object_proxy"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var EmberObject = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var ObjectProxy = __dependency4__["default"]; function appendView(view) { run(function() { view.appendTo('#qunit-fixture'); }); } var compile = Ember.Handlebars.compile; var view; QUnit.module("Handlebars {{#if}} and {{#unless}} helpers", { teardown: function() { run(function() { if (view) { view.destroy(); } }); } }); test("unless should keep the current context (#784)", function() { view = EmberView.create({ o: EmberObject.create({foo: '42'}), template: compile('{{#with view.o}}{{#view Ember.View}}{{#unless view.doesNotExist}}foo: {{foo}}{{/unless}}{{/view}}{{/with}}') }); appendView(view); equal(view.$().text(), 'foo: 42'); }); test("The `if` helper tests for `isTruthy` if available", function() { view = EmberView.create({ truthy: EmberObject.create({ isTruthy: true }), falsy: EmberObject.create({ isTruthy: false }), template: compile('{{#if view.truthy}}Yep{{/if}}{{#if view.falsy}}Nope{{/if}}') }); appendView(view); equal(view.$().text(), 'Yep'); }); test("The `if` helper does not print the contents for an object proxy without content", function() { view = EmberView.create({ truthy: ObjectProxy.create({ content: {} }), falsy: ObjectProxy.create({ content: null }), template: compile('{{#if view.truthy}}Yep{{/if}}{{#if view.falsy}}Nope{{/if}}') }); appendView(view); equal(view.$().text(), 'Yep'); }); test("The `if` helper updates if an object proxy gains or loses context", function() { view = EmberView.create({ proxy: ObjectProxy.create({ content: null }), template: compile('{{#if view.proxy}}Yep{{/if}}') }); appendView(view); equal(view.$().text(), ''); run(function() { view.set('proxy.content', {}); }); equal(view.$().text(), 'Yep'); run(function() { view.set('proxy.content', null); }); equal(view.$().text(), ''); }); test("The `if` helper updates if an array is empty or not", function() { view = EmberView.create({ array: Ember.A(), template: compile('{{#if view.array}}Yep{{/if}}') }); appendView(view); equal(view.$().text(), ''); run(function() { view.get('array').pushObject(1); }); equal(view.$().text(), 'Yep'); run(function() { view.get('array').removeObject(1); }); equal(view.$().text(), ''); }); test("The `if` helper updates when the value changes", function() { view = EmberView.create({ conditional: true, template: compile('{{#if view.conditional}}Yep{{/if}}') }); appendView(view); equal(view.$().text(), 'Yep'); run(function(){ view.set('conditional', false); }); equal(view.$().text(), ''); }); test("The `unbound if` helper does not update when the value changes", function() { view = EmberView.create({ conditional: true, template: compile('{{#unbound if view.conditional}}Yep{{/unbound}}') }); appendView(view); equal(view.$().text(), 'Yep'); run(function(){ view.set('conditional', false); }); equal(view.$().text(), 'Yep'); }); test("The `unless` helper updates when the value changes", function() { view = EmberView.create({ conditional: false, template: compile('{{#unless view.conditional}}Nope{{/unless}}') }); appendView(view); equal(view.$().text(), 'Nope'); run(function(){ view.set('conditional', true); }); equal(view.$().text(), ''); }); test("The `unbound if` helper does not update when the value changes", function() { view = EmberView.create({ conditional: false, template: compile('{{#unbound unless view.conditional}}Nope{{/unbound}}') }); appendView(view); equal(view.$().text(), 'Nope'); run(function(){ view.set('conditional', true); }); equal(view.$().text(), 'Nope'); }); test("The `if` helper ignores a controller option", function() { var lookupCalled = false; view = EmberView.create({ container: { lookup: function() { lookupCalled = true; } }, truthy: true, template: compile('{{#if view.truthy controller="foo"}}Yep{{/if}}') }); appendView(view); equal(lookupCalled, false, 'controller option should NOT be used'); }); }); define("ember-handlebars/tests/helpers/if_unless_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/if_unless_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/if_unless_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/loc_test", ["ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__) { "use strict"; var run = __dependency1__["default"]; var EmberView = __dependency2__["default"]; function buildView(template, context) { return EmberView.create({ template: Ember.Handlebars.compile(template), context: (context || {}) }); } function appendView(view) { run(function() { view.appendTo('#qunit-fixture'); }); } function destroyView(view) { run(function() { view.destroy(); }); } var oldString; QUnit.module('Handlebars {{loc valueToLocalize}} helper', { setup: function() { oldString = Ember.STRINGS; Ember.STRINGS = { '_Howdy Friend': 'Hallo Freund' }; }, teardown: function() { Ember.STRINGS = oldString; } }); test("let the original value through by default", function() { var view = buildView('{{loc "Hiya buddy!"}}'); appendView(view); equal(view.$().text(), "Hiya buddy!"); destroyView(view); }); test("localize a simple string", function() { var view = buildView('{{loc "_Howdy Friend"}}'); appendView(view); equal(view.$().text(), "Hallo Freund"); destroyView(view); }); }); define("ember-handlebars/tests/helpers/loc_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/loc_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/loc_test.js should pass jshint.'); }); }); define("ember-handlebars/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-handlebars-compiler"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var EmberObject = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var jQuery = __dependency4__["default"]; var trim = jQuery.trim; var Container = __dependency5__["default"]; var EmberHandlebars = __dependency6__["default"]; var compile = EmberHandlebars.compile; var MyApp; var originalLookup = Ember.lookup, lookup, TemplateTests, view, container; QUnit.module("Support for {{partial}} helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; MyApp = lookup.MyApp = EmberObject.create({}); container = new Container(); container.optionsForType('template', { instantiate: false }); }, teardown: function() { run(function() { if (view) { view.destroy(); } }); Ember.lookup = originalLookup; } }); test("should render other templates registered with the container", function() { container.register('template:_subTemplateFromContainer', EmberHandlebars.compile('sub-template')); view = EmberView.create({ container: container, template: EmberHandlebars.compile('This {{partial "subTemplateFromContainer"}} is pretty great.') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(trim(view.$().text()), "This sub-template is pretty great."); }); test("should render other slash-separated templates registered with the container", function() { container.register('template:child/_subTemplateFromContainer', EmberHandlebars.compile("sub-template")); view = EmberView.create({ container: container, template: EmberHandlebars.compile('This {{partial "child/subTemplateFromContainer"}} is pretty great.') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(trim(view.$().text()), "This sub-template is pretty great."); }); test("should use the current view's context", function() { container.register('template:_person_name', EmberHandlebars.compile("{{firstName}} {{lastName}}")); view = EmberView.create({ container: container, template: EmberHandlebars.compile('Who is {{partial "person_name"}}?') }); view.set('controller', EmberObject.create({ firstName: 'Kris', lastName: 'Selden' })); run(function() { view.appendTo('#qunit-fixture'); }); equal(trim(view.$().text()), "Who is Kris Selden?"); }); test("Quoteless parameters passed to {{template}} perform a bound property lookup of the partial name", function() { container.register('template:_subTemplate', EmberHandlebars.compile("sub-template")); container.register('template:_otherTemplate', EmberHandlebars.compile("other-template")); view = EmberView.create({ container: container, template: EmberHandlebars.compile('This {{partial view.partialName}} is pretty {{partial nonexistent}}great.'), partialName: 'subTemplate' }); run(function() { view.appendTo('#qunit-fixture'); }); equal(trim(view.$().text()), "This sub-template is pretty great."); run(function() { view.set('partialName', 'otherTemplate'); }); equal(trim(view.$().text()), "This other-template is pretty great."); run(function() { view.set('partialName', null); }); equal(trim(view.$().text()), "This is pretty great."); }); }); define("ember-handlebars/tests/helpers/partial_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/partial_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/partial_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/template_test", ["ember-metal/run_loop","ember-views/views/view","ember-runtime/system/object","ember-views/system/jquery","ember-runtime/system/container","ember-handlebars-compiler"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var run = __dependency1__["default"]; var EmberView = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var jQuery = __dependency4__["default"]; var trim = jQuery.trim; var Container = __dependency5__["default"]; var EmberHandlebars = __dependency6__["default"]; var MyApp; var originalLookup = Ember.lookup, lookup, TemplateTests, view, container; QUnit.module("Support for {{template}} helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; MyApp = lookup.MyApp = EmberObject.create({}); container = new Container(); container.optionsForType('template', { instantiate: false }); }, teardown: function() { run(function() { if (view) { view.destroy(); } }); Ember.lookup = originalLookup; } }); test("should render other templates via the container (DEPRECATED)", function() { container.register('template:sub_template_from_container', EmberHandlebars.compile('sub-template')); view = EmberView.create({ container: container, template: EmberHandlebars.compile('This {{template "sub_template_from_container"}} is pretty great.') }); expectDeprecation(/The `template` helper has been deprecated in favor of the `partial` helper./); run(function() { view.appendTo('#qunit-fixture'); }); equal(trim(view.$().text()), "This sub-template is pretty great."); }); test("should use the current view's context (DEPRECATED)", function() { container.register('template:person_name', EmberHandlebars.compile("{{firstName}} {{lastName}}")); view = EmberView.create({ container: container, template: EmberHandlebars.compile('Who is {{template "person_name"}}?') }); view.set('controller', EmberObject.create({ firstName: 'Kris', lastName: 'Selden' })); expectDeprecation(/The `template` helper has been deprecated in favor of the `partial` helper./); run(function() { view.appendTo('#qunit-fixture'); }); equal(trim(view.$().text()), "Who is Kris Selden?"); }); }); define("ember-handlebars/tests/helpers/template_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/template_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/template_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/unbound_test", ["ember-views/views/view","ember-runtime/system/object","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-handlebars-compiler","ember-metal/error","ember-runtime/system/container","ember-handlebars/ext"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; /*globals Foo */ var EmberView = __dependency1__["default"]; var EmberObject = __dependency2__["default"]; var Ember = __dependency3__["default"]; var get = __dependency4__.get; var set = __dependency5__.set; var run = __dependency6__["default"]; var EmberHandlebars = __dependency7__["default"]; var EmberError = __dependency8__["default"]; var Container = __dependency9__["default"]; var makeBoundHelper = __dependency10__.makeBoundHelper; function appendView(view) { run(function() { view.appendTo('#qunit-fixture'); }); } var view; var originalLookup = Ember.lookup, lookup; var container; QUnit.module("Handlebars {{#unbound}} helper -- classic single-property usage", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView.create({ template: Ember.Handlebars.compile("{{unbound foo}} {{unbound bar}}"), context: EmberObject.create({ foo: "BORK", barBinding: 'foo' }) }); appendView(view); }, teardown: function() { run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); test("it should render the current value of a property on the context", function() { equal(view.$().text(), "BORK BORK", "should render the current value of a property"); }); test("it should not re-render if the property changes", function() { run(function() { view.set('context.foo', 'OOF'); }); equal(view.$().text(), "BORK BORK", "should not re-render if the property changes"); }); test("it should throw the helper missing error if multiple properties are provided", function() { throws(function() { appendView(EmberView.create({ template: EmberHandlebars.compile('{{unbound foo bar}}'), context: EmberObject.create({ foo: "BORK", bar: 'foo' }) })); }, EmberError); }); QUnit.module("Handlebars {{#unbound boundHelper arg1 arg2... argN}} form: render unbound helper invocations", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; Ember.Handlebars.registerBoundHelper('surround', function(prefix, value, suffix) { return prefix + '-' + value + '-' + suffix; }); Ember.Handlebars.registerBoundHelper('capitalize', function(value) { return value.toUpperCase(); }); Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { return get(value, 'firstName').toUpperCase(); }, 'firstName'); Ember.Handlebars.registerBoundHelper('concat', function(value) { return [].slice.call(arguments, 0, -1).join(''); }); Ember.Handlebars.registerBoundHelper('concatNames', function(value) { return get(value, 'firstName') + get(value, 'lastName'); }, 'firstName', 'lastName'); }, teardown: function() { delete Ember.Handlebars.helpers['surround']; delete Ember.Handlebars.helpers['capitalize']; delete Ember.Handlebars.helpers['capitalizeName']; delete Ember.Handlebars.helpers['concat']; delete Ember.Handlebars.helpers['concatNames']; run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); test("should be able to render an unbound helper invocation", function() { try { Ember.Handlebars.registerBoundHelper('repeat', function(value, options) { var count = options.hash.count; var a = []; while(a.length < count) { a.push(value); } return a.join(''); }); view = EmberView.create({ template: Ember.Handlebars.compile('{{unbound repeat foo countBinding="bar"}} {{repeat foo countBinding="bar"}} {{unbound repeat foo count=2}} {{repeat foo count=4}}'), context: EmberObject.create({ foo: "X", numRepeatsBinding: "bar", bar: 5 }) }); appendView(view); equal(view.$().text(), "XXXXX XXXXX XX XXXX", "first render is correct"); run(function() { set(view, 'context.bar', 1); }); equal(view.$().text(), "XXXXX X XX XXXX", "only unbound bound options changed"); } finally { delete Ember.Handlebars.helpers['repeat']; } }); test("should be able to render an bound helper invocation mixed with static values", function() { view = EmberView.create({ template: Ember.Handlebars.compile('{{unbound surround prefix value "bar"}} {{surround prefix value "bar"}} {{unbound surround "bar" value suffix}} {{surround "bar" value suffix}}'), context: EmberObject.create({ prefix: "before", value: "core", suffix: "after" }) }); appendView(view); equal(view.$().text(), "before-core-bar before-core-bar bar-core-after bar-core-after", "first render is correct"); run(function() { set(view, 'context.prefix', 'beforeChanged'); set(view, 'context.value', 'coreChanged'); set(view, 'context.suffix', 'afterChanged'); }); equal(view.$().text(), "before-core-bar beforeChanged-coreChanged-bar bar-core-after bar-coreChanged-afterChanged", "only bound values change"); }); test("should be able to render unbound forms of multi-arg helpers", function() { view = EmberView.create({ template: Ember.Handlebars.compile("{{concat foo bar bing}} {{unbound concat foo bar bing}}"), context: EmberObject.create({ foo: "a", bar: "b", bing: "c" }) }); appendView(view); equal(view.$().text(), "abc abc", "first render is correct"); run(function() { set(view, 'context.bar', 'X'); }); equal(view.$().text(), "aXc abc", "unbound helpers/properties stayed the same"); }); test("should be able to render an unbound helper invocation for helpers with dependent keys", function() { view = EmberView.create({ template: Ember.Handlebars.compile("{{capitalizeName person}} {{unbound capitalizeName person}} {{concatNames person}} {{unbound concatNames person}}"), context: EmberObject.create({ person: EmberObject.create({ firstName: 'shooby', lastName: 'taylor' }) }) }); appendView(view); equal(view.$().text(), "SHOOBY SHOOBY shoobytaylor shoobytaylor", "first render is correct"); run(function() { set(view, 'context.person.firstName', 'sally'); }); equal(view.$().text(), "SALLY SHOOBY sallytaylor shoobytaylor", "only bound values change"); }); test("should be able to render an unbound helper invocation in #each helper", function() { view = EmberView.create({ template: Ember.Handlebars.compile( [ "{{#each person in people}}", "{{capitalize person.firstName}} {{unbound capitalize person.firstName}}", "{{/each}}"].join("")), context: { people: Ember.A([ { firstName: 'shooby', lastName: 'taylor' }, { firstName: 'cindy', lastName: 'taylor' } ])} }); appendView(view); equal(view.$().text(), "SHOOBY SHOOBYCINDY CINDY", "unbound rendered correctly"); }); test("should be able to render an unbound helper invocation with bound hash options", function() { try { Ember.Handlebars.registerBoundHelper('repeat', function(value) { return [].slice.call(arguments, 0, -1).join(''); }); view = EmberView.create({ template: Ember.Handlebars.compile("{{capitalizeName person}} {{unbound capitalizeName person}} {{concatNames person}} {{unbound concatNames person}}"), context: EmberObject.create({ person: EmberObject.create({ firstName: 'shooby', lastName: 'taylor' }) }) }); appendView(view); equal(view.$().text(), "SHOOBY SHOOBY shoobytaylor shoobytaylor", "first render is correct"); run(function() { set(view, 'context.person.firstName', 'sally'); }); equal(view.$().text(), "SALLY SHOOBY sallytaylor shoobytaylor", "only bound values change"); } finally { delete Ember.Handlebars.registerBoundHelper['repeat']; } }); QUnit.module("Handlebars {{#unbound}} helper -- Container Lookup", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; container = new Container(); container.optionsForType('helper', { instantiate: false }); }, teardown: function() { if (view) { run(view, 'destroy'); } Ember.lookup = originalLookup; } }); test("should lookup helpers in the container", function() { container.register('helper:up-case', makeBoundHelper(function(value) { return value.toUpperCase(); })); view = EmberView.create({ template: Ember.Handlebars.compile("{{unbound up-case displayText}}"), container: container, context: { displayText: 'such awesome' } }); appendView(view); equal(view.$().text(), "SUCH AWESOME", "proper values were rendered"); run(function() { set(view, 'context.displayText', 'no changes'); }); equal(view.$().text(), "SUCH AWESOME", "only bound values change"); }); }); define("ember-handlebars/tests/helpers/unbound_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/unbound_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/unbound_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/view_test", ["ember-views/views/view","container/container","ember-metal/run_loop","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; /*globals EmberDev */ var EmberView = __dependency1__["default"]; var Container = __dependency2__["default"]; var run = __dependency3__["default"]; var jQuery = __dependency4__["default"]; var view, originalLookup; var container = { lookupFactory: function() { } }; function viewClass(options) { options.container = options.container || container; return EmberView.extend(options); } QUnit.module("Handlebars {{#view}} helper", { setup: function() { originalLookup = Ember.lookup; }, teardown: function() { Ember.lookup = originalLookup; if (view) { run(view, 'destroy'); } } }); test("By default view:toplevel is used", function() { var DefaultView = viewClass({ elementId: 'toplevel-view', template: Ember.Handlebars.compile('hello world') }); var container = { lookupFactory: lookupFactory }; view = EmberView.extend({ template: Ember.Handlebars.compile('{{view}}'), container: container }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#toplevel-view').text(), 'hello world'); function lookupFactory(fullName) { equal(fullName, 'view:toplevel'); return DefaultView; } }); test("View lookup - App.FuView", function() { Ember.lookup = { App: { FuView: viewClass({ elementId: "fu", template: Ember.Handlebars.compile("bro") }) } }; view = viewClass({ template: Ember.Handlebars.compile("{{view App.FuView}}") }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#fu').text(), 'bro'); }); test("View lookup - 'App.FuView'", function() { Ember.lookup = { App: { FuView: viewClass({ elementId: "fu", template: Ember.Handlebars.compile("bro") }) } }; view = viewClass({ template: Ember.Handlebars.compile("{{view 'App.FuView'}}") }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#fu').text(), 'bro'); }); test("View lookup - 'fu'", function() { var FuView = viewClass({ elementId: "fu", template: Ember.Handlebars.compile("bro") }); var container = { lookupFactory: lookupFactory }; view = EmberView.extend({ template: Ember.Handlebars.compile("{{view 'fu'}}"), container: container }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#fu').text(), 'bro'); function lookupFactory(fullName) { equal(fullName, 'view:fu'); return FuView; } }); test("View lookup - view.computed", function() { var FuView = viewClass({ elementId: "fu", template: Ember.Handlebars.compile("bro") }); var container = { lookupFactory: lookupFactory }; view = EmberView.extend({ template: Ember.Handlebars.compile("{{view view.computed}}"), container: container, computed: 'fu' }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#fu').text(), 'bro'); function lookupFactory(fullName) { equal(fullName, 'view:fu'); return FuView; } }); test("id bindings downgrade to one-time property lookup", function() { view = EmberView.extend({ template: Ember.Handlebars.compile("{{#view Ember.View id=view.meshuggah}}{{view.parentView.meshuggah}}{{/view}}"), meshuggah: 'stengah' }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#stengah').text(), 'stengah', "id binding performed property lookup"); run(view, 'set', 'meshuggah', 'omg'); equal(jQuery('#stengah').text(), 'omg', "id didn't change"); }); test("mixing old and new styles of property binding fires a warning, treats value as if it were quoted", function() { if (EmberDev && EmberDev.runningProdBuild){ ok(true, 'Logging does not occur in production builds'); return; } expect(2); var oldWarn = Ember.warn; Ember.warn = function(msg) { equal(msg, "You're attempting to render a view by passing borfBinding=view.snork to a view helper, but this syntax is ambiguous. You should either surround view.snork in quotes or remove `Binding` from borfBinding."); }; view = EmberView.extend({ template: Ember.Handlebars.compile("{{#view Ember.View borfBinding=view.snork}}

    {{view.borf}}

    {{/view}}"), snork: "nerd" }).create(); run(view, 'appendTo', '#qunit-fixture'); equal(jQuery('#lol').text(), "nerd", "awkward mixed syntax treated like binding"); Ember.warn = oldWarn; }); test("allows you to pass attributes that will be assigned to the class instance, like class=\"foo\"", function() { expect(4); var container = new Container(); container.register('view:toplevel', EmberView.extend()); view = EmberView.extend({ template: Ember.Handlebars.compile('{{view id="foo" tagName="h1" class="foo"}}{{#view id="bar" class="bar"}}Bar{{/view}}'), container: container }).create(); run(view, 'appendTo', '#qunit-fixture'); ok(jQuery('#foo').hasClass('foo')); ok(jQuery('#foo').is('h1')); ok(jQuery('#bar').hasClass('bar')); equal(jQuery('#bar').text(), 'Bar'); }); }); define("ember-handlebars/tests/helpers/view_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/view_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/view_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/with_test", ["ember-views/views/view","ember-metal/run_loop","ember-runtime/system/object","ember-metal/computed","ember-handlebars-compiler","ember-metal/property_set","ember-metal/property_get","ember-runtime/controllers/object_controller","ember-runtime/system/container","ember-runtime/system/native_array"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; /*globals Foo */ /*jshint newcap:false*/ var EmberView = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var computed = __dependency4__.computed; var EmberHandlebars = __dependency5__["default"]; var set = __dependency6__.set; var get = __dependency7__.get; var ObjectController = __dependency8__["default"]; var Container = __dependency9__["default"]; var A = __dependency10__.A; function appendView(view) { run(function() { view.appendTo('#qunit-fixture'); }); } var view; var originalLookup = Ember.lookup, lookup; QUnit.module("Handlebars {{#with}} helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView.create({ template: EmberHandlebars.compile("{{#with person as tom}}{{title}}: {{tom.name}}{{/with}}"), context: { title: "Señor Engineer", person: { name: "Tom Dale" } } }); appendView(view); }, teardown: function() { run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); test("it should support #with foo as bar", function() { equal(view.$().text(), "Señor Engineer: Tom Dale", "should be properly scoped"); }); test("updating the context should update the alias", function() { run(function() { view.set('context.person', { name: "Yehuda Katz" }); }); equal(view.$().text(), "Señor Engineer: Yehuda Katz", "should be properly scoped after updating"); }); test("updating a property on the context should update the HTML", function() { run(function() { set(view, 'context.person.name', "Yehuda Katz"); }); equal(view.$().text(), "Señor Engineer: Yehuda Katz", "should be properly scoped after updating"); }); test("updating a property on the view should update the HTML", function() { run(function() { view.set('context.title', "Señorette Engineer"); }); equal(view.$().text(), "Señorette Engineer: Tom Dale", "should be properly scoped after updating"); }); QUnit.module("Multiple Handlebars {{with}} helpers with 'as'", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView.create({ template: EmberHandlebars.compile("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"} } }); appendView(view); }, teardown: function() { run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); 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"); }); test("the scoped variable is not available outside the {{with}} block.", function(){ run(function() { view.set('template', EmberHandlebars.compile("{{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"); }); test("nested {{with}} blocks shadow the outer scoped variable properly.", function(){ run(function() { view.set('template', EmberHandlebars.compile("{{#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", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; lookup.Foo = { bar: 'baz' }; view = EmberView.create({ template: EmberHandlebars.compile("{{#with Foo.bar as qux}}{{qux}}{{/with}}") }); appendView(view); }, teardown: function() { run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); test("it should support #with Foo.bar as qux", function() { equal(view.$().text(), "baz", "should be properly scoped"); run(function() { set(lookup.Foo, 'bar', 'updated'); }); equal(view.$().text(), "updated", "should update"); }); QUnit.module("Handlebars {{#with keyword as foo}}"); test("it should support #with view as foo", function() { var view = EmberView.create({ template: EmberHandlebars.compile("{{#with view as myView}}{{myView.name}}{{/with}}"), name: "Sonics" }); appendView(view); equal(view.$().text(), "Sonics", "should be properly scoped"); run(function() { set(view, 'name', "Thunder"); }); equal(view.$().text(), "Thunder", "should update"); run(function() { view.destroy(); }); }); test("it should support #with name as food, then #with foo as bar", function() { var view = EmberView.create({ template: EmberHandlebars.compile("{{#with name as foo}}{{#with foo as bar}}{{bar}}{{/with}}{{/with}}"), context: { name: "caterpillar" } }); appendView(view); equal(view.$().text(), "caterpillar", "should be properly scoped"); run(function() { set(view, 'context.name', "butterfly"); }); equal(view.$().text(), "butterfly", "should update"); run(function() { view.destroy(); }); }); QUnit.module("Handlebars {{#with this as foo}}"); test("it should support #with this as qux", function() { var view = EmberView.create({ template: EmberHandlebars.compile("{{#with this as person}}{{person.name}}{{/with}}"), controller: EmberObject.create({ name: "Los Pivots" }) }); appendView(view); equal(view.$().text(), "Los Pivots", "should be properly scoped"); run(function() { set(view, 'controller.name', "l'Pivots"); }); equal(view.$().text(), "l'Pivots", "should update"); run(function() { view.destroy(); }); }); QUnit.module("Handlebars {{#with foo}} insideGroup"); test("it should render without fail", function() { var View = EmberView.extend({ template: EmberHandlebars.compile("{{#view view.childView}}{{#with person}}{{name}}{{/with}}{{/view}}"), controller: EmberObject.create({ person: { name: "Ivan IV Vasilyevich" } }), childView: EmberView.extend({ render: function(){ this.set('templateData.insideGroup', true); return this._super.apply(this, arguments); } }) }); var view = View.create(); appendView(view); equal(view.$().text(), "Ivan IV Vasilyevich", "should be properly scoped"); run(function() { set(view, 'controller.person.name', "Ivan the Terrible"); }); equal(view.$().text(), "Ivan the Terrible", "should update"); run(function() { view.destroy(); }); }); QUnit.module("Handlebars {{#with foo}} with defined controller"); test("it should wrap context with object controller", function() { var Controller = ObjectController.extend({ controllerName: computed(function() { return "controller:"+this.get('model.name') + ' and ' + this.get('parentController.name'); }) }); var person = EmberObject.create({name: 'Steve Holt'}); var container = new Container(); var parentController = EmberObject.create({ container: container, name: 'Bob Loblaw' }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#with view.person controller="person"}}{{controllerName}}{{/with}}'), person: person, controller: parentController }); container.register('controller:person', Controller); appendView(view); equal(view.$().text(), "controller:Steve Holt and Bob Loblaw"); run(function() { view.rerender(); }); equal(view.$().text(), "controller:Steve Holt and Bob Loblaw"); run(function() { parentController.set('name', 'Carl Weathers'); view.rerender(); }); equal(view.$().text(), "controller:Steve Holt and Carl Weathers"); run(function() { person.set('name', 'Gob'); view.rerender(); }); equal(view.$().text(), "controller:Gob and Carl Weathers"); strictEqual(view.get('_childViews')[0].get('controller.target'), parentController, "the target property of the child controllers are set correctly"); run(function() { view.destroy(); }); // destroy existing view }); 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 container = new Container(); var parentController = EmberObject.create({ container: container, name: 'Bob Loblaw', people: people }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#each person in people}}{{#with person controller="person"}}{{controllerName}}{{/with}}{{/each}}'), controller: parentController }); container.register('controller:person', Controller); appendView(view); equal(view.$().text(), "controller:Steve Holt and Bob Loblawcontroller:Carl Weathers and Bob Loblaw"); run(function() { view.destroy(); }); // destroy existing view }); test("it should wrap keyword with object controller", function() { var PersonController = ObjectController.extend({ name: computed('model.name', function() { return get(this, 'model.name').toUpperCase(); }) }); var person = EmberObject.create({name: 'Steve Holt'}); var container = new Container(); var parentController = EmberObject.create({ container: container, person: person, name: 'Bob Loblaw' }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#with person as steve controller="person"}}{{name}} - {{steve.name}}{{/with}}'), controller: parentController }); container.register('controller:person', PersonController); appendView(view); equal(view.$().text(), "Bob Loblaw - STEVE HOLT"); run(function() { view.rerender(); }); equal(view.$().text(), "Bob Loblaw - STEVE HOLT"); run(function() { parentController.set('name', 'Carl Weathers'); view.rerender(); }); equal(view.$().text(), "Carl Weathers - STEVE HOLT"); run(function() { person.set('name', 'Gob'); view.rerender(); }); equal(view.$().text(), "Carl Weathers - GOB"); run(function() { view.destroy(); }); // destroy existing view }); test("destroys the controller generated with {{with foo controller='blah'}}", function() { var destroyed = false; var Controller = ObjectController.extend({ willDestroy: function() { this._super(); destroyed = true; } }); var person = EmberObject.create({name: 'Steve Holt'}); var container = new Container(); var parentController = EmberObject.create({ container: container, person: person, name: 'Bob Loblaw' }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#with person controller="person"}}{{controllerName}}{{/with}}'), controller: parentController }); container.register('controller:person', Controller); appendView(view); run(view, 'destroy'); // destroy existing view ok(destroyed, 'controller was destroyed properly'); }); test("destroys the controller generated with {{with foo as bar controller='blah'}}", function() { var destroyed = false; var Controller = ObjectController.extend({ willDestroy: function() { this._super(); destroyed = true; } }); var person = EmberObject.create({name: 'Steve Holt'}); var container = new Container(); var parentController = EmberObject.create({ container: container, person: person, name: 'Bob Loblaw' }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#with person as steve controller="person"}}{{controllerName}}{{/with}}'), controller: parentController }); container.register('controller:person', Controller); appendView(view); run(view, 'destroy'); // destroy existing view ok(destroyed, 'controller was destroyed properly'); }); QUnit.module("{{#with}} helper binding to view keyword", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; view = EmberView.create({ template: EmberHandlebars.compile("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" }, } }); appendView(view); }, teardown: function() { run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); 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"); }); }); define("ember-handlebars/tests/helpers/with_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/with_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/with_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/helpers/yield_test", ["ember-metal/run_loop","ember-views/views/view","ember-metal/computed","ember-runtime/system/namespace","ember-runtime/system/container","ember-handlebars-compiler","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/native_array","ember-views/views/component","ember-metal/error"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; /*jshint newcap:false*/ var run = __dependency1__["default"]; var EmberView = __dependency2__["default"]; var computed = __dependency3__.computed; var Namespace = __dependency4__["default"]; var Container = __dependency5__["default"]; var EmberHandlebars = __dependency6__["default"]; var get = __dependency7__.get; var set = __dependency8__.set; var A = __dependency9__.A; var Component = __dependency10__["default"]; var EmberError = __dependency11__["default"]; var originalLookup = Ember.lookup, lookup, TemplateTests, view, container; QUnit.module("Support for {{yield}} helper", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; lookup.TemplateTests = TemplateTests = Namespace.create(); container = new Container(); container.optionsForType('template', { instantiate: false }); }, teardown: function() { run(function() { Ember.TEMPLATES = {}; if (view) { view.destroy(); } }); Ember.lookup = originalLookup; } }); test("a view with a layout set renders its template where the {{yield}} helper appears", function() { TemplateTests.ViewWithLayout = EmberView.extend({ layout: EmberHandlebars.compile('

    {{title}}

    {{yield}}
    ') }); view = EmberView.create({ template: EmberHandlebars.compile('{{#view TemplateTests.ViewWithLayout title="My Fancy Page"}}
    Show something interesting here
    {{/view}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('div.wrapper div.page-body').length, 1, 'page-body is embedded within wrapping my-page'); }); test("block should work properly even when templates are not hard-coded", function() { container.register('template:nester', EmberHandlebars.compile('

    {{title}}

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

    {{boundText}}

    {{yield}}

    ") }); view = EmberView.create({ controller: { boundText: "outer", component: component }, template: EmberHandlebars.compile('{{#view component}}{{boundText}}{{/view}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('div p:contains(inner) + p:contains(outer)').length, 1, "Yield points at the right context"); }); test("yield inside a conditional uses the outer context", function() { var component = Component.extend({ boundText: "inner", truthy: true, obj: {}, layout: EmberHandlebars.compile("

    {{boundText}}

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

    ") }); view = EmberView.create({ controller: { boundText: "outer", truthy: true, obj: { component: component, truthy: true, boundText: 'insideWith' } }, template: EmberHandlebars.compile('{{#with obj}}{{#if truthy}}{{#view component}}{{#if truthy}}{{boundText}}{{/if}}{{/view}}{{/if}}{{/with}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('div p:contains(inner) + p:contains(insideWith)').length, 1, "Yield points at the right context"); }); test("outer keyword doesn't mask inner component property", function () { var component = Component.extend({ item: "inner", layout: EmberHandlebars.compile("

    {{item}}

    {{yield}}

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

    {{item}}

    {{yield}}

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

    {{content}}

    {{yield}}

    ") }); view = EmberView.create({ controller: { boundText: "outer", component: component }, template: EmberHandlebars.compile('{{#with boundText as item}}{{#view component contentBinding="item"}}{{item}}{{/view}}{{/with}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('div p:contains(outer) + p:contains(outer)').length, 1, "component and yield have keyword"); run(function() { view.set('controller.boundText', 'update'); }); equal(view.$('div p:contains(update) + p:contains(update)').length, 1, "keyword has correctly propagated update"); }); test("yield uses the layout context for non component", function() { view = EmberView.create({ controller: { boundText: "outer", inner: { boundText: "inner" } }, layout: EmberHandlebars.compile("

    {{boundText}}

    {{#with inner}}

    {{yield}}

    {{/with}}"), template: EmberHandlebars.compile('{{boundText}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal('outerinner', view.$('p').text(), "Yield points at the right context"); }); test("yield view should be a virtual view", function() { var component = Component.extend({ isParentComponent: true, layout: EmberHandlebars.compile('{{yield}}') }); view = EmberView.create({ template: EmberHandlebars.compile('{{#view component}}{{view includedComponent}}{{/view}}'), controller: { component: component, includedComponent: Component.extend({ didInsertElement: function() { var parentView = this.get('parentView'); ok(parentView.get('isParentComponent'), "parent view is the parent component"); } }) } }); run(function() { view.appendTo('#qunit-fixture'); }); }); test("adding a layout should not affect the context of normal views", function() { var parentView = EmberView.create({ context: "ParentContext" }); view = EmberView.create({ template: EmberHandlebars.compile("View context: {{this}}"), context: "ViewContext", _parentView: parentView }); run(function() { view.createElement(); }); equal(view.$().text(), "View context: ViewContext"); set(view, 'layout', EmberHandlebars.compile("Layout: {{yield}}")); run(function() { view.destroyElement(); view.createElement(); }); equal(view.$().text(), "Layout: View context: ViewContext"); run(function() { parentView.destroy(); }); }); test("yield should work for views even if _parentView is null", function() { view = EmberView.create({ layout: EmberHandlebars.compile('Layout: {{yield}}'), template: EmberHandlebars.compile("View Content") }); run(function() { view.createElement(); }); equal(view.$().text(), "Layout: View Content"); }); QUnit.module("Component {{yield}}", { setup: function() {}, teardown: function() { run(function() { if (view) { view.destroy(); } delete EmberHandlebars.helpers['inner-component']; delete EmberHandlebars.helpers['outer-component']; }); } }); test("yield with nested components (#3220)", function(){ var count = 0; var InnerComponent = Component.extend({ layout: EmberHandlebars.compile("{{yield}}"), _yield: function (context, options) { count++; if (count > 1) throw new EmberError('is looping'); return this._super(context, options); } }); EmberHandlebars.helper('inner-component', InnerComponent); var OuterComponent = Component.extend({ layout: EmberHandlebars.compile("{{#inner-component}}{{yield}}{{/inner-component}}") }); EmberHandlebars.helper('outer-component', OuterComponent); view = EmberView.create({ template: EmberHandlebars.compile( "{{#outer-component}}Hello world{{/outer-component}}" ) }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('div > span').text(), "Hello world"); }); test("yield works inside a conditional in a component that has Ember._Metamorph mixed in", function() { var component = Component.extend(Ember._Metamorph, { item: "inner", layout: Ember.Handlebars.compile("

    {{item}}

    {{#if item}}

    {{yield}}

    {{/if}}") }); view = Ember.View.create({ controller: { item: "outer", component: component }, template: Ember.Handlebars.compile('{{#view component}}{{item}}{{/view}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), 'innerouter', "{{yield}} renders yielded content inside metamorph component"); }); test("view keyword works inside component yield", function () { var component = Component.extend({ layout: EmberHandlebars.compile("

    {{yield}}

    ") }); view = EmberView.create({ dummyText: 'hello', component: component, template: EmberHandlebars.compile('{{#view view.component}}{{view.dummyText}}{{/view}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('div > p').text(), "hello", "view keyword inside component yield block should refer to the correct view"); }); }); define("ember-handlebars/tests/helpers/yield_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/yield_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/yield_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/loader_test", ["ember-views/system/jquery","ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var jQuery = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var trim = jQuery.trim; var originalLookup = Ember.lookup, lookup, Tobias, App, view; QUnit.module("test Ember.Handlebars.bootstrap", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; }, teardown: function() { Ember.TEMPLATES = {}; Ember.lookup = originalLookup; if(App) { run(App, 'destroy'); } if (view) { run(view, 'destroy'); } } }); function checkTemplate(templateName) { run(function() { Ember.Handlebars.bootstrap(jQuery('#qunit-fixture')); }); var template = Ember.TEMPLATES[templateName]; ok(template, 'template is available on Ember.TEMPLATES'); equal(jQuery('#qunit-fixture script').length, 0, 'script removed'); var view = EmberView.create({ template: template, context: { firstName: 'Tobias', drug: 'teamocil' } }); run(function() { view.createElement(); }); equal(trim(view.$().text()), 'Tobias takes teamocil', 'template works'); run(function() { view.destroy(); }); } test('template with data-template-name should add a new template to Ember.TEMPLATES', function() { jQuery('#qunit-fixture').html(''); checkTemplate('funkyTemplate'); }); test('template with id instead of data-template-name should add a new template to Ember.TEMPLATES', function() { jQuery('#qunit-fixture').html(''); checkTemplate('funkyTemplate'); }); test('template without data-template-name or id should default to application', function() { jQuery('#qunit-fixture').html(''); checkTemplate('application'); }); test('template with type text/x-raw-handlebars should be parsed', function() { jQuery('#qunit-fixture').html(''); run(function() { Ember.Handlebars.bootstrap(jQuery('#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"); }); test('duplicated default application templates should throw exception', function() { jQuery('#qunit-fixture').html(''); throws(function () { Ember.Handlebars.bootstrap(jQuery('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); test('default application template and id application template present should throw exception', function() { jQuery('#qunit-fixture').html(''); throws(function () { Ember.Handlebars.bootstrap(jQuery('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); test('default application template and data-template-name application template present should throw exception', function() { jQuery('#qunit-fixture').html(''); throws(function () { Ember.Handlebars.bootstrap(jQuery('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); test('duplicated template id should throw exception', function() { jQuery('#qunit-fixture').html(''); throws(function () { Ember.Handlebars.bootstrap(jQuery('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); test('duplicated template data-template-name should throw exception', function() { jQuery('#qunit-fixture').html(''); throws(function () { Ember.Handlebars.bootstrap(jQuery('#qunit-fixture')); }, /Template named "[^"]+" already exists\./, "duplicate templates should not be allowed"); }); if (Ember.component) { test('registerComponents initializer', function(){ Ember.TEMPLATES['components/x-apple'] = 'asdf'; App = run(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'); }); test('registerComponents and generated components', function(){ Ember.TEMPLATES['components/x-apple'] = 'asdf'; App = run(Ember.Application, 'create'); view = App.__container__.lookup('component:x-apple'); equal(view.get('layoutName'), 'components/x-apple', 'has correct layout name'); }); test('registerComponents and non-geneated components', function(){ Ember.TEMPLATES['components/x-apple'] = 'asdf'; run(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'); }); } }); define("ember-handlebars/tests/loader_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests'); test('ember-handlebars/tests/loader_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/loader_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/lookup_test", [], function() { "use strict"; QUnit.module("Ember.Handlebars.resolveParams"); test("Raw string parameters should be returned as Strings", function() { var params = Ember.Handlebars.resolveParams({}, ["foo", "bar", "baz"], { types: ["STRING", "STRING", "STRING"] }); deepEqual(params, ["foo", "bar", "baz"]); }); test("Raw boolean parameters should be returned as Booleans", function() { var params = Ember.Handlebars.resolveParams({}, [true, false], { types: ["BOOLEAN", "BOOLEAN"] }); deepEqual(params, [true, false]); }); test("Raw numeric parameters should be returned as Numbers", function() { var params = Ember.Handlebars.resolveParams({}, [1, 1.0, 1.5, 0.5], { types: ["NUMBER", "NUMBER", "NUMBER", "NUMBER"] }); deepEqual(params, [1, 1, 1.5, 0.5]); }); test("ID parameters should be looked up on the context", function() { var context = { salutation: "Mr", name: { first: "Tom", last: "Dale" } }; var params = Ember.Handlebars.resolveParams(context, ["salutation", "name.first", "name.last"], { types: ["ID", "ID", "ID"] }); deepEqual(params, ["Mr", "Tom", "Dale"]); }); test("ID parameters can look up keywords", function() { var controller = { salutation: "Mr" }; var view = { name: { first: "Tom", last: "Dale" } }; var context = { yuno: "State Charts" }; var options = { types: ["ID", "ID", "ID", "ID"], data: { keywords: { controller: controller, view: view } } }; var params = Ember.Handlebars.resolveParams(context, ["controller.salutation", "view.name.first", "view.name.last", "yuno"], options); deepEqual(params, ["Mr", "Tom", "Dale", "State Charts"]); }); QUnit.module("Ember.Handlebars.resolveHash"); test("Raw string parameters should be returned as Strings", function() { var hash = Ember.Handlebars.resolveHash({}, { string: "foo" }, { hashTypes: { string: "STRING" } }); deepEqual(hash, { string: "foo" }); }); test("Raw boolean parameters should be returned as Booleans", function() { var hash = Ember.Handlebars.resolveHash({}, { yes: true, no: false }, { hashTypes: { yes: "BOOLEAN", no: "BOOLEAN" } }); deepEqual(hash, { yes: true, no: false }); }); test("Raw numeric parameters should be returned as Numbers", function() { var hash = Ember.Handlebars.resolveHash({}, { one: 1, oneFive: 1.5, ohFive: 0.5 }, { hashTypes: { one: "NUMBER", oneFive: "NUMBER", ohFive: "NUMBER" } }); deepEqual(hash, { one: 1, oneFive: 1.5, ohFive: 0.5 }); }); test("ID parameters should be looked up on the context", function() { var context = { salutation: "Mr", name: { first: "Tom", last: "Dale" } }; var hash = Ember.Handlebars.resolveHash(context, { mr: "salutation", firstName: "name.first", lastName: "name.last" }, { hashTypes: { mr: "ID", firstName: "ID", lastName: "ID" } }); deepEqual(hash, { mr: "Mr", firstName: "Tom", lastName: "Dale" }); }); test("ID parameters can look up keywords", function() { var controller = { salutation: "Mr" }; var view = { name: { first: "Tom", last: "Dale" } }; var context = { yuno: "State Charts" }; var options = { hashTypes: { mr: "ID", firstName: "ID", lastName: "ID", yuno: "ID" }, data: { keywords: { controller: controller, view: view } } }; var hash = Ember.Handlebars.resolveHash(context, { mr: "controller.salutation", firstName: "view.name.first", lastName: "view.name.last", yuno: "yuno" }, options); deepEqual(hash, { mr: "Mr", firstName: "Tom", lastName: "Dale", yuno: "State Charts" }); }); }); define("ember-handlebars/tests/lookup_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests'); test('ember-handlebars/tests/lookup_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/lookup_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/views/collection_view_test", ["ember-views/views/view","ember-metal/run_loop","ember-views/system/jquery","ember-runtime/system/object","ember-metal/computed","ember-runtime/system/namespace","ember-runtime/system/array_proxy","ember-views/views/collection_view","ember-runtime/system/native_array","ember-runtime/system/container","ember-handlebars-compiler","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__) { "use strict"; /*globals TemplateTests:true,App:true */ /*jshint newcap:false*/ var EmberView = __dependency1__["default"]; var run = __dependency2__["default"]; var jQuery = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var computed = __dependency5__.computed; var Namespace = __dependency6__["default"]; var ArrayProxy = __dependency7__["default"]; var CollectionView = __dependency8__["default"]; var A = __dependency9__.A; var Container = __dependency10__["default"]; var EmberHandlebars = __dependency11__["default"]; var trim = jQuery.trim; var get = __dependency12__.get; var set = __dependency13__.set; function firstGrandchild(view) { return get(get(view, 'childViews').objectAt(0), 'childViews').objectAt(0); } function nthChild(view, nth) { return get(view, 'childViews').objectAt(nth || 0); } var firstChild = nthChild; var originalLookup = Ember.lookup, lookup, TemplateTests, view; QUnit.module("ember-handlebars/tests/views/collection_view_test", { setup: function() { Ember.lookup = lookup = { Ember: Ember }; lookup.TemplateTests = TemplateTests = Namespace.create(); }, teardown: function() { run(function() { if (view) { view.destroy(); } }); Ember.lookup = originalLookup; } }); test("passing a block to the collection helper sets it as the template for example views", function() { TemplateTests.CollectionTestView = CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection TemplateTests.CollectionTestView}} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('label').length, 3, 'one label element is created for each content item'); }); test("collection helper should try to use container to resolve view", function() { var container = new Container(); var ACollectionView = CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }); container.register('view:collectionTest', ACollectionView); var controller = {container: container}; view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('{{#collection "collectionTest"}} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('label').length, 3, 'one label element is created for each content item'); }); test("collection helper should accept relative paths", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#collection view.collection}} {{/collection}}'), collection: CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }) }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('label').length, 3, 'one label element is created for each content item'); }); test("empty views should be removed when content is added to the collection (regression, ht: msofaer)", function() { var App; run(function() { lookup.App = App = Namespace.create(); }); App.EmptyView = EmberView.extend({ template : EmberHandlebars.compile("No Rows Yet") }); App.ListView = CollectionView.extend({ emptyView: App.EmptyView }); App.listController = ArrayProxy.create({ content : A() }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection App.ListView contentBinding="App.listController" tagName="table"}} {{view.content.title}} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('tr').length, 1, 'Make sure the empty view is there (regression)'); run(function() { App.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.'); run(function() { App.destroy(); }); }); test("should be able to specify which class should be used for the empty view", function() { var App; run(function() { lookup.App = App = Namespace.create(); }); App.EmptyView = EmberView.extend({ template: EmberHandlebars.compile('This is an empty view') }); view = EmberView.create({ template: EmberHandlebars.compile('{{collection emptyViewClass="App.EmptyView"}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), 'This is an empty view', "Empty view should be rendered."); run(function() { App.destroy(); }); }); test("if no content is passed, and no 'else' is specified, nothing is rendered", function() { TemplateTests.CollectionTestView = CollectionView.extend({ tagName: 'ul', content: A() }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.CollectionTestView"}} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('li').length, 0, 'if no "else" is specified, nothing is rendered'); }); test("if no content is passed, and 'else' is specified, the else block is rendered", function() { TemplateTests.CollectionTestView = CollectionView.extend({ tagName: 'ul', content: A() }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.CollectionTestView"}} {{ else }} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('li:has(del)').length, 1, 'the else block is rendered'); }); test("a block passed to a collection helper defaults to the content property of the context", function() { TemplateTests.CollectionTestView = CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.CollectionTestView"}} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); 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'); }); test("a block passed to a collection helper defaults to the view", function() { TemplateTests.CollectionTestView = CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.CollectionTestView"}} {{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); // 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(function() { set(firstChild(view), 'content', A()); }); equal(view.$('label').length, 0, "all list item views should be removed from DOM"); }); test("should include an id attribute if id is set in the options hash", function() { TemplateTests.CollectionTestView = CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.CollectionTestView" id="baz"}}foo{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('ul#baz').length, 1, "adds an id attribute"); }); test("should give its item views the class specified by itemClass", function() { TemplateTests.itemClassTestCollectionView = CollectionView.extend({ tagName: 'ul', content: A(['foo', 'bar', 'baz']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection "TemplateTests.itemClassTestCollectionView" itemClass="baz"}}foo{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('ul li.baz').length, 3, "adds class attribute"); }); test("should give its item views the classBinding specified by itemClassBinding", function() { TemplateTests.itemClassBindingTestCollectionView = CollectionView.extend({ tagName: 'ul', content: A([EmberObject.create({ isBaz: false }), EmberObject.create({ isBaz: true }), EmberObject.create({ isBaz: true })]) }); view = EmberView.create({ isBar: true, template: EmberHandlebars.compile('{{#collection "TemplateTests.itemClassBindingTestCollectionView" itemClassBinding="view.isBar"}}foo{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); 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"`. }); test("should give its item views the property specified by itemPropertyBinding", function() { TemplateTests.itemPropertyBindingTestItemView = EmberView.extend({ tagName: 'li' }); // Use preserveContext=false so the itemView handlebars context is the view context // Set itemView bindings using item* view = EmberView.create({ baz: "baz", content: A([EmberObject.create(), EmberObject.create(), EmberObject.create()]), template: EmberHandlebars.compile('{{#collection contentBinding="view.content" tagName="ul" itemViewClass="TemplateTests.itemPropertyBindingTestItemView" itemPropertyBinding="view.baz" preserveContext=false}}{{view.property}}{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('ul li').length, 3, "adds 3 itemView"); view.$('ul li').each(function(i, li) { equal(jQuery(li).text(), "baz", "creates the li with the property = baz"); }); run(function() { set(view, 'baz', "yobaz"); }); equal(view.$('ul li:first').text(), "yobaz", "change property of sub view"); }); test("should work inside a bound {{#if}}", function() { var testData = A([EmberObject.create({ isBaz: false }), EmberObject.create({ isBaz: true }), EmberObject.create({ isBaz: true })]); TemplateTests.ifTestCollectionView = CollectionView.extend({ tagName: 'ul', content: testData }); view = EmberView.create({ template: EmberHandlebars.compile('{{#if view.shouldDisplay}}{{#collection "TemplateTests.ifTestCollectionView"}}{{content.isBaz}}{{/collection}}{{/if}}'), shouldDisplay: true }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('ul li').length, 3, "renders collection when conditional is true"); run(function() { set(view, 'shouldDisplay', false); }); equal(view.$('ul li').length, 0, "removes collection when conditional changes to false"); run(function() { set(view, 'shouldDisplay', true); }); equal(view.$('ul li').length, 3, "collection renders when conditional changes to true"); }); test("should pass content as context when using {{#each}} helper", function() { view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.releases}}Mac OS X {{version}}: {{name}} {{/each}}'), releases: A([ { version: '10.7', name: 'Lion' }, { version: '10.6', name: 'Snow Leopard' }, { version: '10.5', name: 'Leopard' } ]) }); run(function() { view.appendTo('#qunit-fixture'); }); 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"); }); test("should re-render when the content object changes", function() { TemplateTests.RerenderTest = CollectionView.extend({ tagName: 'ul', content: A() }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection TemplateTests.RerenderTest}}{{view.content}}{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); run(function() { set(firstChild(view), 'content', A(['bing', 'bat', 'bang'])); }); run(function() { set(firstChild(view), 'content', A(['ramalamadingdong'])); }); equal(view.$('li').length, 1, "rerenders with correct number of items"); equal(trim(view.$('li:eq(0)').text()), "ramalamadingdong"); }); test("select tagName on collection helper automatically sets child tagName to option", function() { TemplateTests.RerenderTest = CollectionView.extend({ content: A(['foo']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection TemplateTests.RerenderTest tagName="select"}}{{view.content}}{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('option').length, 1, "renders the correct child tag name"); }); test("tagName works in the #collection helper", function() { TemplateTests.RerenderTest = CollectionView.extend({ content: A(['foo', 'bar']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection TemplateTests.RerenderTest tagName="ol"}}{{view.content}}{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('ol').length, 1, "renders the correct tag name"); equal(view.$('li').length, 2, "rerenders with correct number of items"); run(function() { set(firstChild(view), 'content', A(['bing', 'bat', 'bang'])); }); equal(view.$('li').length, 3, "rerenders with correct number of items"); equal(trim(view.$('li:eq(0)').text()), "bing"); }); test("should render nested collections", function() { TemplateTests.InnerList = CollectionView.extend({ tagName: 'ul', content: A(['one','two','three']) }); TemplateTests.OuterList = CollectionView.extend({ tagName: 'ul', content: A(['foo']) }); view = EmberView.create({ template: EmberHandlebars.compile('{{#collection TemplateTests.OuterList class="outer"}}{{content}}{{#collection TemplateTests.InnerList class="inner"}}{{content}}{{/collection}}{{/collection}}') }); run(function() { view.appendTo('#qunit-fixture'); }); 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"); }); test("should render multiple, bound nested collections (#68)", function() { var view; run(function() { TemplateTests.contentController = ArrayProxy.create({ content: A(['foo','bar']) }); TemplateTests.InnerList = CollectionView.extend({ tagName: 'ul', contentBinding: 'parentView.innerListContent' }); TemplateTests.OuterListItem = EmberView.extend({ template: EmberHandlebars.compile('{{#collection TemplateTests.InnerList class="inner"}}{{content}}{{/collection}}{{content}}'), innerListContent: computed(function() { return A([1,2,3]); }) }); TemplateTests.OuterList = CollectionView.extend({ tagName: 'ul', contentBinding: 'TemplateTests.contentController', itemViewClass: TemplateTests.OuterListItem }); view = EmberView.create({ template: EmberHandlebars.compile('{{collection TemplateTests.OuterList class="outer"}}') }); }); run(function() { view.appendTo('#qunit-fixture'); }); 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"); run(function() { view.destroy(); }); }); test("should allow view objects to be swapped out without throwing an error (#78)", function() { var view, dataset, secondDataset; run(function() { TemplateTests.datasetController = EmberObject.create(); TemplateTests.ReportingView = EmberView.extend({ datasetBinding: 'TemplateTests.datasetController.dataset', readyBinding: 'dataset.ready', itemsBinding: 'dataset.items', template: EmberHandlebars.compile("{{#if view.ready}}{{collection TemplateTests.CollectionView}}{{else}}Loading{{/if}}") }); TemplateTests.CollectionView = CollectionView.extend({ contentBinding: 'parentView.items', tagName: 'ul', template: EmberHandlebars.compile("{{view.content}}") }); view = TemplateTests.ReportingView.create(); }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), "Loading", "renders the loading text when the dataset is not ready"); run(function() { dataset = EmberObject.create({ ready: true, items: 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(function() { secondDataset = EmberObject.create({ready: false}); TemplateTests.datasetController.set('dataset',secondDataset); }); equal(view.$().text(), "Loading", "renders the loading text when the second dataset is not ready"); run(function() { view.destroy(); }); }); test("context should be content", function() { var App, view; run(function() { lookup.App = App = Namespace.create(); }); App.items = A([ EmberObject.create({name: 'Dave'}), EmberObject.create({name: 'Mary'}), EmberObject.create({name: 'Sara'}) ]); App.AnItemView = EmberView.extend({ template: EmberHandlebars.compile("Greetings {{name}}") }); App.AView = EmberView.extend({ template: EmberHandlebars.compile('{{collection contentBinding="App.items" itemViewClass="App.AnItemView"}}') }); run(function() { view = App.AView.create(); }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), "Greetings DaveGreetings MaryGreetings Sara"); run(function() { view.destroy(); App.destroy(); }); }); }); define("ember-handlebars/tests/views/collection_view_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/views'); test('ember-handlebars/tests/views/collection_view_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/views/collection_view_test.js should pass jshint.'); }); }); define("ember-handlebars/tests/views/handlebars_bound_view_test", ["ember-handlebars/views/handlebars_bound_view"], function(__dependency1__) { "use strict"; var SimpleHandlebarsView = __dependency1__.SimpleHandlebarsView; QUnit.module('SimpleHandlebarsView'); test('does not render if update is triggured by normalizedValue is the same as the previous normalizedValue', function(){ var html = null; var path = 'foo'; var pathRoot = { 'foo': 'bar' }; var isEscaped = false; var templateData; var view = new SimpleHandlebarsView(path, pathRoot, isEscaped, templateData); view.morph.html = function(newHTML) { html = newHTML; }; equal(html, null); view.update(); equal(html, 'bar', 'expected call to morph.html with "bar"'); html = null; view.update(); equal(html, null, 'expected no call to morph.html'); pathRoot.foo = 'baz'; // change property view.update(); equal(html, 'baz', 'expected call to morph.html with "baz"'); }); }); define("ember-handlebars/tests/views/handlebars_bound_view_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/views'); test('ember-handlebars/tests/views/handlebars_bound_view_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/views/handlebars_bound_view_test.js should pass jshint.'); }); }); define("ember-handlebars/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-metal/mixin","ember-handlebars-compiler","ember-handlebars/views/metamorph_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var jQuery = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var get = __dependency4__.get; var set = __dependency5__.set; var observer = __dependency6__.observer; var EmberHandlebars = __dependency7__["default"]; var _MetamorphView = __dependency8__["default"]; var view, childView, metamorphView; QUnit.module("Metamorph views", { setup: function() { view = EmberView.create({ render: function(buffer) { buffer.push("

    View

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

    Bye bros

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

    Meta

    "); this.appendChild(childView); } }); run(function() { view.appendTo("#qunit-fixture"); }); equal(get(childView, 'parentView'), view, "A child of a metamorph view cannot see the metamorph view as its parent"); var children = get(view, 'childViews'); equal(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.create({ render: function(buffer) { buffer.push("

    View

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

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

    "); } }); run(function() { view.appendTo("#qunit-fixture"); }); }, teardown: function() { run(function() { view.destroy(); if (!metamorphView.isDestroyed) { metamorphView.destroy(); } }); } }); test("a metamorph view generates without a DOM node", function() { var meta = jQuery("> h2", "#" + get(view, 'elementId')); equal(meta.length, 1, "The metamorph element should be directly inside its parent"); }); test("a metamorph view can be removed from the DOM", function() { run(function() { metamorphView.destroy(); }); var meta = jQuery('#from-morph'); equal(meta.length, 0, "the associated DOM was removed"); }); test("a metamorph view can be rerendered", function() { equal(jQuery('#from-meta').text(), "Jason", "precond - renders to the DOM"); set(metamorphView, 'powerRanger', 'Trini'); run(function() { metamorphView.rerender(); }); equal(jQuery('#from-meta').text(), "Trini", "updates value when re-rendering"); }); // Redefining without setup/teardown QUnit.module("Metamorph views correctly handle DOM"); test("a metamorph view calls its childrens' willInsertElement and didInsertElement", function() { var parentView; var willInsertElementCalled = false; var didInsertElementCalled = false; var didInsertElementSawElement = false; parentView = EmberView.create({ ViewWithCallback: EmberView.extend({ template: EmberHandlebars.compile('
    '), willInsertElement: function() { willInsertElementCalled = true; }, didInsertElement: function() { didInsertElementCalled = true; didInsertElementSawElement = (this.$('div').length === 1); } }), template: EmberHandlebars.compile('{{#if view.condition}}{{view "view.ViewWithCallback"}}{{/if}}'), condition: false }); run(function() { parentView.append(); }); run(function() { parentView.set('condition', true); }); ok(willInsertElementCalled, "willInsertElement called"); ok(didInsertElementCalled, "didInsertElement called"); ok(didInsertElementSawElement, "didInsertElement saw element"); run(function() { parentView.destroy(); }); }); test("replacing a Metamorph should invalidate childView elements", function() { var elementOnDidChange, elementOnDidInsert; view = EmberView.create({ show: false, CustomView: EmberView.extend({ init: function() { this._super(); // 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'); }, elementDidChange: observer('element', function() { elementOnDidChange = this.get('element'); }), didInsertElement: function() { elementOnDidInsert = this.get('element'); } }), template: EmberHandlebars.compile("{{#if view.show}}{{view view.CustomView}}{{/if}}") }); run(function() { view.append(); }); run(function() { view.set('show', true); }); ok(elementOnDidChange, "should have an element on change"); ok(elementOnDidInsert, "should have an element on insert"); run(function() { view.destroy(); }); }); test("trigger rerender of parent and SimpleHandlebarsView", function () { var view = EmberView.create({ show: true, foo: 'bar', template: EmberHandlebars.compile("{{#if view.show}}{{#if view.foo}}{{view.foo}}{{/if}}{{/if}}") }); run(function() { view.append(); }); equal(view.$().text(), 'bar'); run(function() { view.set('foo', 'baz'); // schedule render of simple bound view.set('show', false); // destroy tree }); equal(view.$().text(), ''); run(function() { view.destroy(); }); }); test("re-rendering and then changing the property does not raise an exception", function() { view = EmberView.create({ show: true, foo: 'bar', metamorphView: _MetamorphView, template: EmberHandlebars.compile("{{#view view.metamorphView}}truth{{/view}}") }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), 'truth'); run(function() { view.get('_childViews')[0].rerender(); view.get('_childViews')[0].rerender(); }); equal(view.$().text(), 'truth'); run(function() { view.destroy(); }); }); }); define("ember-handlebars/tests/views/metamorph_view_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/views'); test('ember-handlebars/tests/views/metamorph_view_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/views/metamorph_view_test.js should pass jshint.'); }); }); define("ember-handlebars/views/handlebars_bound_view.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/views'); test('ember-handlebars/views/handlebars_bound_view.js should pass jshint', function() { ok(true, 'ember-handlebars/views/handlebars_bound_view.js should pass jshint.'); }); }); define("ember-handlebars/views/metamorph_view.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/views'); test('ember-handlebars/views/metamorph_view.js should pass jshint', function() { ok(true, 'ember-handlebars/views/metamorph_view.js should pass jshint.'); }); }); define("ember-metal.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-metal.js should pass jshint', function() { ok(true, 'ember-metal.js should pass jshint.'); }); }); define("ember-metal/alias.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/alias.js should pass jshint', function() { ok(true, 'ember-metal/alias.js should pass jshint.'); }); }); define("ember-metal/array.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/array.js should pass jshint', function() { ok(true, 'ember-metal/array.js should pass jshint.'); }); }); define("ember-metal/binding.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/binding.js should pass jshint', function() { ok(true, 'ember-metal/binding.js should pass jshint.'); }); }); define("ember-metal/chains.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/chains.js should pass jshint', function() { ok(true, 'ember-metal/chains.js should pass jshint.'); }); }); define("ember-metal/computed.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/computed.js should pass jshint', function() { ok(true, 'ember-metal/computed.js should pass jshint.'); }); }); define("ember-metal/computed_macros.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/computed_macros.js should pass jshint', function() { ok(true, 'ember-metal/computed_macros.js should pass jshint.'); }); }); define("ember-metal/core.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/core.js should pass jshint', function() { ok(true, 'ember-metal/core.js should pass jshint.'); }); }); define("ember-metal/dependent_keys.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/dependent_keys.js should pass jshint', function() { ok(true, 'ember-metal/dependent_keys.js should pass jshint.'); }); }); define("ember-metal/enumerable_utils.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/enumerable_utils.js should pass jshint', function() { ok(true, 'ember-metal/enumerable_utils.js should pass jshint.'); }); }); define("ember-metal/error.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/error.js should pass jshint', function() { ok(true, 'ember-metal/error.js should pass jshint.'); }); }); define("ember-metal/events.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/events.js should pass jshint', function() { ok(true, 'ember-metal/events.js should pass jshint.'); }); }); define("ember-metal/expand_properties.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/expand_properties.js should pass jshint', function() { ok(true, 'ember-metal/expand_properties.js should pass jshint.'); }); }); define("ember-metal/get_properties.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/get_properties.js should pass jshint', function() { ok(true, 'ember-metal/get_properties.js should pass jshint.'); }); }); define("ember-metal/instrumentation.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/instrumentation.js should pass jshint', function() { ok(true, 'ember-metal/instrumentation.js should pass jshint.'); }); }); define("ember-metal/is_blank.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/is_blank.js should pass jshint', function() { ok(true, 'ember-metal/is_blank.js should pass jshint.'); }); }); define("ember-metal/is_empty.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/is_empty.js should pass jshint', function() { ok(true, 'ember-metal/is_empty.js should pass jshint.'); }); }); define("ember-metal/is_none.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/is_none.js should pass jshint', function() { ok(true, 'ember-metal/is_none.js should pass jshint.'); }); }); define("ember-metal/libraries.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/libraries.js should pass jshint', function() { ok(true, 'ember-metal/libraries.js should pass jshint.'); }); }); define("ember-metal/logger.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/logger.js should pass jshint', function() { ok(true, 'ember-metal/logger.js should pass jshint.'); }); }); define("ember-metal/map.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/map.js should pass jshint', function() { ok(true, 'ember-metal/map.js should pass jshint.'); }); }); define("ember-metal/merge.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/merge.js should pass jshint', function() { ok(true, 'ember-metal/merge.js should pass jshint.'); }); }); define("ember-metal/mixin.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/mixin.js should pass jshint', function() { ok(true, 'ember-metal/mixin.js should pass jshint.'); }); }); define("ember-metal/observer.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/observer.js should pass jshint', function() { ok(true, 'ember-metal/observer.js should pass jshint.'); }); }); define("ember-metal/observer_set.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/observer_set.js should pass jshint', function() { ok(true, 'ember-metal/observer_set.js should pass jshint.'); }); }); define("ember-metal/platform.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/platform.js should pass jshint', function() { ok(true, 'ember-metal/platform.js should pass jshint.'); }); }); define("ember-metal/properties.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/properties.js should pass jshint', function() { ok(true, 'ember-metal/properties.js should pass jshint.'); }); }); define("ember-metal/property_events.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/property_events.js should pass jshint', function() { ok(true, 'ember-metal/property_events.js should pass jshint.'); }); }); define("ember-metal/property_get.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/property_get.js should pass jshint', function() { ok(true, 'ember-metal/property_get.js should pass jshint.'); }); }); define("ember-metal/property_set.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/property_set.js should pass jshint', function() { ok(true, 'ember-metal/property_set.js should pass jshint.'); }); }); define("ember-metal/run_loop.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/run_loop.js should pass jshint', function() { ok(true, 'ember-metal/run_loop.js should pass jshint.'); }); }); define("ember-metal/set_properties.jshint", [], function() { "use strict"; module('JSHint - ember-metal'); test('ember-metal/set_properties.js should pass jshint', function() { ok(true, 'ember-metal/set_properties.js should pass jshint.'); }); }); define("ember-metal/tests/accessors/getPath_test", ["ember-metal/property_get"], function(__dependency1__) { "use strict"; /*globals Foo:true $foo:true */ var get = __dependency1__.get; var obj, moduleOpts = { setup: function() { obj = { foo: { bar: { baz: { biff: 'BIFF' } } }, falseValue: false }; window.Foo = { bar: { baz: { biff: 'FooBiff' } } }; window.$foo = { bar: { baz: { biff: '$FOOBIFF' } } }; }, teardown: function() { obj = undefined; window.Foo = undefined; window.$foo = undefined; } }; QUnit.module('Ember.get with path', moduleOpts); // .......................................................... // LOCAL PATHS // test('[obj, foo] -> obj.foo', function() { deepEqual(get(obj, 'foo'), obj.foo); }); test('[obj, foo.bar] -> obj.foo.bar', function() { deepEqual(get(obj, 'foo.bar'), obj.foo.bar); }); test('[obj, this.foo] -> obj.foo', function() { deepEqual(get(obj, 'this.foo'), obj.foo); }); test('[obj, this.foo.bar] -> obj.foo.bar', function() { deepEqual(get(obj, 'this.foo.bar'), obj.foo.bar); }); test('[obj, this.Foo.bar] -> (null)', function() { deepEqual(get(obj, 'this.Foo.bar'), undefined); }); test('[obj, falseValue.notDefined] -> (null)', function() { deepEqual(get(obj, 'falseValue.notDefined'), undefined); }); // .......................................................... // NO TARGET // test('[null, Foo] -> Foo', function() { deepEqual(get('Foo'), Foo); }); test('[null, Foo.bar] -> Foo.bar', function() { deepEqual(get('Foo.bar'), Foo.bar); }); }); define("ember-metal/tests/accessors/getPath_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/getPath_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/getPath_test.js should pass jshint.'); }); }); define("ember-metal/tests/accessors/getProperties_test", ["ember-metal/get_properties"], function(__dependency1__) { "use strict"; var getProperties = __dependency1__["default"]; QUnit.module('Ember.getProperties'); 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(obj, "firstName", "lastName"), { firstName: 'Steve', lastName: 'Jobs' }); deepEqual(getProperties(obj, "firstName", "lastName"), { firstName: 'Steve', lastName: 'Jobs' }); deepEqual(getProperties(obj, "lastName"), { lastName: 'Jobs' }); deepEqual(getProperties(obj), {}); deepEqual(getProperties(obj, ["firstName", "lastName"]), { firstName: 'Steve', lastName: 'Jobs' }); deepEqual(getProperties(obj, ["firstName"]), { firstName: 'Steve' }); deepEqual(getProperties(obj, []), {}); }); }); define("ember-metal/tests/accessors/getProperties_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/getProperties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/getProperties_test.js should pass jshint.'); }); }); define("ember-metal/tests/accessors/get_test", ["ember-metal/tests/props_helper","ember-metal/property_get","ember-metal/mixin","ember-metal/observer","ember-metal/platform"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var testBoth = __dependency1__["default"]; var get = __dependency2__.get; var getWithDefault = __dependency2__.getWithDefault; var Mixin = __dependency3__.Mixin; var observer = __dependency3__.observer; var addObserver = __dependency4__.addObserver; var create = __dependency5__.create; QUnit.module('Ember.get'); 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(get(obj, key), obj[key], key); } }); 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; addObserver(obj, 'foo', function() { count++; }); equal(get(obj, 'foo'), 'FOO', 'should return value from unknown'); }); test('warn on attempts to get a property of undefined', function() { expectAssertion(function() { get(undefined, 'aProperty'); }, /Cannot call get with 'aProperty' on an undefined object/i); }); test('warn on attempts to get a property path of undefined', function() { expectAssertion(function() { get(undefined, 'aProperty.on.aPath'); }, /Cannot call get with 'aProperty.on.aPath' on an undefined object/); }); test('warn on attempts to get a falsy property', function() { var obj = {}; expectAssertion(function() { get(obj, null); }, /Cannot call get with null key/); expectAssertion(function() { get(obj, NaN); }, /Cannot call get with NaN key/); expectAssertion(function() { get(obj, undefined); }, /Cannot call get with undefined key/); expectAssertion(function() { get(obj, false); }, /Cannot call get with false key/); }); // .......................................................... // BUGS // test('(regression) watched properties on unmodified inherited objects should still return their original value', function() { var MyMixin = Mixin.create({ someProperty: 'foo', propertyDidChange: observer('someProperty', function() { // NOTHING TO DO }) }); var baseObject = MyMixin.apply({}); var theRealObject = create(baseObject); equal(get(theRealObject, 'someProperty'), 'foo', 'should return the set value, not false'); }); QUnit.module("Ember.getWithDefault"); 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(getWithDefault(obj, key, "fail"), obj[key], key); } obj = { undef: undefined }; equal(getWithDefault(obj, "undef", "default"), "default", "explicit undefined retrieves the default"); equal(getWithDefault(obj, "not-present", "default"), "default", "non-present key retrieves the default"); }); 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(get(obj, 'foo'), 'FOO', 'should return value from unknown'); equal(obj.count, 1, 'should have invoked'); }); 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; addObserver(obj, 'foo', function() { count++; }); equal(getWithDefault(obj, 'foo', "fail"), 'FOO', 'should return value from unknownProperty'); equal(getWithDefault(obj, 'bar', "default"), 'default', 'should convert undefined from unknownProperty into default'); }); // .......................................................... // BUGS // test('(regression) watched properties on unmodified inherited objects should still return their original value', function() { var MyMixin = Mixin.create({ someProperty: 'foo', propertyDidChange: observer('someProperty', function() { // NOTHING TO DO }) }); var baseObject = MyMixin.apply({}); var theRealObject = create(baseObject); equal(getWithDefault(theRealObject, 'someProperty', "fail"), 'foo', 'should return the set value, not false'); }); }); define("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.'); }); }); define("ember-metal/tests/accessors/isGlobalPath_test", ["ember-metal/binding"], function(__dependency1__) { "use strict"; var isGlobalPath = __dependency1__.isGlobalPath; QUnit.module('Ember.isGlobalPath'); test("global path's are recognized", function() { ok( isGlobalPath('App.myProperty') ); ok( isGlobalPath('App.myProperty.subProperty') ); }); test("if there is a 'this' in the path, it's not a global path", function() { ok( !isGlobalPath('this.myProperty') ); ok( !isGlobalPath('this') ); }); test("if the path starts with a lowercase character, it is not a global path", function() { ok( !isGlobalPath('myObj') ); ok( !isGlobalPath('myObj.SecondProperty') ); }); }); define("ember-metal/tests/accessors/isGlobalPath_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/isGlobalPath_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/isGlobalPath_test.js should pass jshint.'); }); }); define("ember-metal/tests/accessors/normalizeTuple_test", ["ember-metal/property_get"], function(__dependency1__) { "use strict"; /*globals Foo:true, $foo:true */ var normalizeTuple = __dependency1__.normalizeTuple; var obj, 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 // test('[obj, foo] -> [obj, foo]', function() { deepEqual(normalizeTuple(obj, 'foo'), [obj, 'foo']); }); test('[obj, *] -> [obj, *]', function() { deepEqual(normalizeTuple(obj, '*'), [obj, '*']); }); test('[obj, foo.bar] -> [obj, foo.bar]', function() { deepEqual(normalizeTuple(obj, 'foo.bar'), [obj, 'foo.bar']); }); test('[obj, foo.*] -> [obj, foo.*]', function() { deepEqual(normalizeTuple(obj, 'foo.*'), [obj, 'foo.*']); }); test('[obj, foo.*.baz] -> [obj, foo.*.baz]', function() { deepEqual(normalizeTuple(obj, 'foo.*.baz'), [obj, 'foo.*.baz']); }); test('[obj, this.foo] -> [obj, foo]', function() { deepEqual(normalizeTuple(obj, 'this.foo'), [obj, 'foo']); }); test('[obj, this.foo.bar] -> [obj, foo.bar]', function() { deepEqual(normalizeTuple(obj, 'this.foo.bar'), [obj, 'foo.bar']); }); test('[obj, this.Foo.bar] -> [obj, Foo.bar]', function() { deepEqual(normalizeTuple(obj, 'this.Foo.bar'), [obj, 'Foo.bar']); }); // .......................................................... // GLOBAL PATHS // test('[obj, Foo] -> [obj, Foo]', function() { deepEqual(normalizeTuple(obj, 'Foo'), [obj, 'Foo']); }); test('[obj, Foo.bar] -> [Foo, bar]', function() { deepEqual(normalizeTuple(obj, 'Foo.bar'), [Foo, 'bar']); }); test('[obj, $foo.bar.baz] -> [$foo, bar.baz]', function() { deepEqual(normalizeTuple(obj, '$foo.bar.baz'), [$foo, 'bar.baz']); }); // .......................................................... // NO TARGET // test('[null, Foo] -> EXCEPTION', function() { raises(function() { normalizeTuple(null, 'Foo'); }, Error); }); test('[null, Foo.bar] -> [Foo, bar]', function() { deepEqual(normalizeTuple(null, 'Foo.bar'), [Foo, 'bar']); }); }); define("ember-metal/tests/accessors/normalizeTuple_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/normalizeTuple_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/normalizeTuple_test.js should pass jshint.'); }); }); define("ember-metal/tests/accessors/setPath_test", ["ember-metal/property_set","ember-metal/property_get"], function(__dependency1__, __dependency2__) { "use strict"; var set = __dependency1__.set; var trySet = __dependency1__.trySet; var get = __dependency2__.get; var originalLookup = Ember.lookup; var obj, moduleOpts = { setup: function() { obj = { foo: { bar: { baz: { biff: 'BIFF' } } } }; Ember.lookup = { Foo: { bar: { baz: { biff: 'FooBiff' } } }, $foo: { bar: { baz: { biff: '$FOOBIFF' } } } }; }, teardown: function() { obj = null; Ember.lookup = originalLookup; } }; QUnit.module('set with path', moduleOpts); test('[Foo, bar] -> Foo.bar', function() { Ember.lookup.Foo = {toString: function() { return 'Foo'; }}; // Behave like an Ember.Namespace set(Ember.lookup.Foo, 'bar', 'baz'); equal(get(Ember.lookup.Foo, 'bar'), 'baz'); }); // .......................................................... // // LOCAL PATHS test('[obj, foo] -> obj.foo', function() { set(obj, 'foo', "BAM"); equal(get(obj, 'foo'), "BAM"); }); test('[obj, foo.bar] -> obj.foo.bar', function() { set(obj, 'foo.bar', "BAM"); equal(get(obj, 'foo.bar'), "BAM"); }); test('[obj, this.foo] -> obj.foo', function() { set(obj, 'this.foo', "BAM"); equal(get(obj, 'foo'), "BAM"); }); test('[obj, this.foo.bar] -> obj.foo.bar', function() { set(obj, 'this.foo.bar', "BAM"); equal(get(obj, 'foo.bar'), "BAM"); }); // .......................................................... // NO TARGET // test('[null, Foo.bar] -> Foo.bar', function() { set(null, 'Foo.bar', "BAM"); equal(get(Ember.lookup.Foo, 'bar'), "BAM"); }); // .......................................................... // DEPRECATED // QUnit.module("set with path - deprecated", { setup: function() { moduleOpts.setup(); }, teardown: function() { moduleOpts.teardown(); } }); 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 { set(null, 'bla', "BAM"); } catch(ex) { equal(ex.message, exceptionMessage); } }); 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 { set(obj, 'bla.bla', "BAM"); } catch(ex) { equal(ex.message, exceptionMessage); } }); test('[obj, foo.baz.bat] -> EXCEPTION', function() { raises(function() { set(obj, 'foo.baz.bat', "BAM"); }, Error); }); test('[obj, foo.baz.bat] -> EXCEPTION', function() { trySet(obj, 'foo.baz.bat', "BAM"); ok(true, "does not raise"); }); }); define("ember-metal/tests/accessors/setPath_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/accessors'); test('ember-metal/tests/accessors/setPath_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/accessors/setPath_test.js should pass jshint.'); }); }); define("ember-metal/tests/accessors/set_test", ["ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; QUnit.module('set'); 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(set(newObj, key, obj[key]), obj[key], 'should return value'); equal(get(newObj, key), obj[key], 'should set value'); } }); 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(set(obj, 'foo', "BAR"), 'BAR', 'should return set value'); equal(obj.count, 1, 'should have invoked'); }); }); define("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.'); }); }); define("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","ember-metal/mixin"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var alias = __dependency1__.alias; var Descriptor = __dependency2__.Descriptor; var defineProperty = __dependency2__.defineProperty; var get = __dependency3__.get; var set = __dependency4__.set; var meta = __dependency5__.meta; var isWatching = __dependency6__.isWatching; var addObserver = __dependency7__.addObserver; var removeObserver = __dependency7__.removeObserver; var mixin = __dependency8__.mixin; var observer = __dependency8__.observer; var obj, count; QUnit.module('ember-metal/alias', { setup: function() { obj = { foo: { faz: 'FOO' } }; count = 0; }, teardown: function() { obj = null; } }); function incrementCount() { count++; } test('should proxy get to alt key', function() { defineProperty(obj, 'bar', alias('foo.faz')); equal(get(obj, 'bar'), 'FOO'); }); test('should proxy set to alt key', function() { defineProperty(obj, 'bar', alias('foo.faz')); set(obj, 'bar', 'BAR'); equal(get(obj, 'foo.faz'), 'BAR'); }); test('basic lifecycle', function() { defineProperty(obj, 'bar', alias('foo.faz')); var m = meta(obj); addObserver(obj, 'bar', incrementCount); equal(m.deps['foo.faz'].bar, 1); removeObserver(obj, 'bar', incrementCount); equal(m.deps['foo.faz'].bar, 0); }); test('begins watching alt key as soon as alias is watched', function() { defineProperty(obj, 'bar', alias('foo.faz')); addObserver(obj, 'bar', incrementCount); ok(isWatching(obj, 'foo.faz')); set(obj, 'foo.faz', 'BAR'); equal(count, 1); }); test('immediately sets up dependencies if already being watched', function() { addObserver(obj, 'bar', incrementCount); defineProperty(obj, 'bar', alias('foo.faz')); ok(isWatching(obj, 'foo.faz')); set(obj, 'foo.faz', 'BAR'); equal(count, 1); }); }); define("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.'); }); }); define("ember-metal/tests/binding/connect_test", ["ember-metal/core","ember-metal/tests/props_helper","ember-metal/binding","ember-metal/run_loop","ember-metal/platform","ember-metal/property_set","ember-metal/property_get","ember-metal/watching"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var Ember = __dependency1__["default"]; var testBoth = __dependency2__["default"]; var Binding = __dependency3__.Binding; var bind = __dependency3__.bind; var run = __dependency4__["default"]; var create = __dependency5__.create; var set = __dependency6__.set; var get = __dependency7__.get; var rewatch = __dependency8__.rewatch; function performTest(binding, a, b, get, set, connect) { if (connect === undefined) connect = function() {binding.connect(a);}; ok(!run.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(function () { set(b, 'bar', 'BAZZ'); }); equal(get(a, 'foo'), 'BAZZ', 'a should have changed'); run(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.lookup; Ember.lookup = lookup = {}; }, teardown: function(){ lookup = null; Ember.lookup = originalLookup; } }); testBoth('Connecting a binding between two properties', function(get, set) { var a = { foo: 'FOO', bar: 'BAR' }; // a.bar -> a.foo var binding = new Binding('foo', 'bar'); performTest(binding, a, a, get, set); }); 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 Binding('foo', 'b.bar'); performTest(binding, a, b, get, set); }); 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 Binding('foo', 'GlobalB.b.bar'); performTest(binding, a, b, get, set); // make sure modifications update b = { bar: 'BIFF' }; run(function() { set(GlobalB, 'b', b); }); equal(get(a, 'foo'), 'BIFF', 'a should have changed'); }); 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 Binding('foo', 'b.bar'); performTest(binding, a, b, get, set, function () { binding.connect(a); binding.connect(a); }); }); testBoth('Bindings should be inherited', function(get, set) { var a = { foo: 'FOO', b: { bar: 'BAR' } }; var binding = new Binding('foo', 'b.bar'); var a2; run(function () { binding.connect(a); a2 = create(a); rewatch(a2); }); equal(get(a2, 'foo'), "BAR", "Should have synced binding on child"); equal(get(a, 'foo'), "BAR", "Should NOT have synced binding on parent"); run(function () { set(a2, 'b', { bar: 'BAZZ' }); }); equal(get(a2, 'foo'), "BAZZ", "Should have synced binding on child"); equal(get(a, 'foo'), "BAR", "Should NOT have synced binding on parent"); }); test('inherited bindings should sync on create', function() { var a; run(function () { var A = function() { bind(this, 'foo', 'bar.baz'); }; a = new A(); set(a, 'bar', { baz: 'BAZ' }); }); equal(get(a, 'foo'), 'BAZ', 'should have synced binding on new obj'); }); }); define("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.'); }); }); define("ember-metal/tests/binding/oneWay_test", ["ember-metal/property_set","ember-metal/property_get","ember-metal/run_loop","ember-metal/binding"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var set = __dependency1__.set; var get = __dependency2__.get; var run = __dependency3__["default"]; var oneWay = __dependency4__.oneWay; var MyApp; QUnit.module('system/mixin/binding/oneWay_test', { setup: function() { MyApp = { foo: { value: 'FOO' }, bar: { value: 'BAR' } }; }, teardown: function() { MyApp = null; } }); test('oneWay(true) should only sync one way', function() { var binding; run(function() { binding = oneWay(MyApp, 'bar.value', 'foo.value'); }); equal(get(MyApp, 'foo.value'), 'FOO', 'foo synced'); equal(get(MyApp, 'bar.value'), 'FOO', 'bar synced'); run(function() { set(MyApp, 'bar.value', 'BAZ'); }); equal(get(MyApp, 'foo.value'), 'FOO', 'foo synced'); equal(get(MyApp, 'bar.value'), 'BAZ', 'bar not synced'); run(function() { set(MyApp, 'foo.value', 'BIFF'); }); equal(get(MyApp, 'foo.value'), 'BIFF', 'foo synced'); equal(get(MyApp, 'bar.value'), 'BIFF', 'foo synced'); }); }); define("ember-metal/tests/binding/oneWay_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/binding'); test('ember-metal/tests/binding/oneWay_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/binding/oneWay_test.js should pass jshint.'); }); }); define("ember-metal/tests/binding/sync_test", ["ember-metal/tests/props_helper","ember-metal/run_loop","ember-metal/observer","ember-metal/platform","ember-metal/binding","ember-metal/watching","ember-metal/computed","ember-metal/properties"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var testBoth = __dependency1__["default"]; var run = __dependency2__["default"]; var addObserver = __dependency3__.addObserver; var removeObserver = __dependency3__.removeObserver; var _suspendObserver = __dependency3__._suspendObserver; var create = __dependency4__.create; var bind = __dependency5__.bind; var rewatch = __dependency6__.rewatch; var computed = __dependency7__.computed; var defineProperty = __dependency8__.defineProperty; QUnit.module("system/binding/sync_test.js"); testBoth("bindings should not sync twice in a single run loop", function(get, set) { var a, b, setValue, setCalled=0, getCalled=0; run(function() { a = {}; defineProperty(a, 'foo', computed(function(key, value) { if (arguments.length === 2) { setCalled++; setValue = value; return value; } else { getCalled++; return setValue; } })["volatile"]()); b = { a: a }; bind(b, 'foo', 'a.foo'); }); // reset after initial binding synchronization getCalled = 0; run(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"); }); testBoth("bindings should not infinite loop if computed properties return objects", function(get, set) { var a, b, getCalled=0; run(function() { a = {}; defineProperty(a, 'foo', computed(function() { getCalled++; if (getCalled > 1000) { throw 'infinite loop detected'; } return ['foo', 'bar']; })); b = { a: a }; bind(b, 'foo', 'a.foo'); }); deepEqual(get(b, 'foo'), ['foo', 'bar'], "the binding should sync"); equal(getCalled, 1, "Get should only be called once"); }); testBoth("bindings should do the right thing when observers trigger bindings in the opposite direction", function(get, set) { var a, b, c; run(function() { a = { foo: 'trololol' }; b = { a: a }; bind(b, 'foo', 'a.foo'); c = { a: a }; bind(c, 'foo', 'a.foo'); }); addObserver(b, 'foo', function() { set(c, 'foo', "what is going on"); }); run(function() { set(a, 'foo', 'trollface'); }); equal(get(a, 'foo'), "what is going on"); }); testBoth("bindings should do the right thing when binding is in prototype", function(get, set) { var obj, proto, a, b, selectionChanged; run(function() { obj = { selection: null }; selectionChanged = 0; addObserver(obj, 'selection', function () { selectionChanged++; }); proto = { obj: obj, changeSelection: function (value) { set(this, 'selection', value); } }; bind(proto, 'selection', 'obj.selection'); a = create(proto); b = create(proto); rewatch(a); rewatch(b); }); run(function () { set(a, 'selection', 'a'); }); run(function () { set(b, 'selection', 'b'); }); run(function () { set(a, 'selection', 'a'); }); equal(selectionChanged, 3); equal(get(obj, 'selection'), 'a'); }); testBoth("bindings should not try to sync destroyed objects", function(get, set) { var a, b; run(function() { a = { foo: 'trololol' }; b = { a: a }; bind(b, 'foo', 'a.foo'); }); run(function() { set(a, 'foo', 'trollface'); set(b, 'isDestroyed', true); ok(true, "should not raise"); }); run(function() { a = { foo: 'trololol' }; b = { a: a }; bind(b, 'foo', 'a.foo'); }); run(function() { set(b, 'foo', 'trollface'); set(a, 'isDestroyed', true); ok(true, "should not raise"); }); }); }); define("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.'); }); }); define("ember-metal/tests/chains_test", ["ember-metal/utils","ember-metal/observer","ember-metal/chains","ember-metal/platform"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var META_KEY = __dependency1__.META_KEY; var addObserver = __dependency2__.addObserver; var finishChains = __dependency3__.finishChains; var create = __dependency4__.create; QUnit.module("Chains"); test("finishChains should properly copy chains from prototypes to instances", function() { function didChange() {} var obj = {}; addObserver(obj, 'foo.bar', null, didChange); var childObj = create(obj); finishChains(childObj); ok(obj[META_KEY].chains !== childObj[META_KEY].chains, "The chains object is copied"); }); }); define("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.'); }); }); define("ember-metal/tests/computed_test", ["ember-metal/core","ember-metal/tests/props_helper","ember-metal/platform","ember-metal/computed","ember-metal/properties","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/watching","ember-metal/observer","ember-metal/enumerable_utils"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; var Ember = __dependency1__["default"]; var testBoth = __dependency2__["default"]; var create = __dependency3__.create; var ComputedProperty = __dependency4__.ComputedProperty; var computed = __dependency4__.computed; var cacheFor = __dependency4__.cacheFor; var Descriptor = __dependency5__.Descriptor; var defineProperty = __dependency5__.defineProperty; var get = __dependency6__.get; var set = __dependency7__.set; var meta = __dependency8__.meta; var isWatching = __dependency9__.isWatching; var addObserver = __dependency10__.addObserver; var addBeforeObserver = __dependency10__.addBeforeObserver; var indexOf = __dependency11__.indexOf; var originalLookup = Ember.lookup, lookup; var obj, count, Global; QUnit.module('computed'); test('computed property should be an instance of descriptor', function() { ok(computed(function() {}) instanceof Descriptor); }); test('defining computed property should invoke property on get', function() { var obj = {}; var count = 0; defineProperty(obj, 'foo', computed(function(key) { count++; return 'computed '+key; })); equal(get(obj, 'foo'), 'computed foo', 'should return value'); equal(count, 1, 'should have invoked computed property'); }); test('defining computed property should invoke property on set', function() { var obj = {}; var count = 0; defineProperty(obj, 'foo', computed(function(key, value) { if (value !== undefined) { count++; this['__'+key] = 'computed '+value; } return this['__'+key]; })); equal(set(obj, 'foo', 'bar'), 'bar', 'should return set value'); equal(count, 1, 'should have invoked computed property'); equal(get(obj, 'foo'), 'computed bar', 'should return new value'); }); var objA, objB; QUnit.module('computed should inherit through prototype', { setup: function() { objA = { __foo: 'FOO' } ; defineProperty(objA, 'foo', computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'computed '+value; } return this['__'+key]; })); objB = create(objA); objB.__foo = 'FOO'; // make a copy; }, teardown: function() { objA = objB = null; } }); 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' } ; defineProperty(objA, 'foo', computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'computed '+value; } return this['__'+key]; })); objB = create(objA); defineProperty(objB, 'foo'); // make this just a normal property. }, teardown: function() { objA = objB = null; } }); 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' } ; defineProperty(objA, 'foo', computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'A '+value; } return this['__'+key]; })); objB = create(objA); objB.__foo = 'FOO'; defineProperty(objB, 'foo', computed(function(key, value) { if (value !== undefined) { this['__'+key] = 'B '+value; } return this['__'+key]; })); }, teardown: function() { objA = objB = null; } }); 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'); test("can set metadata on a computed property", function() { var computedProperty = computed(function() { }); computedProperty.meta({ key: 'keyValue' }); equal(computedProperty.meta().key, 'keyValue', "saves passed meta hash to the _meta property"); }); test("meta should return an empty hash if no meta is set", function() { var computedProperty = computed(function() { }); deepEqual(computedProperty.meta(), {}, "returned value is an empty hash"); }); // .......................................................... // CACHEABLE // QUnit.module('computed - cacheable', { setup: function() { obj = {}; count = 0; defineProperty(obj, 'foo', computed(function(key, value) { count++; return 'bar '+count; })); }, teardown: function() { obj = count = null; } }); 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'); }); 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'); }); testBoth('inherited property should not pick up cache', function(get, set) { var objB = create(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'); }); testBoth('cacheFor should return the cached value', function(get, set) { equal(cacheFor(obj, 'foo'), undefined, "should not yet be a cached value"); get(obj, 'foo'); equal(cacheFor(obj, 'foo'), "bar 1", "should retrieve cached value"); }); testBoth('cacheFor should return falsy cached values', function(get, set) { defineProperty(obj, 'falsy', computed(function() { return false; })); equal(cacheFor(obj, 'falsy'), undefined, "should not yet be a cached value"); get(obj, 'falsy'); equal(cacheFor(obj, 'falsy'), false, "should retrieve cached value"); }); testBoth("setting a cached computed property passes the old value as the third argument", function(get, set) { var obj = { foo: 0 }; var receivedOldValue; defineProperty(obj, 'plusOne', 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"); }); testBoth("the old value is only passed in if the computed property specifies three arguments", function(get, set) { var obj = { foo: 0 }; var receivedOldValue; defineProperty(obj, 'plusOne', 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; defineProperty(obj, 'foo', computed(function(key, value) { count++; get(this, 'bar'); return 'bar '+count; }).property('bar')); }, teardown: function() { obj = count = null; } }); testBoth('should lazily watch dependent keys on set', function (get, set) { equal(isWatching(obj, 'bar'), false, 'precond not watching dependent key'); set(obj, 'foo', 'bar'); equal(isWatching(obj, 'bar'), true, 'lazily watching dependent key'); }); testBoth('should lazily watch dependent keys on get', function (get, set) { equal(isWatching(obj, 'bar'), false, 'precond not watching dependent key'); get(obj, 'foo'); equal(isWatching(obj, 'bar'), true, 'lazily watching dependent key'); }); testBoth('local dependent key should invalidate cache', function(get, set) { equal(isWatching(obj, 'bar'), false, 'precond not watching dependent key'); equal(get(obj, 'foo'), 'bar 1', 'get once'); equal(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'); }); testBoth('should invalidate multiple nested dependent keys', function(get, set) { var count = 0; defineProperty(obj, 'bar', computed(function() { count++; get(this, 'baz'); return 'baz '+count; }).property('baz')); equal(isWatching(obj, 'bar'), false, 'precond not watching dependent key'); equal(isWatching(obj, 'baz'), false, 'precond not watching dependent key'); equal(get(obj, 'foo'), 'bar 1', 'get once'); equal(isWatching(obj, 'bar'), true, 'lazily setup watching dependent key'); equal(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(isWatching(obj, 'bar'), false, 'should not be watching dependent key after cache cleared'); equal(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(isWatching(obj, 'bar'), true, 'lazily setup watching dependent key'); equal(isWatching(obj, 'baz'), true, 'lazily setup watching dependent key'); }); testBoth('circular keys should not blow up', function(get, set) { defineProperty(obj, 'bar', computed(function(key, value) { count++; return 'bar '+count; }).property('foo')); defineProperty(obj, 'foo', 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'); }); testBoth('redefining a property should undo old depenent keys', function(get ,set) { equal(isWatching(obj, 'bar'), false, 'precond not watching dependent key'); equal(get(obj, 'foo'), 'bar 1'); equal(isWatching(obj, 'bar'), true, 'lazily watching dependent key'); defineProperty(obj, 'foo', computed(function() { count++; return 'baz '+count; }).property('baz')); equal(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'); }); testBoth('can watch multiple dependent keys specified declaratively via brace expansion', function (get, set) { defineProperty(obj, 'foo', 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"); }); testBoth('throws assertion if brace expansion notation has spaces', function (get, set) { throws(function () { defineProperty(obj, 'roo', computed(function (key, value) { count++; return 'roo ' + count; }).property('fee.{bar, baz,bop , }')); }, /cannot contain spaces/); }); // .......................................................... // CHAINED DEPENDENT KEYS // var func, moduleOpts = { setup: function() { originalLookup = Ember.lookup; lookup = Ember.lookup = {}; obj = { foo: { bar: { baz: { biff: "BIFF" } } } }; Global = { foo: { bar: { baz: { biff: "BIFF" } } } }; lookup['Global'] = Global; count = 0; func = function() { count++; return get(obj, 'foo.bar.baz.biff')+' '+count; }; }, teardown: function() { obj = count = func = Global = null; Ember.lookup = originalLookup; } }; QUnit.module('computed - dependentkey with chained properties', moduleOpts); testBoth('depending on simple chain', function(get, set) { // assign computed property defineProperty(obj, 'prop', 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'); 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'); }); testBoth('depending on Global chain', function(get, set) { // assign computed property defineProperty(obj, 'prop', 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'); 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'); }); testBoth('chained dependent keys should evaluate computed properties lazily', function(get,set) { defineProperty(obj.foo.bar, 'b', computed(func)); defineProperty(obj.foo, 'c', computed(function() {}).property('bar.b')); equal(count, 0, 'b should not run'); }); // .......................................................... // BUGS // QUnit.module('computed edge cases'); test('adding a computed property should show up in key iteration',function() { var obj = {}; defineProperty(obj, 'foo', computed(function() {})); var found = []; for(var key in obj) found.push(key); ok(indexOf(found, 'foo')>=0, 'should find computed property in iteration found=' + found); ok('foo' in obj, 'foo in obj should pass'); }); testBoth("when setting a value after it had been retrieved empty don't pass function UNDEFINED as oldValue", function(get, set) { var obj = {}, oldValueIsNoFunction = true; defineProperty(obj, 'foo', 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'); testBoth('setting a watched computed property', function(get, set) { var obj = { firstName: 'Yehuda', lastName: 'Katz' }; defineProperty(obj, 'fullName', 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, fullNameDidChange = 0, firstNameWillChange = 0, firstNameDidChange = 0, lastNameWillChange = 0, lastNameDidChange = 0; addBeforeObserver(obj, 'fullName', function () { fullNameWillChange++; }); addObserver(obj, 'fullName', function () { fullNameDidChange++; }); addBeforeObserver(obj, 'firstName', function () { firstNameWillChange++; }); addObserver(obj, 'firstName', function () { firstNameDidChange++; }); addBeforeObserver(obj, 'lastName', function () { lastNameWillChange++; }); 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); }); testBoth('setting a cached computed property that modifies the value you give it', function(get, set) { var obj = { foo: 0 }; defineProperty(obj, 'plusOne', 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, plusOneDidChange = 0; addBeforeObserver(obj, 'plusOne', function () { plusOneWillChange++; }); 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'); testBoth("when setting a value on a computed property that doesn't handle sets", function(get, set) { var obj = {}, observerFired = false; defineProperty(obj, 'foo', computed(function() { return 'foo'; })); addObserver(obj, 'foo', null, function() { observerFired = true; }); set(obj, 'foo', 'bar'); equal(get(obj, 'foo'), 'bar', 'The set value is properly returned'); ok(!meta(obj).descs.foo, 'The computed property was removed'); ok(observerFired, 'The observer was still notified'); }); QUnit.module('computed - readOnly'); test('is chainable', function() { var cp = computed(function() {}).readOnly(); ok(cp instanceof Descriptor); ok(cp instanceof ComputedProperty); }); testBoth('protects against setting', function(get, set) { var obj = { }; defineProperty(obj, 'bar', computed(function(key) { return 'barValue'; }).readOnly()); equal(get(obj, 'bar'), 'barValue'); raises(function() { set(obj, 'bar', 'newBar'); }, /Cannot set read\-only property "bar" on object:/ ); equal(get(obj, 'bar'), 'barValue'); }); QUnit.module('CP macros'); testBoth('computed.not', function(get, set) { var obj = {foo: true}; defineProperty(obj, 'notFoo', computed.not('foo')); equal(get(obj, 'notFoo'), false); obj = {foo: {bar: true}}; defineProperty(obj, 'notFoo', computed.not('foo.bar')); equal(get(obj, 'notFoo'), false); }); testBoth('computed.empty', function(get, set) { var obj = {foo: [], bar: undefined, baz: null, quz: ''}; defineProperty(obj, 'fooEmpty', computed.empty('foo')); defineProperty(obj, 'barEmpty', computed.empty('bar')); defineProperty(obj, 'bazEmpty', computed.empty('baz')); defineProperty(obj, 'quzEmpty', computed.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); }); testBoth('computed.bool', function(get, set) { var obj = {foo: function() {}, bar: 'asdf', baz: null, quz: false}; defineProperty(obj, 'fooBool', computed.bool('foo')); defineProperty(obj, 'barBool', computed.bool('bar')); defineProperty(obj, 'bazBool', computed.bool('baz')); defineProperty(obj, 'quzBool', computed.bool('quz')); equal(get(obj, 'fooBool'), true); equal(get(obj, 'barBool'), true); equal(get(obj, 'bazBool'), false); equal(get(obj, 'quzBool'), false); }); testBoth('computed.alias', function(get, set) { var obj = { bar: 'asdf', baz: null, quz: false}; defineProperty(obj, 'bay', computed(function(key) { return 'apple'; })); defineProperty(obj, 'barAlias', computed.alias('bar')); defineProperty(obj, 'bazAlias', computed.alias('baz')); defineProperty(obj, 'quzAlias', computed.alias('quz')); defineProperty(obj, 'bayAlias', computed.alias('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); }); testBoth('computed.alias set', function(get, set) { var obj = {}; var constantValue = 'always `a`'; defineProperty(obj, 'original', computed(function(key, value) { return constantValue; })); defineProperty(obj, 'aliased', computed.alias('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); }); testBoth('computed.defaultTo', function(get, set) { var obj = { source: 'original source value' }; defineProperty(obj, 'copy', computed.defaultTo('source')); 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'); }); testBoth('computed.match', function(get, set) { var obj = { name: 'Paul' }; defineProperty(obj, 'isPaul', computed.match('name', /Paul/)); equal(get(obj, 'isPaul'), true, 'is Paul'); set(obj, 'name', 'Pierre'); equal(get(obj, 'isPaul'), false, 'is not Paul anymore'); }); testBoth('computed.notEmpty', function(get, set) { var obj = { items: [1] }; defineProperty(obj, 'hasItems', computed.notEmpty('items')); equal(get(obj, 'hasItems'), true, 'is not empty'); set(obj, 'items', []); equal(get(obj, 'hasItems'), false, 'is empty'); }); testBoth('computed.equal', function(get, set) { var obj = { name: 'Paul' }; defineProperty(obj, 'isPaul', computed.equal('name', 'Paul')); equal(get(obj, 'isPaul'), true, 'is Paul'); set(obj, 'name', 'Pierre'); equal(get(obj, 'isPaul'), false, 'is not Paul anymore'); }); testBoth('computed.gt', function(get, set) { var obj = { number: 2 }; defineProperty(obj, 'isGreaterThenOne', computed.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'); }); testBoth('computed.gte', function(get, set) { var obj = { number: 2 }; defineProperty(obj, 'isGreaterOrEqualThenOne', computed.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'); }); testBoth('computed.lt', function(get, set) { var obj = { number: 0 }; defineProperty(obj, 'isLesserThenOne', computed.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'); }); testBoth('computed.lte', function(get, set) { var obj = { number: 0 }; defineProperty(obj, 'isLesserOrEqualThenOne', computed.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'); }); testBoth('computed.and', function(get, set) { var obj = { one: true, two: true }; defineProperty(obj, 'oneAndTwo', computed.and('one', 'two')); equal(get(obj, 'oneAndTwo'), true, 'one and two'); set(obj, 'one', false); equal(get(obj, 'oneAndTwo'), false, 'one and not two'); }); testBoth('computed.or', function(get, set) { var obj = { one: true, two: true }; defineProperty(obj, 'oneOrTwo', computed.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, 'one', true); equal(get(obj, 'oneOrTwo'), true, 'one or two'); }); testBoth('computed.any', function(get, set) { var obj = { one: 'foo', two: 'bar' }; defineProperty(obj, 'anyOf', computed.any('one', 'two')); equal(get(obj, 'anyOf'), 'foo', 'is foo'); set(obj, 'one', false); equal(get(obj, 'anyOf'), 'bar', 'is bar'); }); testBoth('computed.collect', function(get, set) { var obj = { one: 'foo', two: 'bar', three: null }; defineProperty(obj, 'all', computed.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'); }); function oneWayTest(methodName) { return function(get, set) { var obj = { firstName: 'Teddy', lastName: 'Zeenny' }; defineProperty(obj, 'nickName', computed[methodName]('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'); }; } testBoth('computed.oneWay', oneWayTest('oneWay')); testBoth('computed.reads', oneWayTest('reads')); testBoth('computed.readOnly', function(get, set) { var obj = { firstName: 'Teddy', lastName: 'Zeenny' }; defineProperty(obj, 'nickName', computed.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'); }); testBoth('computed.deprecatingAlias', function(get, set) { var obj = { bar: 'asdf', baz: null, quz: false}; defineProperty(obj, 'bay', computed(function(key) { return 'apple'; })); defineProperty(obj, 'barAlias', computed.deprecatingAlias('bar')); defineProperty(obj, 'bazAlias', computed.deprecatingAlias('baz')); defineProperty(obj, 'quzAlias', computed.deprecatingAlias('quz')); defineProperty(obj, 'bayAlias', computed.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); }); }); define("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.'); }); }); define("ember-metal/tests/core/inspect_test", ["ember-metal/utils","ember-metal/core"], function(__dependency1__, __dependency2__) { "use strict"; var inspect = __dependency1__.inspect; var Ember = __dependency2__["default"]; QUnit.module("Ember.inspect"); test("strings", function() { equal(inspect("foo"), "foo"); }); test("numbers", function() { equal(inspect(2.6), "2.6"); }); test("null", function() { equal(inspect(null), "null"); }); test("undefined", function() { equal(inspect(undefined), "undefined"); }); test("true", function() { equal(inspect(true), "true"); }); test("false", function() { equal(inspect(false), "false"); }); test("object", function() { equal(inspect({}), "{}"); equal(inspect({ foo: 'bar' }), "{foo: bar}"); equal(inspect({ foo: Ember.K }), "{foo: function() { ... }}"); }); test("array", function() { equal(inspect([1,2,3]), "[1,2,3]"); }); test("regexp", function() { equal(inspect(/regexp/), "/regexp/"); }); test("date", function() { var inspected = 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"); }); }); define("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.'); }); }); define("ember-metal/tests/enumerable_utils_test", ["ember-metal/enumerable_utils"], function(__dependency1__) { "use strict"; var EnumerableUtils = __dependency1__["default"]; QUnit.module('Ember.EnumerableUtils.intersection'); test('returns an array of objects that appear in both enumerables', function() { var a = [1,2,3], b = [2,3,4], result; result = EnumerableUtils.intersection(a, b); deepEqual(result, [2,3]); }); test("large replace", function() { expect(0); // https://code.google.com/p/chromium/issues/detail?id=56588 EnumerableUtils.replace([], 0, undefined, new Array(62401)); // max + 1 in Chrome 28.0.1500.71 EnumerableUtils.replace([], 0, undefined, new Array(65535)); // max + 1 in Safari 6.0.5 (8536.30.1) EnumerableUtils.replace([], 0, undefined, new Array(491519)); // max + 1 in FireFox 22.0 }); }); define("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.'); }); }); define("ember-metal/tests/error_test", [], function() { "use strict"; QUnit.module("Ember Error Throwing"); test("new Ember.Error displays provided message", function() { raises( function() { throw new Ember.Error('A Message'); }, function(e) { return e.message === 'A Message'; }, 'the assigned message was displayed' ); }); }); define("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.'); }); }); define("ember-metal/tests/events_test", ["ember-metal/mixin","ember-metal/platform","ember-metal/utils","ember-metal/events"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Mixin = __dependency1__.Mixin; var create = __dependency2__.create; var meta = __dependency3__.meta; var on = __dependency4__.on; var addListener = __dependency4__.addListener; var removeListener = __dependency4__.removeListener; var suspendListener = __dependency4__.suspendListener; var suspendListeners = __dependency4__.suspendListeners; var sendEvent = __dependency4__.sendEvent; var hasListeners = __dependency4__.hasListeners; var watchedEvents = __dependency4__.watchedEvents; var listenersFor = __dependency4__.listenersFor; var actionsDiff = __dependency4__.actionsDiff; var actionsUnion = __dependency4__.actionsUnion; QUnit.module('system/props/events_test'); test('listener should receive event - removing should remove', function() { var obj = {}, count = 0; var F = function() { count++; }; addListener(obj, 'event!', F); equal(count, 0, 'nothing yet'); sendEvent(obj, 'event!'); equal(count, 1, 'received event'); removeListener(obj, 'event!', F); count = 0; sendEvent(obj, 'event!'); equal(count, 0, 'received event'); }); test('listeners should be inherited', function() { var obj = {}, count = 0; var F = function() { count++; }; addListener(obj, 'event!', F); var obj2 = create(obj); equal(count, 0, 'nothing yet'); sendEvent(obj2, 'event!'); equal(count, 1, 'received event'); removeListener(obj2, 'event!', F); count = 0; sendEvent(obj2, 'event!'); equal(count, 0, 'did not receive event'); sendEvent(obj, 'event!'); equal(count, 1, 'should still invoke on parent'); }); test('adding a listener more than once should only invoke once', function() { var obj = {}, count = 0; var F = function() { count++; }; addListener(obj, 'event!', F); addListener(obj, 'event!', F); sendEvent(obj, 'event!'); equal(count, 1, 'should only invoke once'); }); test('adding a listener with a target should invoke with target', function() { var obj = {}, target; target = { count: 0, method: function() { this.count++; } }; addListener(obj, 'event!', target, target.method); sendEvent(obj, 'event!'); equal(target.count, 1, 'should invoke'); }); test('suspending a listener should not invoke during callback', function() { var obj = {}, target, otherTarget; target = { count: 0, method: function() { this.count++; } }; otherTarget = { count: 0, method: function() { this.count++; } }; addListener(obj, 'event!', target, target.method); addListener(obj, 'event!', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); sendEvent(obj, 'event!'); return 'result'; } sendEvent(obj, 'event!'); equal(suspendListener(obj, 'event!', target, target.method, callback), 'result'); sendEvent(obj, 'event!'); equal(target.count, 2, 'should invoke'); equal(otherTarget.count, 3, 'should invoke'); }); test('adding a listener with string method should lookup method on event delivery', function() { var obj = {}, target; target = { count: 0, method: function() {} }; addListener(obj, 'event!', target, 'method'); sendEvent(obj, 'event!'); equal(target.count, 0, 'should invoke but do nothing'); target.method = function() { this.count++; }; sendEvent(obj, 'event!'); equal(target.count, 1, 'should invoke now'); }); test('calling sendEvent with extra params should be passed to listeners', function() { var obj = {}, params = null; addListener(obj, 'event!', function() { params = Array.prototype.slice.call(arguments); }); sendEvent(obj, 'event!', ['foo', 'bar']); deepEqual(params, ['foo', 'bar'], 'params should be saved'); }); 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 }; addListener(obj, 'event!', obj, function() { this.count++; }); sendEvent(obj, 'event!', ['foo', 'bar']); equal(obj.count, 2, 'should have invoked method & listener'); }); test('hasListeners tells you if there are listeners for a given event', function() { var obj = {}, F = function() {}, F2 = function() {}; equal(hasListeners(obj, 'event!'), false, 'no listeners at first'); addListener(obj, 'event!', F); addListener(obj, 'event!', F2); equal(hasListeners(obj, 'event!'), true, 'has listeners'); removeListener(obj, 'event!', F); equal(hasListeners(obj, 'event!'), true, 'has listeners'); removeListener(obj, 'event!', F2); equal(hasListeners(obj, 'event!'), false, 'has no more listeners'); addListener(obj, 'event!', F); equal(hasListeners(obj, 'event!'), true, 'has listeners'); }); test('calling removeListener without method should remove all listeners', function() { var obj = {}, F = function() {}, F2 = function() {}; equal(hasListeners(obj, 'event!'), false, 'no listeners at first'); addListener(obj, 'event!', F); addListener(obj, 'event!', F2); equal(hasListeners(obj, 'event!'), true, 'has listeners'); removeListener(obj, 'event!'); equal(hasListeners(obj, 'event!'), false, 'has no more listeners'); }); test('while suspended, it should not be possible to add a duplicate listener', function() { var obj = {}, target; target = { count: 0, method: function() { this.count++; } }; addListener(obj, 'event!', target, target.method); function callback() { addListener(obj, 'event!', target, target.method); } sendEvent(obj, 'event!'); suspendListener(obj, 'event!', target, target.method, callback); equal(target.count, 1, 'should invoke'); equal(meta(obj).listeners['event!'].length, 3, "a duplicate listener wasn't added"); // now test suspendListeners... sendEvent(obj, 'event!'); suspendListeners(obj, ['event!'], target, target.method, callback); equal(target.count, 2, 'should have invoked again'); equal(meta(obj).listeners['event!'].length, 3, "a duplicate listener wasn't added"); }); test('a listener can be added as part of a mixin', function() { var triggered = 0; var MyMixin = Mixin.create({ foo1: on('bar', function() { triggered++; }), foo2: on('bar', function() { triggered++; }) }); var obj = {}; MyMixin.apply(obj); sendEvent(obj, 'bar'); equal(triggered, 2, 'should invoke listeners'); }); test('a listener added as part of a mixin may be overridden', function() { var triggered = 0; var FirstMixin = Mixin.create({ foo: on('bar', function() { triggered++; }) }); var SecondMixin = Mixin.create({ foo: on('baz', function() { triggered++; }) }); var obj = {}; FirstMixin.apply(obj); SecondMixin.apply(obj); sendEvent(obj, 'bar'); equal(triggered, 0, 'should not invoke from overriden property'); sendEvent(obj, 'baz'); equal(triggered, 1, 'should invoke from subclass property'); }); }); define("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.'); }); }); define("ember-metal/tests/features_test", ["ember-metal/core"], function(__dependency1__) { "use strict"; var Ember = __dependency1__["default"]; var isEnabled = Ember.FEATURES.isEnabled, origFeatures, origEnableAll, origEnableOptional; QUnit.module("Ember.FEATURES.isEnabled", { setup: function(){ origFeatures = Ember.FEATURES; origEnableAll = Ember.ENV.ENABLE_ALL_FEATURES; origEnableOptional = Ember.ENV.ENABLE_OPTIONAL_FEATURES; }, teardown: function(){ Ember.FEATURES = origFeatures; Ember.ENV.ENABLE_ALL_FEATURES = origEnableAll; Ember.ENV.ENABLE_OPTIONAL_FEATURES = origEnableOptional; } }); test("ENV.ENABLE_ALL_FEATURES", function() { Ember.ENV.ENABLE_ALL_FEATURES = true; Ember.FEATURES['fred'] = false; Ember.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"); }); test("ENV.ENABLE_OPTIONAL_FEATURES", function() { Ember.ENV.ENABLE_OPTIONAL_FEATURES = true; Ember.FEATURES['fred'] = false; Ember.FEATURES['barney'] = true; Ember.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"); }); test("isEnabled without ENV options", function(){ Ember.ENV.ENABLE_ALL_FEATURES = false; Ember.ENV.ENABLE_OPTIONAL_FEATURES = false; Ember.FEATURES['fred'] = false; Ember.FEATURES['barney'] = true; Ember.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"); }); }); define("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.'); }); }); define("ember-metal/tests/instrumentation_test", ["ember-metal/instrumentation"], function(__dependency1__) { "use strict"; var instrument = __dependency1__.instrument; var subscribe = __dependency1__.subscribe; var unsubscribe = __dependency1__.unsubscribe; var reset = __dependency1__.reset; QUnit.module("Ember Instrumentation", { setup: function() { }, teardown: function() { reset(); } }); test("subscribing to a simple path receives the listener", function() { expect(12); var sentPayload = {}, count = 0; 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++; } }); instrument("render", sentPayload, function() { }); instrument("render.handlebars", sentPayload, function() { }); }); test("returning a value from the before callback passes it to the after callback", function() { expect(2); var passthru1 = {}, passthru2 = {}; subscribe("render", { before: function(name, timestamp, payload) { return passthru1; }, after: function(name, timestamp, payload, beforeValue) { strictEqual(beforeValue, passthru1); } }); subscribe("render", { before: function(name, timestamp, payload) { return passthru2; }, after: function(name, timestamp, payload, beforeValue) { strictEqual(beforeValue, passthru2); } }); instrument("render", null, function() {}); }); test("raising an exception in the instrumentation attaches it to the payload", function() { expect(2); var error = new Error("Instrumentation"); subscribe("render", { before: function() {}, after: function(name, timestamp, payload) { strictEqual(payload.exception, error); } }); subscribe("render", { before: function() {}, after: function(name, timestamp, payload) { strictEqual(payload.exception, error); } }); instrument("render.handlebars", null, function() { throw error; }); }); test("it is possible to add a new subscriber after the first instrument", function() { instrument("render.handlebars", null, function() {}); subscribe("render", { before: function() { ok(true, "Before callback was called"); }, after: function() { ok(true, "After callback was called"); } }); instrument("render.handlebars", null, function() {}); }); test("it is possible to remove a subscriber", function() { expect(4); var count = 0; var subscriber = 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++; } }); instrument("render.handlebars", null, function() {}); unsubscribe(subscriber); instrument("render.handlebars", null, function() {}); }); }); define("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.'); }); }); define("ember-metal/tests/is_blank_test", ["ember-metal/is_blank"], function(__dependency1__) { "use strict"; var isBlank = __dependency1__["default"]; QUnit.module("Ember.isBlank"); test("Ember.isBlank", function() { var string = "string", fn = function() {}, object = {length: 0}; equal(true, isBlank(null), "for null"); equal(true, isBlank(undefined), "for undefined"); equal(true, isBlank(""), "for an empty String"); equal(true, isBlank(" "), "for a whitespace String"); equal(true, isBlank("\n\t"), "for another whitespace String"); equal(false, isBlank("\n\t Hi"), "for a String with whitespaces"); equal(false, isBlank(true), "for true"); equal(false, isBlank(false), "for false"); equal(false, isBlank(string), "for a String"); equal(false, isBlank(fn), "for a Function"); equal(false, isBlank(0), "for 0"); equal(true, isBlank([]), "for an empty Array"); equal(false, isBlank({}), "for an empty Object"); equal(true, isBlank(object), "for an Object that has zero 'length'"); equal(false, isBlank([1,2,3]), "for a non-empty array"); }); }); define("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.'); }); }); define("ember-metal/tests/is_empty_test", ["ember-metal/is_empty"], function(__dependency1__) { "use strict"; var isEmpty = __dependency1__["default"]; QUnit.module("Ember.isEmpty"); test("Ember.isEmpty", function() { var string = "string", fn = function() {}, object = {length: 0}; equal(true, isEmpty(null), "for null"); equal(true, isEmpty(undefined), "for undefined"); equal(true, isEmpty(""), "for an empty String"); equal(false, isEmpty(true), "for true"); equal(false, isEmpty(false), "for false"); equal(false, isEmpty(string), "for a String"); equal(false, isEmpty(fn), "for a Function"); equal(false, isEmpty(0), "for 0"); equal(true, isEmpty([]), "for an empty Array"); equal(false, isEmpty({}), "for an empty Object"); equal(true, isEmpty(object), "for an Object that has zero 'length'"); }); }); define("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.'); }); }); define("ember-metal/tests/is_none_test", ["ember-metal/is_none"], function(__dependency1__) { "use strict"; var isNone = __dependency1__["default"]; QUnit.module("Ember.isNone"); test("Ember.isNone", function() { var string = "string", fn = function() {}; equal(true, isNone(null), "for null"); equal(true, isNone(undefined), "for undefined"); equal(false, isNone(""), "for an empty String"); equal(false, isNone(true), "for true"); equal(false, isNone(false), "for false"); equal(false, isNone(string), "for a String"); equal(false, isNone(fn), "for a Function"); equal(false, isNone(0), "for 0"); equal(false, isNone([]), "for an empty Array"); equal(false, isNone({}), "for an empty Object"); }); }); define("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.'); }); }); define("ember-metal/tests/libraries_test", ["ember-metal","ember-metal/libraries"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var libraries = __dependency2__["default"]; var libs = libraries; test('Ember registers itself', function() { equal(libs[0].name, "Ember"); }); test('core libraries come before other libraries', function() { var l = libs.length; libs.register("my-lib", "2.0.0a"); libs.registerCoreLibrary("DS", "1.0.0-beta.2"); equal(libs[l].name, "DS"); equal(libs[l+1].name, "my-lib"); libs.deRegister("my-lib"); libs.deRegister("DS"); }); test('only the first registration of a library is stored', function() { var l = libs.length; libs.register("magic", 1.23); libs.register("magic", 2.23); libs.register("magic", 3.23); equal(libs[l].name, "magic"); equal(libs[l].version, 1.23); equal(libs.length, l+1); libs.deRegister("magic"); }); test('libraries can be de-registered', function() { var l = libs.length; 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(libs[l].name, "lib2"); equal(libs.length, l+1); libs.deRegister("lib2"); }); }); define("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.'); }); }); define("ember-metal/tests/map_test", ["ember-metal/map"], function(__dependency1__) { "use strict"; var OrderedSet = __dependency1__.OrderedSet; var Map = __dependency1__.Map; var MapWithDefault = __dependency1__.MapWithDefault; var object, number, string, map; var varieties = [['Map', Map], ['MapWithDefault', MapWithDefault]], variety; 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); }; test("add", 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); equal(map.has({}), false); }); test("remove", function() { map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); map.remove(object); map.remove(number); map.remove(string); // doesn't explode map.remove({}); mapHasEntries([]); }); 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); }); test("copy and then remove", function() { map.set(object, "winning"); map.set(number, "winning"); map.set(string, "winning"); var map2 = map.copy(); map2.remove(object); map2.remove(number); map2.remove(string); mapHasEntries([ [ object, "winning" ], [ number, "winning" ], [ string, "winning" ] ]); mapHasEntries([ ], map2); }); test("length", function() { //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.remove('does not exist'); equal(map.length, 2); //Check copy var copy = map.copy(); equal(copy.length, 2); //Remove a key twice map.remove(number); equal(map.length, 1); map.remove(number); equal(map.length, 1); //Remove the last key map.remove(string); equal(map.length, 0); map.remove(string); equal(map.length, 0); }); } for (var i = 0; i < varieties.length; i++) { testMap(varieties[i]); } QUnit.module("MapWithDefault - default values"); test("Retrieving a value that has not been set returns and sets a default value", function() { var map = MapWithDefault.create({ defaultValue: function(key) { return [key]; } }); var value = map.get('ohai'); deepEqual(value, [ 'ohai' ]); strictEqual(value, map.get('ohai')); }); test("Copying a MapWithDefault copies the default value", function() { var 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']); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/alias_method_test", [], function() { "use strict"; QUnit.module('Ember.aliasMethod'); function validateAliasMethod(obj) { equal(obj.fooMethod(), 'FOO', 'obj.fooMethod()'); equal(obj.barMethod(), 'FOO', 'obj.barMethod should be a copy of foo'); } test('methods of another name are aliased when the mixin is applied', function() { var MyMixin = Ember.Mixin.create({ fooMethod: function() { return 'FOO'; }, barMethod: Ember.aliasMethod('fooMethod') }); var obj = MyMixin.apply({}); validateAliasMethod(obj); }); test('should follow aliasMethods all the way down', function() { var MyMixin = Ember.Mixin.create({ bar: Ember.aliasMethod('foo'), // put first to break ordered iteration baz: function() { return 'baz'; }, foo: Ember.aliasMethod('baz') }); var obj = MyMixin.apply({}); equal(Ember.get(obj, 'bar')(), 'baz', 'should have followed aliasMethods'); }); test('should alias methods from other dependent mixins', function() { var BaseMixin = Ember.Mixin.create({ fooMethod: function() { return 'FOO'; } }); var MyMixin = Ember.Mixin.create(BaseMixin, { barMethod: Ember.aliasMethod('fooMethod') }); var obj = MyMixin.apply({}); validateAliasMethod(obj); }); test('should alias methods from other mixins applied at same time', function() { var BaseMixin = Ember.Mixin.create({ fooMethod: function() { return 'FOO'; } }); var MyMixin = Ember.Mixin.create({ barMethod: Ember.aliasMethod('fooMethod') }); var obj = Ember.mixin({}, BaseMixin, MyMixin); validateAliasMethod(obj); }); test('should alias methods from mixins already applied on object', function() { var BaseMixin = Ember.Mixin.create({ quxMethod: function() { return 'qux'; } }); var MyMixin = Ember.Mixin.create({ bar: Ember.aliasMethod('foo'), barMethod: Ember.aliasMethod('fooMethod') }); var obj = { fooMethod: function() { return 'FOO'; } }; BaseMixin.apply(obj); MyMixin.apply(obj); validateAliasMethod(obj); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/apply_test", [], function() { "use strict"; /*globals raises */ QUnit.module('Ember.Mixin.apply'); function K() {} test('using apply() should apply properties', function() { var MixinA = Ember.Mixin.create({ foo: 'FOO', baz: K }); var obj = {}; Ember.mixin(obj, MixinA); equal(Ember.get(obj, 'foo'), "FOO", 'should apply foo'); equal(Ember.get(obj, 'baz'), K, 'should apply foo'); }); test('applying anonymous properties', function() { var obj = {}; Ember.mixin(obj, { foo: 'FOO', baz: K }); equal(Ember.get(obj, 'foo'), "FOO", 'should apply foo'); equal(Ember.get(obj, 'baz'), K, 'should apply foo'); }); test('applying null values', function() { expectAssertion(function() { Ember.mixin({}, null); }); }); test('applying a property with an undefined value', function() { var obj = { tagName: '' }; Ember.mixin(obj, { tagName: undefined }); strictEqual(Ember.get(obj, 'tagName'), ''); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/computed_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-metal/computed","ember-metal/properties"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.K var get = __dependency2__.get; var set = __dependency3__.set; var Mixin = __dependency4__.Mixin; var computed = __dependency5__.computed; var defineProperty = __dependency6__.defineProperty; QUnit.module('Mixin Computed Properties'); test('overriding computed properties', function() { var MixinA, MixinB, MixinC, MixinD; var obj; MixinA = Mixin.create({ aProp: computed(function() { return 'A'; }) }); MixinB = Mixin.create(MixinA, { aProp: computed(function() { return this._super()+'B'; }) }); MixinC = Mixin.create(MixinA, { aProp: computed(function() { return this._super()+'C'; }) }); MixinD = Mixin.create({ aProp: computed(function() { return this._super()+'D'; }) }); obj = {}; MixinB.apply(obj); equal(get(obj, 'aProp'), 'AB', "should expose super for B"); obj = {}; MixinC.apply(obj); equal(get(obj, 'aProp'), 'AC', "should expose super for C"); obj = {}; MixinA.apply(obj); MixinD.apply(obj); equal(get(obj, 'aProp'), 'AD', "should define super for D"); obj = { }; defineProperty(obj, 'aProp', computed(function(key, value) { return 'obj'; })); MixinD.apply(obj); equal(get(obj, 'aProp'), "objD", "should preserve original computed property"); }); test('calling set on overridden computed properties', function() { var SuperMixin, SubMixin; var obj; var superGetOccurred = false, superSetOccurred = false; SuperMixin = Mixin.create({ aProp: computed(function(key, val) { if (arguments.length === 1) { superGetOccurred = true; } else { superSetOccurred = true; } return true; }) }); SubMixin = Mixin.create(SuperMixin, { aProp: computed(function(key, val) { return this._super.apply(this, arguments); }) }); obj = {}; SubMixin.apply(obj); set(obj, 'aProp', 'set thyself'); ok(superSetOccurred, 'should pass set to _super'); superSetOccurred = false; // reset the set assertion obj = {}; SubMixin.apply(obj); get(obj, 'aProp'); ok(superGetOccurred, 'should pass get to _super'); set(obj, 'aProp', 'set thyself'); ok(superSetOccurred, 'should pass set to _super after getting'); }); test('setter behavior works properly when overriding computed properties', function() { var obj = {}; var MixinA = Mixin.create({ cpWithSetter2: computed(Ember.K), cpWithSetter3: computed(Ember.K), cpWithoutSetter: computed(Ember.K) }); var cpWasCalled = false; var MixinB = Mixin.create({ cpWithSetter2: computed(function(k, v) { cpWasCalled = true; }), cpWithSetter3: computed(function(k, v) { cpWasCalled = true; }), cpWithoutSetter: computed(function(k) { cpWasCalled = true; }) }); MixinA.apply(obj); MixinB.apply(obj); set(obj, 'cpWithSetter2', 'test'); ok(cpWasCalled, "The computed property setter was called when defined with two args"); cpWasCalled = false; set(obj, 'cpWithSetter3', 'test'); ok(cpWasCalled, "The computed property setter was called when defined with three args"); cpWasCalled = false; set(obj, 'cpWithoutSetter', 'test'); equal(get(obj, 'cpWithoutSetter'), 'test', "The default setter was called, the value is correct"); ok(!cpWasCalled, "The default setter was called, not the CP itself"); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/concatenatedProperties_test", [], function() { "use strict"; /*globals setup */ QUnit.module('Ember.Mixin concatenatedProperties'); test('defining concatenated properties should concat future version', function() { var MixinA = Ember.Mixin.create({ concatenatedProperties: ['foo'], foo: ['a', 'b', 'c'] }); var MixinB = Ember.Mixin.create({ foo: ['d', 'e', 'f'] }); var obj = Ember.mixin({}, MixinA, MixinB); deepEqual(Ember.get(obj, 'foo'), ['a', 'b', 'c', 'd', 'e', 'f']); }); test('concatenatedProperties should be concatenated', function() { var MixinA = Ember.Mixin.create({ concatenatedProperties: ['foo'], foo: ['a', 'b', 'c'] }); var MixinB = Ember.Mixin.create({ concatenatedProperties: 'bar', foo: ['d', 'e', 'f'], bar: [1,2,3] }); var MixinC = Ember.Mixin.create({ bar: [4,5,6] }); var obj = Ember.mixin({}, MixinA, MixinB, MixinC); deepEqual(Ember.get(obj, 'concatenatedProperties'), ['foo', 'bar'], 'get concatenatedProperties'); deepEqual(Ember.get(obj, 'foo'), ['a', 'b', 'c', 'd', 'e', 'f'], 'get foo'); deepEqual(Ember.get(obj, 'bar'), [1,2,3,4,5,6], 'get bar'); }); test('adding a prop that is not an array should make array', function() { var MixinA = Ember.Mixin.create({ concatenatedProperties: ['foo'], foo: [1,2,3] }); var MixinB = Ember.Mixin.create({ foo: 4 }); var obj = Ember.mixin({}, MixinA, MixinB); deepEqual(Ember.get(obj, 'foo'), [1,2,3,4]); }); test('adding a prop that is not an array should make array', function() { var MixinA = Ember.Mixin.create({ concatenatedProperties: ['foo'], foo: 'bar' }); var obj = Ember.mixin({}, MixinA); deepEqual(Ember.get(obj, 'foo'), ['bar']); }); test('adding a non-concatenable property that already has a defined value should result in an array with both values', function() { var mixinA = Ember.Mixin.create({ foo: 1 }); var mixinB = Ember.Mixin.create({ concatenatedProperties: ['foo'], foo: 2 }); var obj = Ember.mixin({}, mixinA, mixinB); deepEqual(Ember.get(obj, 'foo'), [1, 2]); }); test('adding a concatenable property that already has a defined value should result in a concatenated value', function() { var mixinA = Ember.Mixin.create({ foobar: 'foo' }); var mixinB = Ember.Mixin.create({ concatenatedProperties: ['foobar'], foobar: 'bar' }); var obj = Ember.mixin({}, mixinA, mixinB); equal(Ember.get(obj, 'foobar'), 'foobar'); }); }); define("ember-metal/tests/mixin/concatenatedProperties_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/concatenatedProperties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/concatenatedProperties_test.js should pass jshint.'); }); }); define("ember-metal/tests/mixin/detect_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-metal/computed","ember-metal/properties"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.K var get = __dependency2__.get; var set = __dependency3__.set; var Mixin = __dependency4__.Mixin; var computed = __dependency5__.computed; var defineProperty = __dependency6__.defineProperty; QUnit.module('Mixin.detect'); test('detect() finds a directly applied mixin', function() { var MixinA = 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()'); }); test('detect() finds nested mixins', function() { var MixinA = Mixin.create({}); var MixinB = 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()'); }); test('detect() finds mixins on other mixins', function() { var MixinA = Mixin.create({}); var MixinB = Mixin.create(MixinA); equal(MixinA.detect(MixinB), true, 'MixinA is part of MixinB'); equal(MixinB.detect(MixinA), false, 'MixinB is not part of MixinA'); }); test('detect handles null values', function() { var MixinA = Mixin.create(); equal(MixinA.detect(null), false); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/introspection_test", ["ember-metal/utils","ember-metal/mixin","ember-metal/enumerable_utils"], function(__dependency1__, __dependency2__, __dependency3__) { "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 guidFor = __dependency1__.guidFor; var mixin = __dependency2__.mixin; var Mixin = __dependency2__.Mixin; var EnumerableUtils = __dependency3__["default"]; var PrivateProperty = Mixin.create({ _foo: '_FOO' }); var PublicProperty = Mixin.create({ foo: 'FOO' }); var PrivateMethod = Mixin.create({ _fooMethod: function() {} }); var PublicMethod = Mixin.create({ fooMethod: function() {} }); var BarProperties = Mixin.create({ _bar: '_BAR', bar: 'bar' }); var BarMethods = Mixin.create({ _barMethod: function() {}, barMethod: function() {} }); var Combined = Mixin.create(BarProperties, BarMethods); var obj ; QUnit.module('Basic introspection', { setup: function() { obj = {}; mixin(obj, PrivateProperty, PublicProperty, PrivateMethod, PublicMethod, Combined); } }); test('Ember.mixins()', function() { function mapGuids(ary) { return EnumerableUtils.map(ary, function(x) { return guidFor(x); }); } deepEqual(mapGuids(Mixin.mixins(obj)), mapGuids([PrivateProperty, PublicProperty, PrivateMethod, PublicMethod, Combined, BarProperties, BarMethods]), 'should return included mixins'); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/mergedProperties_test", ["ember-metal/property_get","ember-metal/mixin"], function(__dependency1__, __dependency2__) { "use strict"; /*globals setup */ var get = __dependency1__.get; var mixin = __dependency2__.mixin; var Mixin = __dependency2__.Mixin; QUnit.module('Mixin mergedProperties'); test('defining mergedProperties should merge future version', function() { var MixinA = Mixin.create({ mergedProperties: ['foo'], foo: { a: true, b: true, c: true } }); var MixinB = Mixin.create({ foo: { d: true, e: true, f: true } }); var obj = mixin({}, MixinA, MixinB); deepEqual(get(obj, 'foo'), { a: true, b: true, c: true, d: true, e: true, f: true }); }); test('defining mergedProperties on future mixin should merged into past', function() { var MixinA = Mixin.create({ foo: { a: true, b: true, c: true } }); var MixinB = Mixin.create({ mergedProperties: ['foo'], foo: { d: true, e: true, f: true } }); var obj = mixin({}, MixinA, MixinB); deepEqual(get(obj, 'foo'), { a: true, b: true, c: true, d: true, e: true, f: true }); }); test('defining mergedProperties with null properties should keep properties null', function() { var MixinA = Mixin.create({ mergedProperties: ['foo'], foo: null }); var MixinB = Mixin.create({ foo: null }); var obj = mixin({}, MixinA, MixinB); equal(get(obj, 'foo'), null); }); test("mergedProperties' properties can get overwritten", function() { var MixinA = Mixin.create({ mergedProperties: ['foo'], foo: { a: 1 } }); var MixinB = Mixin.create({ foo: { a: 2 } }); var obj = mixin({}, MixinA, MixinB); deepEqual(get(obj, 'foo'), { a: 2 }); }); test('mergedProperties should be concatenated', function() { var MixinA = Mixin.create({ mergedProperties: ['foo'], foo: { a: true, b: true, c: true } }); var MixinB = Mixin.create({ mergedProperties: 'bar', foo: { d: true, e: true, f: true }, bar: { a: true, l: true } }); var MixinC = Mixin.create({ bar: { e: true, x: true } }); var obj = mixin({}, MixinA, MixinB, MixinC); deepEqual(get(obj, 'mergedProperties'), ['foo', 'bar'], 'get mergedProperties'); deepEqual(get(obj, 'foo'), { a: true, b: true, c: true, d: true, e: true, f: true }, "get foo"); deepEqual(get(obj, 'bar'), { a: true, l: true, e: true, x: true }, "get bar"); }); test("mergedProperties' overwriting methods can call _super", function() { expect(4); var MixinA = Mixin.create({ mergedProperties: ['foo'], foo: { meth: function(a) { equal(a, "WOOT", "_super successfully called MixinA's `foo.meth` method"); return "WAT"; } } }); var MixinB = Mixin.create({ foo: { meth: function(a) { ok(true, "MixinB's `foo.meth` method called"); return this._super.apply(this, arguments); } } }); var MixinC = Mixin.create({ foo: { meth: function(a) { ok(true, "MixinC's `foo.meth` method called"); return this._super(a); } } }); var obj = mixin({}, MixinA, MixinB, MixinC); equal(obj.foo.meth("WOOT"), "WAT"); }); }); define("ember-metal/tests/mixin/mergedProperties_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/mixin'); test('ember-metal/tests/mixin/mergedProperties_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/mixin/mergedProperties_test.js should pass jshint.'); }); }); define("ember-metal/tests/mixin/method_test", ["ember-metal/property_get","ember-metal/platform","ember-metal/mixin"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; /*globals raises */ var get = __dependency1__.get; var create = __dependency2__.create; var mixin = __dependency3__.mixin; var Mixin = __dependency3__.Mixin; QUnit.module('Mixin Methods'); test('defining simple methods', function() { var MixinA, obj, props; props = { publicMethod: function() { return 'publicMethod'; }, _privateMethod: function() { return 'privateMethod'; } }; MixinA = 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'); }); test('overriding public methods', function() { var MixinA, MixinB, MixinC, MixinD, MixinE, MixinF, obj; MixinA = Mixin.create({ publicMethod: function() { return 'A'; } }); MixinB = Mixin.create(MixinA, { publicMethod: function() { return this._super()+'B'; } }); MixinD = Mixin.create(MixinA, { publicMethod: function() { return this._super()+'D'; } }); MixinF = Mixin.create({ publicMethod: function() { return this._super()+'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'); }); test('overriding inherited objects', function() { var cnt = 0; var MixinA = Mixin.create({ foo: function() { cnt++; } }); var MixinB = Mixin.create({ foo: function() { this._super(); cnt++; } }); var objA = {}; MixinA.apply(objA); var objB = create(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'); }); test('Including the same mixin more than once will only run once', function() { var cnt = 0; var MixinA = Mixin.create({ foo: function() { cnt++; } }); var MixinB = Mixin.create(MixinA, { foo: function() { this._super(); } }); var MixinC = Mixin.create(MixinA, { foo: function() { this._super(); } }); var MixinD = Mixin.create(MixinB, MixinC, MixinA, { foo: function() { this._super(); } }); 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'); }); test('_super from a single mixin with no superclass does not error', function() { var MixinA = Mixin.create({ foo: function() { this._super(); } }); var obj = {}; MixinA.apply(obj); obj.foo(); ok(true); }); 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.create({ foo: function() { if (remaining-- > 0) this._super(); } }); var MixinB = Mixin.create({ foo: function() { this._super(); } }); var obj = {}; MixinA.apply(obj); MixinB.apply(obj); obj.foo(); ok(true); }); // .......................................................... // CONFLICTS // QUnit.module('Method Conflicts'); test('overriding toString', function() { var MixinA = Mixin.create({ toString: function() { return 'FOO'; } }); var obj = {}; MixinA.apply(obj); equal(obj.toString(), 'FOO', 'should override toString w/o error'); obj = {}; mixin(obj, { toString: function() { return 'FOO'; } }); equal(obj.toString(), 'FOO', 'should override toString w/o error'); }); // .......................................................... // BUGS // QUnit.module('system/mixin/method_test BUGS'); test('applying several mixins at once with sup already defined causes infinite loop', function() { var cnt = 0; var MixinA = Mixin.create({ foo: function() { cnt++; } }); var MixinB = Mixin.create({ foo: function() { this._super(); cnt++; } }); var MixinC = Mixin.create({ foo: function() { this._super(); cnt++; } }); var obj = {}; mixin(obj, MixinA); // sup already exists mixin(obj, MixinB, MixinC); // must be more than one mixin cnt = 0; obj.foo(); equal(cnt, 3, 'should invoke all 3 methods'); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/observer_test", ["ember-metal/tests/props_helper","ember-metal/property_get","ember-metal/platform","ember-metal/mixin","ember-metal/watching"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; /*globals testBoth */ var testBoth = __dependency1__["default"]; var get = __dependency2__.get; var create = __dependency3__.create; var observer = __dependency4__.observer; var mixin = __dependency4__.mixin; var Mixin = __dependency4__.Mixin; var isWatching = __dependency5__.isWatching; QUnit.module('Mixin observer'); testBoth('global observer helper', function(get, set) { var MyMixin = Mixin.create({ count: 0, foo: observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = 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'); }); testBoth('global observer helper takes multiple params', function(get, set) { var MyMixin = Mixin.create({ count: 0, foo: observer('bar', 'baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = 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'); }); testBoth('replacing observer should remove old observer', function(get, set) { var MyMixin = Mixin.create({ count: 0, foo: observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var Mixin2 = Mixin.create({ foo: observer('baz', function() { set(this, 'count', get(this, 'count')+10); }) }); var obj = 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'); }); testBoth('observing chain with property before', function(get, set) { var obj2 = {baz: 'baz'}; var MyMixin = Mixin.create({ count: 0, bar: obj2, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = 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'); }); testBoth('observing chain with property after', function(get, set) { var obj2 = {baz: 'baz'}; var MyMixin = Mixin.create({ count: 0, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }), bar: obj2 }); var obj = 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'); }); testBoth('observing chain with property in mixin applied later', function(get, set) { var obj2 = {baz: 'baz'}; var MyMixin = Mixin.create({ count: 0, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var MyMixin2 = Mixin.create({bar: obj2}); var obj = 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'); }); testBoth('observing chain with existing property', function(get, set) { var obj2 = {baz: 'baz'}; var MyMixin = Mixin.create({ count: 0, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = 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'); }); testBoth('observing chain with property in mixin before', function(get, set) { var obj2 = {baz: 'baz'}; var MyMixin2 = Mixin.create({bar: obj2}); var MyMixin = Mixin.create({ count: 0, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = 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'); }); testBoth('observing chain with property in mixin after', function(get, set) { var obj2 = {baz: 'baz'}; var MyMixin2 = Mixin.create({bar: obj2}); var MyMixin = Mixin.create({ count: 0, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = 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'); }); testBoth('observing chain with overriden property', function(get, set) { var obj2 = {baz: 'baz'}; var obj3 = {baz: 'foo'}; var MyMixin2 = Mixin.create({bar: obj3}); var MyMixin = Mixin.create({ count: 0, foo: observer('bar.baz', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = mixin({bar: obj2}, MyMixin, MyMixin2); equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); equal(isWatching(obj2, 'baz'), false, 'should not be watching baz'); equal(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'); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/reopen_test", ["ember-metal/mixin"], function(__dependency1__) { "use strict"; var Mixin = __dependency1__["default"]; QUnit.module('Ember.Mixin#reopen'); test('using reopen() to add more properties to a simple', function() { var MixinA = Mixin.create({ foo: 'FOO', baz: 'BAZ' }); MixinA.reopen({ bar: 'BAR', foo: 'FOO2' }); var obj = {}; MixinA.apply(obj); equal(Ember.get(obj, 'foo'), 'FOO2', 'mixin() should override'); equal(Ember.get(obj, 'baz'), 'BAZ', 'preserve MixinA props'); equal(Ember.get(obj, 'bar'), 'BAR', 'include MixinB props'); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/required_test", ["ember-metal/mixin","ember-metal/property_get"], function(__dependency1__, __dependency2__) { "use strict"; /*globals setup raises */ var mixin = __dependency1__.mixin; var Mixin = __dependency1__.Mixin; var required = __dependency1__.required; var get = __dependency2__.get; var PartialMixin, FinalMixin, obj; QUnit.module('Module.required', { setup: function() { PartialMixin = Mixin.create({ foo: required(), bar: 'BAR' }); FinalMixin = Mixin.create({ foo: 'FOO' }); obj = {}; }, teardown: function() { PartialMixin = FinalMixin = obj = null; } }); test('applying a mixin to meet requirement', function() { FinalMixin.apply(obj); PartialMixin.apply(obj); equal(get(obj, 'foo'), 'FOO', 'should now be defined'); }); test('combined mixins to meet requirement', function() { Mixin.create(PartialMixin, FinalMixin).apply(obj); equal(get(obj, 'foo'), 'FOO', 'should now be defined'); }); test('merged mixin', function() { Mixin.create(PartialMixin, { foo: 'FOO' }).apply(obj); equal(get(obj, 'foo'), 'FOO', 'should now be defined'); }); test('define property on source object', function() { obj.foo = 'FOO'; PartialMixin.apply(obj); equal(get(obj, 'foo'), 'FOO', 'should now be defined'); }); test('using apply', function() { mixin(obj, PartialMixin, { foo: 'FOO' }); equal(get(obj, 'foo'), 'FOO', 'should now be defined'); }); }); define("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.'); }); }); define("ember-metal/tests/mixin/without_test", ["ember-metal/mixin"], function(__dependency1__) { "use strict"; var Mixin = __dependency1__.Mixin; test('without should create a new mixin excluding named properties', function() { var MixinA = 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'); }); }); define("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.'); }); }); define("ember-metal/tests/observer_test", ["ember-metal/core","ember-metal/tests/props_helper","ember-metal/observer","ember-metal/property_events","ember-metal/platform","ember-metal/properties","ember-metal/computed","ember-metal/mixin","ember-metal/run_loop","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; /*globals Global:true */ var Ember = __dependency1__["default"]; var testBoth = __dependency2__["default"]; var addObserver = __dependency3__.addObserver; var removeObserver = __dependency3__.removeObserver; var addBeforeObserver = __dependency3__.addBeforeObserver; var _suspendObserver = __dependency3__._suspendObserver; var _suspendObservers = __dependency3__._suspendObservers; var removeBeforeObserver = __dependency3__.removeBeforeObserver; var propertyWillChange = __dependency4__.propertyWillChange; var propertyDidChange = __dependency4__.propertyDidChange; var create = __dependency5__.create; var defineProperty = __dependency6__.defineProperty; var computed = __dependency7__.computed; var cacheFor = __dependency7__.cacheFor; var Mixin = __dependency8__.Mixin; var mixin = __dependency8__.mixin; var observer = __dependency8__.observer; var beforeObserver = __dependency8__.beforeObserver; var immediateObserver = __dependency8__.immediateObserver; var run = __dependency9__["default"]; var beginPropertyChanges = __dependency4__.beginPropertyChanges; var endPropertyChanges = __dependency4__.endPropertyChanges; var changeProperties = __dependency4__.changeProperties; var get = __dependency10__.get; var set = __dependency11__.set; // .......................................................... // ADD OBSERVER // QUnit.module('addObserver'); testBoth('observer should fire when property is modified', function(get,set) { var obj = {}; var count = 0; 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'); }); testBoth('observer should fire when dependent property is modified', function(get, set) { var obj = { bar: 'bar' }; defineProperty(obj, 'foo', computed(function() { return get(this,'bar').toUpperCase(); }).property('bar')); get(obj, 'foo'); var count = 0; 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'); }); testBoth('observer should continue to fire after dependent properties are accessed', function(get, set) { var observerCount = 0, obj = {}; defineProperty(obj, 'prop', Ember.computed(function () { return Math.random(); })); defineProperty(obj, 'anotherProp', Ember.computed('prop', function () { return get(this, 'prop') + Math.random(); })); addObserver(obj, 'prop', function () { observerCount++; }); get(obj, 'anotherProp'); for (var i = 0; i < 10; i++) { propertyWillChange(obj, 'prop'); propertyDidChange(obj, 'prop'); } equal(observerCount, 10, 'should continue to fire indefinitely'); }); if (Ember.EXTEND_PROTOTYPES) { testBoth('observer added declaratively via brace expansion should fire when property changes', function (get, set) { var obj = { }; var count = 0; 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'); }); testBoth('observer specified declaratively via brace expansion should fire when dependent property changes', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; defineProperty(obj, 'foo', computed(function() { return get(this,'bar').toLowerCase(); }).property('bar')); defineProperty(obj, 'bar', computed(function() { return get(this,'baz').toUpperCase(); }).property('baz')); 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'); }); } testBoth('observers watching multiple properties via brace expansion should fire when the properties change', function (get, set) { var obj = { }; var count = 0; mixin(obj, { observeFooAndBar: 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'); }); testBoth('observers watching multiple properties via brace expansion should fire when dependent properties change', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; defineProperty(obj, 'foo', computed(function() { return get(this,'bar').toLowerCase(); }).property('bar')); defineProperty(obj, 'bar', computed(function() { return get(this,'baz').toUpperCase(); }).property('baz')); mixin(obj, { fooAndBarWatcher: 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'); }); testBoth('nested observers should fire in order', function(get,set) { var obj = { foo: 'foo', bar: 'bar' }; var fooCount = 0, barCount = 0; addObserver(obj, 'foo' ,function() { fooCount++; }); 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'); }); testBoth('removing an chain observer on change should not fail', function(get,set) { var foo = { bar: 'bar' }, obj1 = { foo: foo }, obj2 = { foo: foo }, obj3 = { foo: foo }, obj4 = { foo: foo }, count1=0, count2=0, count3=0, count4=0; function observer1() { count1++; } function observer2() { count2++; } function observer3() { count3++; removeObserver(obj1, 'foo.bar', observer1); removeObserver(obj2, 'foo.bar', observer2); removeObserver(obj4, 'foo.bar', observer4); } function observer4() { count4++; } addObserver(obj1, 'foo.bar' , observer1); addObserver(obj2, 'foo.bar' , observer2); addObserver(obj3, 'foo.bar' , observer3); 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'); }); testBoth('removing an chain before observer on change should not fail', function(get,set) { var foo = { bar: 'bar' }, obj1 = { foo: foo }, obj2 = { foo: foo }, obj3 = { foo: foo }, obj4 = { foo: foo }, count1=0, count2=0, count3=0, count4=0; function observer1() { count1++; } function observer2() { count2++; } function observer3() { count3++; removeBeforeObserver(obj1, 'foo.bar', observer1); removeBeforeObserver(obj2, 'foo.bar', observer2); removeBeforeObserver(obj4, 'foo.bar', observer4); } function observer4() { count4++; } addBeforeObserver(obj1, 'foo.bar' , observer1); addBeforeObserver(obj2, 'foo.bar' , observer2); addBeforeObserver(obj3, 'foo.bar' , observer3); 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'); }); testBoth('suspending an observer should not fire during callback', function(get,set) { var obj = {}, target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; addObserver(obj, 'foo', target, target.method); 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(_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'); }); testBoth('suspending an observer should not defer change notifications during callback', function(get,set) { var obj = {}, target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; addObserver(obj, 'foo', target, target.method); addObserver(obj, 'foo', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); set(obj, 'foo', '2'); return 'result'; } set(obj, 'foo', '1'); beginPropertyChanges(); equal(_suspendObserver(obj, 'foo', target, target.method, callback), 'result'); endPropertyChanges(); set(obj, 'foo', '3'); deepEqual(target.values, ['1', '3'], 'should invoke'); deepEqual(otherTarget.values, ['1', '2', '3'], 'should invoke'); }); testBoth('suspending observers should not fire during callback', function(get,set) { var obj = {}, target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; addObserver(obj, 'foo', target, target.method); 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(_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'); }); testBoth('suspending observers should not defer change notifications during callback', function(get,set) { var obj = {}, target, otherTarget; target = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; otherTarget = { values: [], method: function() { this.values.push(get(obj, 'foo')); } }; addObserver(obj, 'foo', target, target.method); addObserver(obj, 'foo', otherTarget, otherTarget.method); function callback() { /*jshint validthis:true */ equal(this, target); set(obj, 'foo', '2'); return 'result'; } set(obj, 'foo', '1'); beginPropertyChanges(); equal(_suspendObservers(obj, ['foo'], target, target.method, callback), 'result'); endPropertyChanges(); set(obj, 'foo', '3'); deepEqual(target.values, ['1', '3'], 'should invoke'); deepEqual(otherTarget.values, ['1', '2', '3'], 'should invoke'); }); testBoth('deferring property change notifications', function(get,set) { var obj = { foo: 'foo' }; var fooCount = 0; addObserver(obj, 'foo' ,function() { fooCount++; }); beginPropertyChanges(obj); set(obj, 'foo', 'BIFF'); set(obj, 'foo', 'BAZ'); endPropertyChanges(obj); equal(fooCount, 1, 'foo should have fired once'); }); 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); addObserver(obj, 'foo' ,function() { fooCount++; }); try { 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'); changeProperties(function() { set(obj, 'foo', 'BIFF2'); set(obj, 'foo', 'BAZ2'); }); equal(fooCount, 2, 'foo should have fired again once'); }); testBoth('deferring property change notifications will not defer before observers', function(get,set) { var obj = { foo: 'foo' }; var fooCount = 0; addBeforeObserver(obj, 'foo' ,function() { fooCount++; }); beginPropertyChanges(obj); set(obj, 'foo', 'BIFF'); equal(fooCount, 1, 'should fire before observer immediately'); set(obj, 'foo', 'BAZ'); endPropertyChanges(obj); equal(fooCount, 1, 'should not fire before observer twice'); }); testBoth('implementing sendEvent on object should invoke when deferring property change notifications ends', function(get, set) { var count = 0, events = []; var obj = { sendEvent: function(eventName) { events.push(eventName); }, foo: 'baz' }; addObserver(obj, 'foo', function() { count++; }); beginPropertyChanges(obj); set(obj, 'foo', 'BAZ'); equal(count, 0, 'should have not invoked observer'); equal(events.length, 1, 'should have invoked sendEvent for before'); 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'); }); testBoth('addObserver should propagate through prototype', function(get,set) { var obj = { foo: 'foo', count: 0 }, obj2; addObserver(obj, 'foo', function() { this.count++; }); obj2 = create(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'); }); 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++; } }; addObserver(observed, 'foo', target1, 'didChange'); 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'); }); 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++; } }; addObserver(observed, 'foo', target1, 'didChange'); 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'); testBoth('removing observer should stop firing', function(get,set) { var obj = {}; var count = 0; function F() { count++; } addObserver(obj, 'foo', F); set(obj, 'foo', 'bar'); equal(count, 1, 'should have invoked observer'); removeObserver(obj, 'foo', F); set(obj, 'foo', 'baz'); equal(count, 1, "removed observer shouldn't fire"); }); testBoth('local observers can be removed', function(get, set) { var barObserved = 0; var MyMixin = Mixin.create({ foo1: observer('bar', function() { barObserved++; }), foo2: observer('bar', function() { barObserved++; }) }); var obj = {}; MyMixin.apply(obj); set(obj, 'bar', 'HI!'); equal(barObserved, 2, 'precond - observers should be fired'); removeObserver(obj, 'bar', null, 'foo1'); barObserved = 0; set(obj, 'bar', 'HI AGAIN!'); equal(barObserved, 1, 'removed observers should not be called'); }); 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++; } }; addObserver(observed, 'foo', target1, 'didChange'); 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'); removeObserver(observed, 'foo', target1, 'didChange'); 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'); testBoth('observer should fire before a property is modified', function(get,set) { var obj = { foo: 'foo' }; var count = 0; 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'); }); testBoth('observer should fire before dependent property is modified', function(get, set) { var obj = { bar: 'bar' }; defineProperty(obj, 'foo', computed(function() { return get(this,'bar').toUpperCase(); }).property('bar')); get(obj, 'foo'); var count = 0; 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.EXTEND_PROTOTYPES) { testBoth('before observer added declaratively via brace expansion should fire when property changes', function (get, set) { var obj = {}; var count = 0; 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'); }); testBoth('before observer specified declaratively via brace expansion should fire when dependent property changes', function (get, set) { var obj = { baz: 'Initial' }; var count = 0; defineProperty(obj, 'foo', computed(function() { return get(this,'bar').toLowerCase(); }).property('bar')); defineProperty(obj, 'bar', computed(function() { return get(this,'baz').toUpperCase(); }).property('baz')); 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'); }); } testBoth('before observer watching multiple properties via brce expansion should fire when properties change', function (get, set) { var obj = {}; var count = 0; mixin(obj, { fooAndBarWatcher: 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'); }); 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; defineProperty(obj, 'foo', computed(function() { return get(this,'bar').toLowerCase(); }).property('bar')); defineProperty(obj, 'bar', computed(function() { return get(this,'baz').toUpperCase(); }).property('baz')); mixin(obj, { fooAndBarWatcher: 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'); }); testBoth('addBeforeObserver should propagate through prototype', function(get,set) { var obj = { foo: 'foo', count: 0 }, obj2; addBeforeObserver(obj, 'foo', function() { this.count++; }); obj2 = create(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 oberver on parent'); equal(obj2.count, 0, 'should not have invoked observer on inherited'); }); 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++; } }; addBeforeObserver(observed, 'foo', target1, 'willChange'); 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; var originalLookup = Ember.lookup, lookup; QUnit.module('addObserver - dependentkey with chained properties', { setup: function() { obj = { foo: { bar: { baz: { biff: "BIFF" } } } }; Ember.lookup = lookup = { Global: { foo: { bar: { baz: { biff: "BIFF" } } } } }; count = 0; }, teardown: function() { obj = count = null; Ember.lookup = originalLookup; } }); testBoth('depending on a chain with a computed property', function (get, set){ defineProperty(obj, 'computed', computed(function () { return {foo: 'bar'}; })); var changed = 0; addObserver(obj, 'computed.foo', function () { changed++; }); equal(undefined, cacheFor(obj, 'computed'), 'addObserver should not compute CP'); set(obj, 'computed.foo', 'baz'); equal(changed, 1, 'should fire observer'); }); testBoth('depending on a simple chain', function(get, set) { var val ; 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'); }); testBoth('depending on a Global chain', function(get, set) { var Global = lookup.Global, val; 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'); testBoth('setting simple prop should not trigger', function(get, set) { var obj = { foo: 'bar' }; var count = 0; 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. testBoth('setting a cached computed property whose value has changed should trigger', function(get, set) { var obj = {}; defineProperty(obj, 'foo', computed(function(key, value) { if (arguments.length === 2) { return value; } return get(this, 'baz'); }).property('baz')); var count = 0; 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"); testBoth("immediate observers should fire synchronously", function(get, set) { var obj = {}, observerCalled = 0, mixin; // explicitly create a run loop so we do not inadvertently // trigger deferred behavior run(function() { mixin = Mixin.create({ fooDidChange: immediateObserver('foo', function() { observerCalled++; equal(get(this, 'foo'), "barbaz", "newly set value is immediately available"); }) }); mixin.apply(obj); defineProperty(obj, 'foo', 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.EXTEND_PROTOTYPES) { testBoth('immediate observers added declaratively via brace expansion fire synchronously', function (get, set) { var obj = {}, observerCalled = 0, mixin; // explicitly create a run loop so we do not inadvertently // trigger deferred behavior run(function() { mixin = Mixin.create({ fooDidChange: function() { observerCalled++; equal(get(this, 'foo'), "barbaz", "newly set value is immediately available"); }.observesImmediately('{foo,bar}') }); mixin.apply(obj); defineProperty(obj, 'foo', 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"); }); }); } testBoth('immediate observers watching multiple properties via brace expansion fire synchronously', function (get, set) { var obj = {}, observerCalled = 0, mixin; // explicitly create a run loop so we do not inadvertently // trigger deferred behavior run(function() { mixin = Mixin.create({ fooDidChange: immediateObserver('{foo,bar}', function() { observerCalled++; equal(get(this, 'foo'), "barbaz", "newly set value is immediately available"); }) }); mixin.apply(obj); defineProperty(obj, 'foo', 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"); }); }); testBoth("immediate observers are for internal properties only", function(get, set) { expectAssertion(function() { immediateObserver('foo.bar', Ember.K); }, 'Immediate observers must observe internal properties only, not properties on other objects.'); }); QUnit.module("changeProperties"); 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 () { addBeforeObserver(obj, 'foo', this, 'willChange'); addObserver(obj, 'foo', this, 'didChange'); }, remove: function() { removeBeforeObserver(obj, 'foo', this, 'willChange'); 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(); 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'); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var set = __dependency1__.set; var get = __dependency2__.get; var computed = __dependency3__.computed; var defineProperty = __dependency4__.defineProperty; var propertyDidChange = __dependency5__.propertyDidChange; var beginPropertyChanges = __dependency5__.beginPropertyChanges; var endPropertyChanges = __dependency5__.endPropertyChanges; var addObserver = __dependency6__.addObserver; /* This test file is designed to capture performance regressions related to deferred computation. Things like run loops, computed properties, and bindings should run the minimum amount of times to achieve best performance, so any bugs that cause them to get evaluated more than necessary should be put here. */ QUnit.module("Computed Properties - Number of times evaluated"); 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, obsCount = 0; defineProperty(obj, 'abc', computed(function(key) { cpCount++; return 'computed '+key; }).property('a', 'b', 'c')); get(obj, 'abc'); cpCount = 0; addObserver(obj, 'abc', function() { obsCount++; }); beginPropertyChanges(); set(obj, 'a', 'aa'); set(obj, 'b', 'bb'); set(obj, 'c', 'cc'); endPropertyChanges(); get(obj, 'abc'); equal(cpCount, 1, "The computed property is only invoked once"); equal(obsCount, 1, "The observer is only invoked once"); }); 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; defineProperty(foo.bar.baz, 'bam', computed(function() { count++; })); addObserver(foo, 'bar.baz.bam', function() {}); propertyDidChange(get(foo, 'bar.baz'), 'bam'); equal(count, 0, "should not have recomputed property"); }); }); define("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.'); }); }); define("ember-metal/tests/platform/create_test", ["ember-metal/platform"], function(__dependency1__) { "use strict"; var create = __dependency1__.create; QUnit.module("Ember.create()"); test("should inherit the properties from the parent object", function() { var obj = { foo: 'FOO' }; var obj2 = create(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 test("passing additional property descriptors should define", function() { var obj = { foo: 'FOO', repl: 'obj' }; var obj2 = create(obj, { bar: { value: 'BAR' }, repl: { value: 'obj2' } }); equal(obj2.bar, 'BAR', 'should have defined'); equal(obj2.repl, 'obj2', 'should have replaced parent'); }); test("passing additional property descriptors should not pollute parent object", function() { var obj = { foo: 'FOO', repl: 'obj' }; var obj2 = create(obj, { repl: { value: 'obj2' } }); notEqual(obj.repl, obj2.repl, 'should not pollute parent object'); }); }); define("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.'); }); }); define("ember-metal/tests/platform/defineProperty_test", ["ember-metal/platform","ember-metal/enumerable_utils"], function(__dependency1__, __dependency2__) { "use strict"; var platform = __dependency1__.platform; var EnumerableUtils = __dependency2__["default"]; function isEnumerable(obj, keyName) { var keys = []; for(var key in obj) { if (obj.hasOwnProperty(key)) keys.push(key); } return EnumerableUtils.indexOf(keys, keyName)>=0; } QUnit.module("platform.defineProperty()"); test("defining a simple property", function() { var obj = {}; platform.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'); }); test('defining a read only property', function() { var obj = {}; platform.defineProperty(obj, 'foo', { enumerable: true, writable: false, value: 'FOO' }); equal(obj.foo, 'FOO', 'should have added property'); if (platform.defineProperty.isSimulated) { obj.foo = "BAR"; equal(obj.foo, 'BAR', 'simulated defineProperty should silently work'); } else { // 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'); } } }); test('defining a non enumerable property', function() { var obj = {}; platform.defineProperty(obj, 'foo', { enumerable: false, writable: true, value: 'FOO' }); if (platform.defineProperty.isSimulated) { equal(isEnumerable(obj, 'foo'), true, 'simulated defineProperty will leave properties enumerable'); } else { equal(isEnumerable(obj, 'foo'), false, 'real defineProperty will make property not-enumerable'); } }); // If accessors don't exist, behavior that relies on getters // and setters don't do anything if (platform.hasPropertyAccessors) { test('defining a getter/setter', function() { var obj = {}, getCnt = 0, setCnt = 0, v = 'FOO'; var desc = { enumerable: true, get: function() { getCnt++; return v; }, set: function(val) { setCnt++; v = val; } }; if (platform.hasPropertyAccessors) { platform.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'); } }); test('defining getter/setter along with writable', function() { var obj ={}; raises(function() { platform.defineProperty(obj, 'foo', { enumerable: true, get: function() {}, set: function() {}, writable: true }); }, Error, 'defining writable and get/set should throw exception'); }); test('defining getter/setter along with value', function() { var obj ={}; raises(function() { platform.defineProperty(obj, 'foo', { enumerable: true, get: function() {}, set: function() {}, value: 'FOO' }); }, Error, 'defining value and get/set should throw exception'); }); } }); define("ember-metal/tests/platform/defineProperty_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/platform'); test('ember-metal/tests/platform/defineProperty_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/platform/defineProperty_test.js should pass jshint.'); }); }); define("ember-metal/tests/properties_test", ["ember-metal/core","ember-metal/platform","ember-metal/property_set","ember-metal/property_get","ember-metal/computed","ember-metal/properties"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; var platform = __dependency2__.platform; var set = __dependency3__.set; var get = __dependency4__.get; var computed = __dependency5__.computed; var defineProperty = __dependency6__.defineProperty; var deprecateProperty = __dependency6__.deprecateProperty; QUnit.module('Ember.defineProperty'); test('toString', function() { var obj = {}; defineProperty(obj, 'toString', undefined, function() { return 'FOO'; }); equal(obj.toString(), 'FOO', 'should replace toString'); }); 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"); } }; defineProperty(obj, 'foo', undefined, "bar"); }); test("for descriptor properties, didDefineProperty hook should be called if implemented", function() { expect(2); var computedProperty = computed(Ember.K); var obj = { didDefineProperty: function(obj, keyName, value) { equal(keyName, 'foo', "key name should be foo"); strictEqual(value, computedProperty, "value should be passed descriptor"); } }; defineProperty(obj, 'foo', computedProperty); }); if (platform.hasPropertyAccessors) { QUnit.module('Ember.deprecateProperty'); test("enables access to deprecated property and returns the value of the new property", function() { expect(3); var obj = {foo: 'bar'}; 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'); }); test("deprecatedKey is not enumerable", function() { expect(2); var obj = {foo: 'bar', blammo: 'whammy'}; deprecateProperty(obj, 'baz', 'foo'); for (var prop in obj) { if (obj.hasOwnProperty(prop)) { notEqual(prop, 'baz'); } } }); test("enables setter to deprecated property and updates the value of the new property", function() { expect(3); var obj = {foo: 'bar'}; 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'); }); } }); define("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.'); }); }); define("ember-metal/tests/props_helper", ["ember-metal/property_get","ember-metal/property_set","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; /*global testBoth:true */ var get = __dependency1__.get; var set = __dependency2__.set; // used by unit tests to test both accessor mode and non-accessor mode __exports__["default"] = function(testname, callback) { test(testname+' using Ember.get()/Ember.set()', function() { callback(get, set); }); // test(testname+' using accessors', function() { // if (Ember.USES_ACCESSORS) callback(aget, aset); // else ok('SKIPPING ACCESSORS'); // }); } }); define("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.'); }); }); define("ember-metal/tests/run_loop/add_queue_test", ["ember-metal/run_loop","ember-metal/array"], function(__dependency1__, __dependency2__) { "use strict"; var run = __dependency1__["default"]; var indexOf = __dependency2__.indexOf; var originalQueues = run.queues; var queues; QUnit.module('system/run_loop/add_queue_test',{ setup: function(){ run.queues = queues = ['blork', 'bleep']; }, teardown: function(){ run.queues = originalQueues; } }); test('adds a queue after a specified one', function() { run._addQueue('testeroo', 'blork'); equal(indexOf.call(queues, 'testeroo'), 1, "new queue was added after specified queue"); }); test('does not add the queue if it already exists', function(){ run._addQueue('testeroo', 'blork'); run._addQueue('testeroo', 'blork'); equal(queues.length, 3, "queue was not added twice"); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/debounce_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; var originalDebounce = run.backburner.debounce; var wasCalled = false; QUnit.module('Ember.run.debounce',{ setup: function() { run.backburner.debounce = function() { wasCalled = true; }; }, teardown: function() { run.backburner.debounce = originalDebounce; } }); test('Ember.run.debounce uses Backburner.debounce', function() { run.debounce(function() {}); ok(wasCalled, 'Ember.run.debounce used'); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/join_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('system/run_loop/join_test'); test('run.join brings its own run loop if none provided', function() { ok(!run.currentRunLoop, 'expects no existing run-loop'); run.join(function() { ok(run.currentRunLoop, 'brings its own run loop'); }); }); test('run.join joins and existing run-loop, and fires its action queue.', function() { var outerRunLoop, wasInvoked; run(function() { outerRunLoop = run.currentRunLoop; run.join(function() { wasInvoked = true; deepEqual(outerRunLoop, run.currentRunLoop, 'joined the existing run-loop'); }); ok(!wasInvoked, 'expected the joined callback not be invoked yet'); }); ok(wasInvoked, 'expected the joined callback to have invoked'); }); test('run.join returns a value if creating a new run-loop', function() { var value = 'returned value'; var result = run.join(function() { return value; }); equal(value, result, 'returns expected output'); }); test('run.join returns undefined if joining another run-loop', function() { var value = 'returned value', result; run(function() { var result = run.join(function() { return value; }); }); equal(result, undefined, 'returns nothing'); }); }); define("ember-metal/tests/run_loop/join_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/run_loop'); test('ember-metal/tests/run_loop/join_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/run_loop/join_test.js should pass jshint.'); }); }); define("ember-metal/tests/run_loop/later_test", ["ember-metal/is_none","ember-metal/run_loop"], function(__dependency1__, __dependency2__) { "use strict"; var isNone = __dependency1__["default"]; var run = __dependency2__["default"]; var originalSetTimeout = window.setTimeout; var originalDateValueOf = Date.prototype.valueOf; function wait(callback, maxWaitCount) { maxWaitCount = isNone(maxWaitCount) ? 100 : maxWaitCount; originalSetTimeout(function() { if (maxWaitCount > 0 && (run.hasScheduledTimers() || run.currentRunLoop)) { wait(callback, maxWaitCount - 1); return; } callback(); }, 10); } 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(function() { run.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(function() { run.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(function() { run.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 }, firstRunLoop, secondRunLoop; run(function() { firstRunLoop = run.currentRunLoop; run.later(obj, function(amt) { this.invoked += amt; secondRunLoop = run.currentRunLoop; }, 10, 1); // 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. var pauseUntil = +new Date() + 100; while(+new Date() < pauseUntil) { /* do nothing - sleeping */ } }); ok(firstRunLoop, "first run loop captured"); ok(!run.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(function() { runLoop = run.currentRunLoop; ok(runLoop); run.later(function() { ok(run.currentRunLoop && run.currentRunLoop !== runLoop, 'first later callback has own run loop'); runLoop = run.currentRunLoop; run.later(function() { ok(run.currentRunLoop && run.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(function() { run.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. var pauseUntil = +new Date() + 60; while(+new Date() < pauseUntil) { /* do nothing - sleeping */ } }, 1); run.later(function() { equal(count, 1, 'callbacks called in order'); }, 50); }); wait(function() { window.setTimeout = originalSetTimeout; QUnit.start(); ok(newSetTimeoutUsed, 'stub setTimeout was used'); }); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/next_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('run.next'); asyncTest('should invoke immediately on next timeout', function() { var invoked = false; run(function() { run.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(function() { firstRunLoop = run.currentRunLoop; run.next(function() { secondRunLoop = run.currentRunLoop; }); }); setTimeout(function() { QUnit.start(); ok(secondRunLoop, 'callback was called from within run loop'); ok(firstRunLoop && secondRunLoop !== firstRunLoop, 'two seperate run loops were invoked'); }, 20); }); asyncTest('multiple calls to run.next share coalesce callbacks into same run loop', function() { var firstRunLoop, secondRunLoop, thirdRunLoop; run(function() { firstRunLoop = run.currentRunLoop; run.next(function() { secondRunLoop = run.currentRunLoop; }); run.next(function() { thirdRunLoop = run.currentRunLoop; }); }); setTimeout(function() { QUnit.start(); ok(secondRunLoop && secondRunLoop === thirdRunLoop, 'callbacks coalesced into same run loop'); }, 20); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/once_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('system/run_loop/once_test'); test('calling invokeOnce more than once invokes only once', function() { var count = 0; run(function() { var F = function() { count++; }; run.once(F); run.once(F); run.once(F); }); equal(count, 1, 'should have invoked once'); }); test('should differentiate based on target', function() { var A = { count: 0 }, B = { count: 0 }; run(function() { var F = function() { this.count++; }; run.once(A, F); run.once(B, F); run.once(A, F); run.once(B, F); }); equal(A.count, 1, 'should have invoked once on A'); equal(B.count, 1, 'should have invoked once on B'); }); test('should ignore other arguments - replacing previous ones', function() { var A = { count: 0 }, B = { count: 0 }; run(function() { var F = function(amt) { this.count += amt; }; run.once(A, F, 10); run.once(B, F, 20); run.once(A, F, 30); run.once(B, F, 40); }); equal(A.count, 30, 'should have invoked once on A'); equal(B.count, 40, 'should have invoked once on B'); }); test('should be inside of a runloop when running', function() { run(function() { run.once(function() { ok(!!run.currentRunLoop, 'should have a runloop'); }); }); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/onerror_test", ["ember-metal","ember-metal/run_loop"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; QUnit.module('system/run_loop/onerror_test'); test('With Ember.onerror undefined, errors in Ember.run are thrown', function () { var thrown = new Error('Boom!'), caught; try { run(function() { throw thrown; }); } catch (error) { caught = error; } deepEqual(caught, thrown); }); test('With Ember.onerror set, errors in Ember.run are caught', function () { var thrown = new Error('Boom!'), caught; Ember.onerror = function(error) { caught = error; }; run(function() { throw thrown; }); deepEqual(caught, thrown); Ember.onerror = undefined; }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/run_bind_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('system/run_loop/run_bind_test'); test('Ember.run.bind builds a run-loop wrapped callback handler', function() { expect(3); var obj = { value: 0, increment: function(increment) { ok(run.currentRunLoop, 'expected a run-loop'); return this.value += increment; } }; var proxiedFunction = run.bind(obj, obj.increment, 1); equal(proxiedFunction(), 1); equal(obj.value, 1); }); test('Ember.run.bind keeps the async callback arguments', function() { var asyncCallback = function(increment, increment2, increment3) { ok(run.currentRunLoop, 'expected a run-loop'); equal(increment, 1); equal(increment2, 2); equal(increment3, 3); }; var asyncFunction = function(fn) { fn(2, 3); }; asyncFunction(run.bind(asyncCallback, asyncCallback, 1)); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/run_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('system/run_loop/run_test'); 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(function() { return 'FOO'; }), 'FOO', 'pass function only'); deepEqual(run(obj, obj.foo), ['BAR', 'FOO'], 'pass obj and obj.method'); deepEqual(run(obj, 'foo'), ['BAR', 'FOO'], 'pass obj and "method"'); deepEqual(run(obj, obj.checkArgs, 'hello', 'world'), ['hello', 'BAR', 'world'], 'pass obj, obj.method, and extra arguments'); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/schedule_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('system/run_loop/schedule_test'); test('scheduling item in queue should defer until finished', function() { var cnt = 0; run(function() { run.schedule('actions', function() { cnt++; }); run.schedule('actions', function() { cnt++; }); equal(cnt, 0, 'should not run action yet') ; }); equal(cnt, 2, 'should flush actions now'); }); test('nested runs should queue each phase independently', function() { var cnt = 0; run(function() { run.schedule('actions', function() { cnt++; }); equal(cnt, 0, 'should not run action yet') ; run(function() { run.schedule('actions', function() { cnt++; }); }); equal(cnt, 1, 'should not run action yet') ; }); equal(cnt, 2, 'should flush actions now'); }); test('prior queues should be flushed before moving on to next queue', function() { var order = []; run(function() { var runLoop = run.currentRunLoop; ok(runLoop, 'run loop present'); run.schedule('sync', function() { order.push('sync'); equal(runLoop, run.currentRunLoop, 'same run loop used'); }); run.schedule('actions', function() { order.push('actions'); equal(runLoop, run.currentRunLoop, 'same run loop used'); run.schedule('actions', function() { order.push('actions'); equal(runLoop, run.currentRunLoop, 'same run loop used'); }); run.schedule('sync', function() { order.push('sync'); equal(runLoop, run.currentRunLoop, 'same run loop used'); }); }); run.schedule('destroy', function() { order.push('destroy'); equal(runLoop, run.currentRunLoop, 'same run loop used'); }); }); deepEqual(order, ['sync', 'actions', 'sync', 'actions', 'destroy']); }); test('makes sure it does not trigger an autorun during testing', function() { expectAssertion(function() { run.schedule('actions', function() {}); }, /wrap any code with asynchronous side-effects in an run/); // make sure not just the first violation is asserted. expectAssertion(function() { run.schedule('actions', function() {}); }, /wrap any code with asynchronous side-effects in an run/); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/sync_test", ["ember-metal/run_loop"], function(__dependency1__) { "use strict"; var run = __dependency1__["default"]; QUnit.module('system/run_loop/sync_test'); test('sync() will immediately flush the sync queue only', function() { var cnt = 0; run(function() { function cntup() { cnt++; } function syncfunc() { if (++cnt<5) run.schedule('sync', syncfunc); run.schedule('actions', cntup); } syncfunc(); equal(cnt, 1, 'should not run action yet') ; run.sync(); equal(cnt, 5, 'should have run sync queue continuously'); }); equal(cnt, 10, 'should flush actions now too'); }); test('calling sync() outside a run loop does not cause an error', function() { expect(0); run.sync(); }); }); define("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.'); }); }); define("ember-metal/tests/run_loop/unwind_test", ["ember-metal/run_loop","ember-metal/error"], function(__dependency1__, __dependency2__) { "use strict"; var run = __dependency1__["default"]; var EmberError = __dependency2__["default"]; QUnit.module('system/run_loop/unwind_test'); test('RunLoop unwinds despite unhandled exception', function() { var initialRunLoop = run.currentRunLoop; raises(function() { run(function() { run.schedule('actions', function() { throw new EmberError("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.currentRunLoop, initialRunLoop, "Previous run loop should be cleaned up despite exception"); // Prevent a failure in this test from breaking subsequent tests. run.currentRunLoop = initialRunLoop; }); test('run unwinds despite unhandled exception', function() { var initialRunLoop = run.currentRunLoop; raises(function() { run(function() { throw new EmberError("boom!"); }); }, EmberError, "boom!"); equal(run.currentRunLoop, initialRunLoop, "Previous run loop should be cleaned up despite exception"); // Prevent a failure in this test from breaking subsequent tests. run.currentRunLoop = initialRunLoop; }); }); define("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.'); }); }); define("ember-metal/tests/utils/can_invoke_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var canInvoke = __dependency1__.canInvoke; var obj; QUnit.module("Ember.canInvoke", { setup: function() { obj = { foobar: "foobar", aMethodThatExists: function() {} }; }, teardown: function() { obj = undefined; } }); test("should return false if the object doesn't exist", function() { equal(canInvoke(undefined, 'aMethodThatDoesNotExist'), false); }); test("should return true if the method exists on the object", function() { equal(canInvoke(obj, 'aMethodThatExists'), true); }); test("should return false if the method doesn't exist on the object", function() { equal(canInvoke(obj, 'aMethodThatDoesNotExist'), false); }); test("should return false if the property exists on the object but is a non-function", function() { equal(canInvoke(obj, 'foobar'), false); }); }); define("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.'); }); }); define("ember-metal/tests/utils/generate_guid_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var generateGuid = __dependency1__.generateGuid; QUnit.module("Ember.generateGuid"); test("Prefix", function() { var a = {}; ok( generateGuid(a, 'tyrell').indexOf('tyrell') > -1, "guid can be prefixed" ); }); }); define("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.'); }); }); define("ember-metal/tests/utils/guidFor_test", ["ember-metal/utils","ember-metal/watching"], function(__dependency1__, __dependency2__) { "use strict"; var guidFor = __dependency1__.guidFor; var generateGuid = __dependency1__.generateGuid; var rewatch = __dependency2__.rewatch; QUnit.module("guidFor"); var sameGuid = function(a, b, message) { equal( guidFor(a), guidFor(b), message ); }; var diffGuid = function(a, b, message) { ok( guidFor(a) !== guidFor(b), message); }; var nanGuid = function(obj) { var type = typeof obj; ok( isNaN(parseInt(guidFor(obj), 0)), "guids for " + type + "don't parse to numbers"); }; test("Object", function() { var a = {}, b = {}; sameGuid( a, a, "same object always yields same guid" ); diffGuid( a, b, "different objects yield different guids" ); nanGuid( a ); }); test("Object with prototype", function() { var Class = function() { }; guidFor(Class.prototype); var a = new Class(); var b = new Class(); sameGuid( a, b , "without calling rewatch, objects copy the guid from their prototype"); rewatch(a); rewatch(b); diffGuid( a, b, "after calling rewatch, objects don't share guids" ); }); test("strings", function() { var a = "string A", aprime = "string A", 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 ); }); test("numbers", function() { var a = 23, aprime = 23, 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 ); }); test("numbers", function() { var a = true, aprime = true, 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 ); }); test("null and undefined", function() { var a = null, aprime = null, 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 ); }); test("arrays", function() { var a = ["a", "b", "c"], aprime = ["a", "b", "c"], 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 ); }); }); define("ember-metal/tests/utils/guidFor_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/utils'); test('ember-metal/tests/utils/guidFor_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/utils/guidFor_test.js should pass jshint.'); }); }); define("ember-metal/tests/utils/is_array_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var isArray = __dependency1__.isArray; QUnit.module("Ember Type Checking"); var global = this; test("Ember.isArray" ,function() { var numarray = [1,2,3], number = 23, strarray = ["Hello", "Hi"], string = "Hello", object = {}, length = {length: 12}, fn = function() {}; equal( isArray(numarray), true, "[1,2,3]" ); equal( isArray(number), false, "23" ); equal( isArray(strarray), true, '["Hello", "Hi"]' ); equal( isArray(string), false, '"Hello"' ); equal( isArray(object), false, "{}" ); equal( isArray(length), true, "{length: 12}" ); equal( isArray(global), false, "global" ); equal( isArray(fn), false, "function() {}" ); }); }); define("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.'); }); }); define("ember-metal/tests/utils/meta_test", ["ember-metal/core","ember-metal/platform","ember-metal/utils"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; /*global jQuery*/ var Ember = __dependency1__["default"]; var create = __dependency2__.create; var platform = __dependency2__.platform; var getMeta = __dependency3__.getMeta; var setMeta = __dependency3__.setMeta; var meta = __dependency3__.meta; var metaPath = __dependency3__.metaPath; QUnit.module("Ember.meta"); test("should return the same hash for an object", function() { var obj = {}; meta(obj).foo = "bar"; equal(meta(obj).foo, "bar", "returns same hash with multiple calls to Ember.meta()"); }); QUnit.module("Ember.metaPath"); test("should not create nested objects if writable is false", function() { var obj = {}; ok(!meta(obj).foo, "precond - foo property on meta does not yet exist"); expectDeprecation(function(){ equal(metaPath(obj, ['foo', 'bar', 'baz'], false), undefined, "should return undefined when writable is false and doesn't already exist"); }); equal(meta(obj).foo, undefined, "foo property is not created"); }); test("should create nested objects if writable is true", function() { var obj = {}; ok(!meta(obj).foo, "precond - foo property on meta does not yet exist"); expectDeprecation(function(){ equal(typeof metaPath(obj, ['foo', 'bar', 'baz'], true), "object", "should return hash when writable is true and doesn't already exist"); }); ok(meta(obj).foo.bar.baz['bat'] = true, "can set a property on the newly created hash"); }); test("getMeta and setMeta", function() { var obj = {}; ok(!getMeta(obj, 'foo'), "precond - foo property on meta does not yet exist"); setMeta(obj, 'foo', "bar"); equal(getMeta(obj, 'foo'), "bar", "foo property on meta now exists"); }); QUnit.module("Ember.meta enumerable"); // Tests fix for https://github.com/emberjs/ember.js/issues/344 // This is primarily for older browsers such as IE8 if (platform.defineProperty.isSimulated) { if (Ember.imports.jQuery) { test("meta is not jQuery.isPlainObject", function () { var proto, obj; proto = {foo: 'bar'}; equal(jQuery.isPlainObject(meta(proto)), false, 'meta should not be isPlainObject when meta property cannot be marked as enumerable: false'); obj = create(proto); equal(jQuery.isPlainObject(meta(obj)), false, 'meta should not be isPlainObject when meta property cannot be marked as enumerable: false'); }); } } else { test("meta is not enumerable", function () { var proto, obj, props, prop; proto = {foo: 'bar'}; meta(proto); obj = create(proto); 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'); } } }); } }); define("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.'); }); }); define("ember-metal/tests/utils/try_catch_finally_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var tryCatchFinally = __dependency1__.tryCatchFinally; 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 { tryCatchFinally(tryable, catchable, finalizer); } catch(e) { errorWasThrown = true; equal(e, error, 'correct error was thrown'); } equal(errorWasThrown, true, 'error was thrown'); } test("no failure", function() { equal(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'); }); test("no failure, return from finally", function() { finalizerResult = 'finalizer return value'; equal(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'); }); test("try failed", function() { tryable = function() { tryCount++; throw error; }; var result = 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'); }); test("catch failed", function() { catchable = function() { catchCount++; throw error; }; 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'); }); 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'); }); 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'); }); 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'); }); 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'); }); }); define("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.'); }); }); define("ember-metal/tests/utils/try_finally_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var tryFinally = __dependency1__.tryFinally; 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 { tryFinally(tryable, finalizer); } catch(e) { errorWasThrown = true; equal(e, error, 'correct error was thrown'); } equal(errorWasThrown, true, 'error was thrown'); } test("no failure", function() { equal(tryFinally(tryable, finalizer), tryableResult, 'correct return value'); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); test("no failure, return from finally", function() { finalizerResult = 'finalizer return value'; equal(tryFinally(tryable, finalizer), finalizerResult, 'crrect return value'); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); test("try failed", function() { tryable = function() { tryCount++; throw error; }; callTryFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); test("finally failed", function() { finalizer = function() { finalizeCount++; throw error; }; callTryFinallyWithError(); equal(tryCount, 1, 'tryable was called once'); equal(finalizeCount, 1, 'finalize was called once'); }); 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'); }); }); define("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.'); }); }); define("ember-metal/tests/utils/try_invoke_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var tryInvoke = __dependency1__.tryInvoke; var obj; QUnit.module("Ember.tryInvoke", { setup: function() { obj = { aMethodThatExists: function() { return true; }, aMethodThatTakesArguments: function(arg1, arg2) { return arg1 === arg2; } }; }, teardown: function() { obj = undefined; } }); test("should return undefined when the object doesn't exist", function() { equal(tryInvoke(undefined, 'aMethodThatDoesNotExist'), undefined); }); test("should return undefined when asked to perform a method that doesn't exist on the object", function() { equal(tryInvoke(obj, 'aMethodThatDoesNotExist'), undefined); }); test("should return what the method returns when asked to perform a method that exists on the object", function() { equal(tryInvoke(obj, 'aMethodThatExists'), true); }); test("should return what the method returns when asked to perform a method that takes arguments and exists on the object", function() { equal(tryInvoke(obj, 'aMethodThatTakesArguments', [true, true]), true); }); }); define("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.'); }); }); define("ember-metal/tests/utils/type_of_test", ["ember-metal/utils"], function(__dependency1__) { "use strict"; var typeOf = __dependency1__.typeOf; QUnit.module("Ember Type Checking"); test("Ember.typeOf", function() { var MockedDate = function() { }; MockedDate.prototype = new Date(); var mockedDate = new MockedDate(), date = new Date(), error = new Error('boum'), object = {a: 'b'}; equal( typeOf(), 'undefined', "undefined"); equal( typeOf(null), 'null', "null"); equal( typeOf('Cyril'), 'string', "Cyril"); equal( typeOf(101), 'number', "101"); equal( typeOf(true), 'boolean', "true"); equal( typeOf([1,2,90]), 'array', "[1,2,90]"); equal( typeOf(/abc/), 'regexp', "/abc/"); equal( typeOf(date), 'date', "new Date()"); equal( typeOf(mockedDate), 'date', "mocked date"); equal( typeOf(error), 'error', "error"); equal( typeOf(object), 'object', "object"); if(Ember.Object) { var klass = Ember.Object.extend(), instance = Ember.Object.create(); equal( Ember.typeOf(klass), 'class', "class"); equal( Ember.typeOf(instance), 'instance', "instance"); } }); }); define("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.'); }); }); define("ember-metal/tests/watching/isWatching_test", ["ember-metal/property_get","ember-metal/properties","ember-metal/mixin","ember-metal/observer","ember-metal/watching"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var get = __dependency1__.get; var defineProperty = __dependency2__.defineProperty; var Mixin = __dependency3__.Mixin; var observer = __dependency3__.observer; var addObserver = __dependency4__.addObserver; var removeObserver = __dependency4__.removeObserver; var isWatching = __dependency5__.isWatching; QUnit.module('isWatching'); function testObserver(setup, teardown, key) { var obj = {}, fn = function() {}; key = key || 'foo'; equal(isWatching(obj, key), false, "precond - isWatching is false by default"); setup(obj, key, fn); equal(isWatching(obj, key), true, "isWatching is true when observers are added"); teardown(obj, key, fn); equal(isWatching(obj, key), false, "isWatching is false after observers are removed"); } test("isWatching is true for regular local observers", function() { testObserver(function(obj, key, fn) { Mixin.create({ didChange: observer(key, fn) }).apply(obj); }, function(obj, key, fn) { removeObserver(obj, key, obj, fn); }); }); test("isWatching is true for nonlocal observers", function() { testObserver(function(obj, key, fn) { addObserver(obj, key, obj, fn); }, function(obj, key, fn) { removeObserver(obj, key, obj, fn); }); }); test("isWatching is true for chained observers", function() { testObserver(function(obj, key, fn) { addObserver(obj, key + '.bar', obj, fn); }, function(obj, key, fn) { removeObserver(obj, key + '.bar', obj, fn); }); }); test("isWatching is true for computed properties", function() { testObserver(function(obj, key, fn) { defineProperty(obj, 'computed', Ember.computed(fn).property(key)); get(obj, 'computed'); }, function(obj, key, fn) { defineProperty(obj, 'computed', null); }); }); test("isWatching is true for chained computed properties", function() { testObserver(function(obj, key, fn) { defineProperty(obj, 'computed', Ember.computed(fn).property(key + '.bar')); get(obj, 'computed'); }, function(obj, key, fn) { 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 test("isWatching is true for 'length' property on object", function() { testObserver(function(obj, key, fn) { defineProperty(obj, 'length', null, '26.2 miles'); addObserver(obj, 'length', obj, fn); }, function(obj, key, fn) { removeObserver(obj, 'length', obj, fn); }, 'length'); }); }); define("ember-metal/tests/watching/isWatching_test.jshint", [], function() { "use strict"; module('JSHint - ember-metal/tests/watching'); test('ember-metal/tests/watching/isWatching_test.js should pass jshint', function() { ok(true, 'ember-metal/tests/watching/isWatching_test.js should pass jshint.'); }); }); define("ember-metal/tests/watching/unwatch_test", ["ember-metal/tests/props_helper","ember-metal/watching","ember-metal/property_events","ember-metal/properties","ember-metal/events","ember-metal/computed","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var testBoth = __dependency1__["default"]; var watch = __dependency2__.watch; var unwatch = __dependency2__.unwatch; var propertyWillChange = __dependency3__.propertyWillChange; var propertyDidChange = __dependency3__.propertyDidChange; var defineProperty = __dependency4__.defineProperty; var addListener = __dependency5__.addListener; var computed = __dependency6__.computed; var get = __dependency7__.get; var set = __dependency8__.set; var willCount, didCount; QUnit.module('unwatch', { setup: function() { willCount = didCount = 0; } }); function addListeners(obj, keyPath) { addListener(obj, keyPath + ':before', function() { willCount++; }); addListener(obj, keyPath + ':change', function() { didCount++; }); } testBoth('unwatching a computed property - regular get/set', function(get, set) { var obj = {}; defineProperty(obj, 'foo', computed(function(keyName, value) { if (value !== undefined) this.__foo = value; return this.__foo; })); addListeners(obj, 'foo'); watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); 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'); }); testBoth('unwatching a regular property - regular get/set', function(get, set) { var obj = { foo: 'BIFF' }; addListeners(obj, 'foo'); watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); 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'); }); test('unwatching should be nested', function() { var obj = { foo: 'BIFF' }; addListeners(obj, 'foo'); watch(obj, 'foo'); watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); unwatch(obj, 'foo'); willCount = didCount = 0; set(obj, 'foo', 'BAZ'); equal(willCount, 1, 'should NOT have invoked willCount'); equal(didCount, 1, 'should NOT have invoked didCount'); 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'); }); testBoth('unwatching "length" property on an object', function(get, set) { var obj = { foo: 'RUN' }; addListeners(obj, 'length'); // Can watch length when it is undefined 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') 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'); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var testBoth = __dependency2__["default"]; var indexOf = __dependency3__.indexOf; var addListener = __dependency4__.addListener; var watch = __dependency5__.watch; var unwatch = __dependency5__.unwatch; var destroy = __dependency5__.destroy; var willCount, didCount, willKeys, didKeys, originalLookup, lookup, Global; QUnit.module('watch', { setup: function() { willCount = didCount = 0; willKeys = []; didKeys = []; originalLookup = Ember.lookup; Ember.lookup = lookup = {}; }, teardown: function(){ Ember.lookup = originalLookup; } }); function addListeners(obj, keyPath) { addListener(obj, keyPath + ':before', function() { willCount++; willKeys.push(keyPath); }); addListener(obj, keyPath + ':change', function() { didCount++; didKeys.push(keyPath); }); } testBoth('watching a computed property', function(get, set) { var obj = {}; Ember.defineProperty(obj, 'foo', Ember.computed(function(keyName, value) { if (value !== undefined) this.__foo = value; return this.__foo; })); addListeners(obj, 'foo'); watch(obj, 'foo'); set(obj, 'foo', 'bar'); equal(willCount, 1, 'should have invoked willCount'); equal(didCount, 1, 'should have invoked didCount'); }); testBoth('watching a regular defined property', function(get, set) { var obj = { foo: 'baz' }; addListeners(obj, 'foo'); 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'); }); testBoth('watching a regular undefined property', function(get, set) { var obj = { }; addListeners(obj, 'foo'); 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'); }); testBoth('watches should inherit', function(get, set) { var obj = { foo: 'baz' }; var objB = Ember.create(obj); addListeners(obj, 'foo'); 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'); }); test("watching an object THEN defining it should work also", function() { var obj = {}; addListeners(obj, 'foo'); watch(obj, 'foo'); Ember.defineProperty(obj, 'foo'); Ember.set(obj, 'foo', 'bar'); equal(Ember.get(obj, 'foo'), 'bar', 'should have set'); equal(willCount, 1, 'should have invoked willChange once'); equal(didCount, 1, 'should have invoked didChange once'); }); test("watching a chain then defining the property", function () { var obj = {}; var foo = {bar: 'bar'}; addListeners(obj, 'foo.bar'); addListeners(foo, 'bar'); watch(obj, 'foo.bar'); Ember.defineProperty(obj, 'foo', undefined, foo); Ember.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'); }); 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'); watch(obj, 'foo.bar.baz'); Ember.defineProperty(bar, 'bar', undefined, baz); Ember.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'); }); 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'); watch(obj, 'foo.bar.baz.biff'); var foo = Ember.get(obj, 'foo'); equal(get(get(get(foo, 'bar'), 'baz'), 'biff'), 'BIFF', 'biff should exist'); unwatch(obj, 'foo.bar.baz.biff'); equal(get(get(get(foo, 'bar'), 'baz'), 'biff'), 'BIFF', 'biff should exist'); }); 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'); 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'); 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 }); 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'); watch(obj, 'Global.foo'); var meta_Global = Ember.meta(Global); var chainNode = Ember.meta(obj).chains._chains.Global._chains.foo; var index = 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'); destroy(obj); index = 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 }); 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'); watch(objA, 'b.foo'); var meta_objB = Ember.meta(objB); var chainNode = Ember.meta(objA).chains._chains.b._chains.foo; var index = 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'); destroy(objA); index = 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 testBoth('watching "length" property on an object', function(get, set) { var obj = { length: '26.2 miles' }; addListeners(obj, 'length'); 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'); }); testBoth('watching "length" property on an array', function(get, set) { var arr = []; addListeners(arr, 'length'); 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'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-routing-handlebars.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-routing-handlebars.js should pass jshint', function() { ok(true, 'ember-routing-handlebars.js should pass jshint.'); }); }); define("ember-routing-handlebars/helpers/action.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/helpers'); test('ember-routing-handlebars/helpers/action.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/helpers/action.js should pass jshint.'); }); }); define("ember-routing-handlebars/helpers/link_to.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/helpers'); test('ember-routing-handlebars/helpers/link_to.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/helpers/link_to.js should pass jshint.'); }); }); define("ember-routing-handlebars/helpers/outlet.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/helpers'); test('ember-routing-handlebars/helpers/outlet.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/helpers/outlet.js should pass jshint.'); }); }); define("ember-routing-handlebars/helpers/render.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/helpers'); test('ember-routing-handlebars/helpers/render.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/helpers/render.js should pass jshint.'); }); }); define("ember-routing-handlebars/helpers/shared.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/helpers'); test('ember-routing-handlebars/helpers/shared.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/helpers/shared.js should pass jshint.'); }); }); define("ember-routing-handlebars/tests/helpers/action_test", ["ember-metal/core","ember-metal/property_set","ember-metal/run_loop","ember-views/system/event_dispatcher","ember-runtime/system/container","ember-runtime/system/object","ember-runtime/controllers/controller","ember-runtime/controllers/object_controller","ember-runtime/controllers/array_controller","ember-handlebars","ember-views/views/view","ember-views/views/component","ember-views/system/jquery","ember-routing-handlebars/helpers/shared","ember-routing-handlebars/helpers/action"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__) { "use strict"; var Ember = __dependency1__["default"]; // A, FEATURES, assert, TESTING_DEPRECATION var set = __dependency2__.set; var run = __dependency3__["default"]; var EventDispatcher = __dependency4__["default"]; var Container = __dependency5__["default"]; var EmberObject = __dependency6__["default"]; var EmberController = __dependency7__["default"]; var EmberObjectController = __dependency8__["default"]; var EmberArrayController = __dependency9__["default"]; var EmberHandlebars = __dependency10__["default"]; var EmberView = __dependency11__["default"]; var EmberComponent = __dependency12__["default"]; var jQuery = __dependency13__["default"]; var ActionHelper = __dependency15__.ActionHelper; var actionHelper = __dependency15__.actionHelper; var dispatcher, view, originalActionHelper, originalRegisterAction = ActionHelper.registerAction; var appendView = function() { run(function() { view.appendTo('#qunit-fixture'); }); }; QUnit.module("Ember.Handlebars - action helper", { setup: function() { originalActionHelper = EmberHandlebars.helpers['action']; EmberHandlebars.registerHelper('action', actionHelper); dispatcher = EventDispatcher.create(); dispatcher.setup(); }, teardown: function() { run(function() { dispatcher.destroy(); if (view) { view.destroy(); } }); delete EmberHandlebars.helpers['action']; EmberHandlebars.helpers['action'] = originalActionHelper; Ember.TESTING_DEPRECATION = false; } }); test("should output a data attribute with a guid", function() { view = EmberView.create({ template: EmberHandlebars.compile('edit') }); appendView(); ok(view.$('a').attr('data-ember-action').match(/\d+/), "A data-ember-action attribute with a guid was added"); }); test("should by default register a click event", function() { var registeredEventName; ActionHelper.registerAction = function(actionName, options) { registeredEventName = options.eventName; }; view = EmberView.create({ template: EmberHandlebars.compile('edit') }); appendView(); equal(registeredEventName, 'click', "The click event was properly registered"); ActionHelper.registerAction = originalRegisterAction; }); test("should allow alternative events to be handled", function() { var registeredEventName; ActionHelper.registerAction = function(actionName, options) { registeredEventName = options.eventName; }; view = EmberView.create({ template: EmberHandlebars.compile('edit') }); appendView(); equal(registeredEventName, 'mouseUp', "The alternative mouseUp event was properly registered"); ActionHelper.registerAction = originalRegisterAction; }); test("should by default target the view's controller", function() { var registeredTarget, controller = {}; ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target; }; view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('edit') }); appendView(); equal(registeredTarget.root, controller, "The controller was registered as the target"); ActionHelper.registerAction = originalRegisterAction; }); test("Inside a yield, the target points at the original target", function() { var controller = {}, watted = false; var component = EmberComponent.extend({ boundText: "inner", truthy: true, obj: {}, layout: EmberHandlebars.compile("

    {{boundText}}

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

    ") }); view = EmberView.create({ controller: { boundText: "outer", truthy: true, wat: function() { watted = true; }, obj: { component: component, truthy: true, boundText: 'insideWith' } }, template: EmberHandlebars.compile('{{#with obj}}{{#if truthy}}{{#view component}}{{#if truthy}}

    {{boundText}}

    {{/if}}{{/view}}{{/if}}{{/with}}') }); appendView(); run(function() { view.$(".wat").click(); }); equal(watted, true, "The action was called on the right context"); }); test("should target the current controller inside an {{each}} loop", function() { var registeredTarget; ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target; }; var itemController = EmberObjectController.create(); var ArrayController = EmberArrayController.extend({ itemController: 'stub', controllerAt: function(idx, object) { return itemController; } }); var controller = ArrayController.create({ model: Ember.A([1]) }); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('{{#each controller}}{{action "editTodo"}}{{/each}}') }); appendView(); equal(registeredTarget.root, itemController, "the item controller is the target of action"); ActionHelper.registerAction = originalRegisterAction; }); test("should target the with-controller inside an {{#with controller='person'}}", function() { var registeredTarget; ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target; }; var PersonController = EmberObjectController.extend(); var container = new Container(); var parentController = EmberObject.create({ container: container }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#with view.person controller="person"}}{{action "editTodo"}}{{/with}}'), person: EmberObject.create(), controller: parentController }); container.register('controller:person', PersonController); appendView(); ok(registeredTarget.root instanceof PersonController, "the with-controller is the target of action"); ActionHelper.registerAction = originalRegisterAction; }); test("should target the with-controller inside an {{each}} in a {{#with controller='person'}}", function() { var eventsCalled = []; var PeopleController = EmberArrayController.extend({ actions: { robert: function() { eventsCalled.push('robert'); }, brian: function() { eventsCalled.push('brian'); } } }); var container = new Container(); var parentController = EmberObject.create({ container: container, people: Ember.A([ {name: 'robert'}, {name: 'brian'} ]) }); view = EmberView.create({ container: container, template: EmberHandlebars.compile('{{#with people controller="people"}}{{#each}}{{name}}{{/each}}{{/with}}'), controller: parentController }); container.register('controller:people', PeopleController); appendView(); view.$('a').trigger('click'); deepEqual(eventsCalled, ['robert', 'brian'], 'the events are fired properly'); }); test("should allow a target to be specified", function() { var registeredTarget; ActionHelper.registerAction = function(actionName, options) { registeredTarget = options.target; }; var anotherTarget = EmberView.create(); view = EmberView.create({ controller: {}, template: EmberHandlebars.compile('edit'), anotherTarget: anotherTarget }); appendView(); equal(registeredTarget.options.data.keywords.view, view, "The specified target was registered"); equal(registeredTarget.target, 'view.anotherTarget', "The specified target was registered"); ActionHelper.registerAction = originalRegisterAction; run(function() { anotherTarget.destroy(); }); }); test("should lazily evaluate the target", function() { var firstEdit = 0, secondEdit = 0; var controller = {}; var first = { edit: function() { firstEdit++; } }; var second = { edit: function() { secondEdit++; } }; controller.theTarget = first; view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('edit') }); appendView(); run(function() { jQuery('a').trigger('click'); }); equal(firstEdit, 1); set(controller, 'theTarget', second); run(function() { jQuery('a').trigger('click'); }); equal(firstEdit, 1); equal(secondEdit, 1); }); test("should register an event handler", function() { var eventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('click me') }); appendView(); var actionId = view.$('a[data-ember-action]').attr('data-ember-action'); ok(ActionHelper.registeredActions[actionId], "The action was registered"); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); test("handles whitelisted modifier keys", function() { var eventHandlerWasCalled = false, shortcutHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; }, shortcut: function() { shortcutHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('click me
    click me too
    ') }); appendView(); var actionId = view.$('a[data-ember-action]').attr('data-ember-action'); ok(ActionHelper.registeredActions[actionId], "The action was registered"); var e = jQuery.Event('click'); e.altKey = true; view.$('a').trigger(e); ok(eventHandlerWasCalled, "The event handler was called"); e = jQuery.Event('click'); e.ctrlKey = true; view.$('div').trigger(e); ok(shortcutHandlerWasCalled, "The \"any\" shortcut's event handler was called"); }); test("should be able to use action more than once for the same event within a view", function() { var editWasCalled = false, deleteWasCalled = false, originalEventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { editWasCalled = true; }, "delete": function() { deleteWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile( 'editdelete' ), click: function() { originalEventHandlerWasCalled = true; } }); appendView(); 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"); }); test("the event should not bubble if `bubbles=false` is passed", function() { var editWasCalled = false, deleteWasCalled = false, originalEventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { editWasCalled = true; }, "delete": function() { deleteWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile( 'editdelete' ), click: function() { originalEventHandlerWasCalled = true; } }); appendView(); 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"); }); test("should work properly in an #each block", function() { var eventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, items: Ember.A([1, 2, 3, 4]), template: EmberHandlebars.compile('{{#each view.items}}click me{{/each}}') }); appendView(); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); test("should work properly in a #with block", function() { var eventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, something: {ohai: 'there'}, template: EmberHandlebars.compile('{{#with view.something}}click me{{/with}}') }); appendView(); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The event handler was called"); }); test("should unregister event handlers on rerender", function() { var eventHandlerWasCalled = false; view = EmberView.extend({ template: EmberHandlebars.compile('click me'), actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); appendView(); var previousActionId = view.$('a[data-ember-action]').attr('data-ember-action'); run(function() { view.rerender(); }); ok(!ActionHelper.registeredActions[previousActionId], "On rerender, the event handler was removed"); var newActionId = view.$('a[data-ember-action]').attr('data-ember-action'); ok(ActionHelper.registeredActions[newActionId], "After rerender completes, a new event handler was added"); }); test("should unregister event handlers on inside virtual views", function() { var things = Ember.A([ { name: 'Thingy' } ]); view = EmberView.create({ template: EmberHandlebars.compile('{{#each view.things}}click me{{/each}}'), things: things }); appendView(); var actionId = view.$('a[data-ember-action]').attr('data-ember-action'); run(function() { things.removeAt(0); }); ok(!ActionHelper.registeredActions[actionId], "After the virtual view was destroyed, the action was unregistered"); }); test("should properly capture events on child elements of a container with an action", function() { var eventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('
    ') }); appendView(); view.$('button').trigger('click'); ok(eventHandlerWasCalled, "Event on a child element triggered the action of it's parent"); }); test("should allow bubbling of events from action helper to original parent event", function() { var eventHandlerWasCalled = false, originalEventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('click me'), click: function() { originalEventHandlerWasCalled = true; } }); appendView(); view.$('a').trigger('click'); ok(eventHandlerWasCalled && originalEventHandlerWasCalled, "Both event handlers were called"); }); test("should not bubble an event from action helper to original parent event if `bubbles=false` is passed", function() { var eventHandlerWasCalled = false, originalEventHandlerWasCalled = false; var controller = EmberController.extend({ actions: { edit: function() { eventHandlerWasCalled = true; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('click me'), click: function() { originalEventHandlerWasCalled = true; } }); appendView(); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The child handler was called"); ok(!originalEventHandlerWasCalled, "The parent handler was not called"); }); test("should allow 'send' as action name (#594)", function() { var eventHandlerWasCalled = false; var eventObjectSent; var controller = EmberController.extend({ send: function() { eventHandlerWasCalled = true; } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('send') }); appendView(); view.$('a').trigger('click'); ok(eventHandlerWasCalled, "The view's send method was called"); }); test("should send the view, event and current Handlebars context to the action", function() { var passedTarget; var passedContext; var aTarget = EmberController.extend({ actions: { edit: function(context) { passedTarget = this; passedContext = context; } } }).create(); var aContext = { aTarget: aTarget }; view = EmberView.create({ aContext: aContext, template: EmberHandlebars.compile('{{#with view.aContext}}edit{{/with}}') }); appendView(); view.$('#edit').trigger('click'); strictEqual(passedTarget, aTarget, "the action is called with the target as this"); strictEqual(passedContext, aContext, "the parameter is passed along"); }); test("should only trigger actions for the event they were registered on", function() { var editWasCalled = false; view = EmberView.extend({ template: EmberHandlebars.compile('edit'), actions: { edit: function() { editWasCalled = true; } } }).create(); appendView(); view.$('a').trigger('mouseover'); ok(!editWasCalled, "The action wasn't called"); }); test("should unwrap controllers passed as a context", function() { var passedContext, model = EmberObject.create(), controller = EmberObjectController.extend({ model: model, actions: { edit: function(context) { passedContext = context; } } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('') }); appendView(); view.$('button').trigger('click'); equal(passedContext, model, "the action was passed the unwrapped model"); }); test("should allow multiple contexts to be specified", function() { var passedContexts, models = [EmberObject.create(), EmberObject.create()]; var controller = EmberController.extend({ actions: { edit: function() { passedContexts = [].slice.call(arguments); } } }).create(); view = EmberView.create({ controller: controller, modelA: models[0], modelB: models[1], template: EmberHandlebars.compile('') }); appendView(); view.$('button').trigger('click'); deepEqual(passedContexts, models, "the action was called with the passed contexts"); }); test("should allow multiple contexts to be specified mixed with string args", function() { var passedParams, model = EmberObject.create(); var controller = EmberController.extend({ actions: { edit: function() { passedParams = [].slice.call(arguments); } } }).create(); view = EmberView.create({ controller: controller, modelA: model, template: EmberHandlebars.compile('') }); appendView(); view.$('button').trigger('click'); deepEqual(passedParams, ["herp", model], "the action was called with the passed contexts"); }); var namespace = { "Component": { toString: function() { return "Component"; }, find: function() { return { id: 1 }; } } }; var compile = function(string) { return EmberHandlebars.compile(string); }; test("it does not trigger action with special clicks", function() { var showCalled = false; view = EmberView.create({ template: compile("Hi") }); var controller = EmberController.extend({ actions: { show: function() { showCalled = true; } } }).create(); run(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); function checkClick(prop, value, expected) { var event = jQuery.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 }); test("it can trigger actions for keyboard events", function() { var showCalled = false; view = EmberView.create({ template: compile("") }); var controller = EmberController.extend({ actions: { show: function() { showCalled = true; } } }).create(); run(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var event = jQuery.Event("keyup"); event["char"] = 'a'; event.which = 65; view.$('input').trigger(event); ok(showCalled, "should call action with keyup"); }); test("a quoteless parameter should allow dynamic lookup of the actionName", function(){ expect(4); var lastAction, actionOrder = []; view = EmberView.create({ template: compile("Hi") }); var controller = EmberController.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(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var testBoundAction = function(propertyValue){ controller.set('hookMeUp', propertyValue); run(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'); }); test("a quoteless parameter should lookup actionName in context", function(){ expect(4); var lastAction, actionOrder = []; view = EmberView.create({ template: compile("{{#each allactions}}{{title}}{{/each}}") }); var controller = EmberController.extend({ allactions: Ember.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(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var testBoundAction = function(propertyValue){ run(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'); }); test("a quoteless parameter should resolve actionName, including path", function(){ expect(4); var lastAction, actionOrder = []; view = EmberView.create({ template: compile("{{#each item in allactions}}{{item.title}}{{/each}}") }); var controller = EmberController.extend({ allactions: Ember.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(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var testBoundAction = function(propertyValue){ run(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'); }); test("a quoteless parameter that also exists as an action name functions properly", function(){ Ember.TESTING_DEPRECATION = true; var triggeredAction; view = EmberView.create({ template: compile("Hi") }); var controller = EmberController.extend({ actions: { ohNoeNotValid: function() { triggeredAction = true; } } }).create(); run(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); run(function(){ view.$("#oops-bound-param").click(); }); ok(triggeredAction, 'the action was triggered'); }); test("a quoteless parameter that also exists as an action name results in an assertion", function(){ var triggeredAction; view = EmberView.create({ template: compile("Hi") }); var controller = EmberController.extend({ actions: { ohNoeNotValid: function() { triggeredAction = true; } } }).create(); run(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var oldAssert = Ember.assert; Ember.assert = function(message, test){ ok(test, message + " -- was properly asserted"); }; run(function(){ view.$("#oops-bound-param").click(); }); ok(triggeredAction, 'the action was triggered'); Ember.assert = oldAssert; }); test("a quoteless parameter that also exists as an action name in deprecated action in controller style results in an assertion", function(){ var dropDeprecatedActionStyleOrig = Ember.FEATURES['ember-routing-drop-deprecated-action-style']; Ember.FEATURES['ember-routing-drop-deprecated-action-style'] = false; var triggeredAction; view = EmberView.create({ template: compile("Hi") }); var controller = EmberController.extend({ ohNoeNotValid: function() { triggeredAction = true; } }).create(); run(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var oldAssert = Ember.assert; Ember.assert = function(message, test){ ok(test, message + " -- was properly asserted"); }; run(function(){ view.$("#oops-bound-param").click(); }); ok(triggeredAction, 'the action was triggered'); Ember.assert = oldAssert; Ember.FEATURES['ember-routing-drop-deprecated-action-style'] = dropDeprecatedActionStyleOrig; }); QUnit.module("Ember.Handlebars - action helper - deprecated invoking directly on target", { setup: function() { originalActionHelper = EmberHandlebars.helpers['action']; EmberHandlebars.registerHelper('action', actionHelper); dispatcher = EventDispatcher.create(); dispatcher.setup(); }, teardown: function() { delete EmberHandlebars.helpers['action']; EmberHandlebars.helpers['action'] = originalActionHelper; run(function() { dispatcher.destroy(); if (view) { view.destroy(); } }); } }); if (!Ember.FEATURES.isEnabled('ember-routing-drop-deprecated-action-style')) { test("should invoke a handler defined directly on the target (DEPRECATED)", function() { var eventHandlerWasCalled, model = EmberObject.create(); var controller = EmberController.extend({ edit: function() { eventHandlerWasCalled = true; } }).create(); view = EmberView.create({ controller: controller, template: EmberHandlebars.compile('') }); appendView(); expectDeprecation(/Action handlers implemented directly on controllers are deprecated/); view.$('button').trigger('click'); ok(eventHandlerWasCalled, "the action was called"); }); } test("should respect preventDefault=false option if provided", function(){ view = EmberView.create({ template: compile("Hi") }); var controller = EmberController.extend({ actions: { show: function() { } } }).create(); run(function() { view.set('controller', controller); view.appendTo('#qunit-fixture'); }); var event = jQuery.Event("click"); view.$('a').trigger(event); equal(event.isDefaultPrevented(), false, "should not preventDefault"); }); }); define("ember-routing-handlebars/tests/helpers/action_test.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/tests/helpers'); test('ember-routing-handlebars/tests/helpers/action_test.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/tests/helpers/action_test.js should pass jshint.'); }); }); define("ember-routing-handlebars/tests/helpers/link_to_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-handlebars","ember-views/views/view","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var Ember = __dependency1__["default"]; // TEMPLATES var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var EmberHandlebars = __dependency5__["default"]; var EmberView = __dependency6__["default"]; var jQuery = __dependency7__["default"]; var view; var appendView = function(view) { run(function() { view.appendTo('#qunit-fixture'); }); }; var compile = EmberHandlebars.compile; QUnit.module("Handlebars {{link-to}} helper", { setup: function() { }, teardown: function() { run(function() { if (view) { view.destroy(); } }); } }); 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.create({ template: compile(template) }); appendView(view); equal(view.$().text(), 'Go to Index'); }); }); define("ember-routing-handlebars/tests/helpers/link_to_test.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/tests/helpers'); test('ember-routing-handlebars/tests/helpers/link_to_test.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/tests/helpers/link_to_test.js should pass jshint.'); }); }); define("ember-routing-handlebars/tests/helpers/outlet_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","container/container","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/router","ember-routing/location/hash_location","ember-handlebars","ember-handlebars/views/metamorph_view","ember-routing/ext/view","ember-views/views/container_view","ember-views/system/jquery","ember-routing-handlebars/helpers/outlet"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__) { "use strict"; var Ember = __dependency1__["default"]; // TEMPLATES var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var Container = __dependency5__["default"]; var Namespace = __dependency6__["default"]; var decamelize = __dependency7__.decamelize; var classify = __dependency7__.classify; var Controller = __dependency8__["default"]; var ObjectController = __dependency9__["default"]; var ArrayController = __dependency10__["default"]; var EmberRouter = __dependency11__["default"]; var HashLocation = __dependency12__["default"]; var EmberHandlebars = __dependency13__["default"]; var _MetamorphView = __dependency14__._MetamorphView; var EmberView = __dependency15__["default"]; var EmberContainerView = __dependency16__["default"]; var jQuery = __dependency17__["default"]; var outletHelper = __dependency18__.outletHelper; var buildContainer = function(namespace) { var container = new Container(); container.set = set; container.resolver = resolverFor(namespace); container.optionsForType('view', { singleton: false }); container.optionsForType('template', { instantiate: false }); container.register('application:main', namespace, { instantiate: false }); container.injection('router:main', 'namespace', 'application:main'); container.register('location:hash', HashLocation); container.register('controller:basic', Controller, { instantiate: false }); container.register('controller:object', ObjectController, { instantiate: false }); container.register('controller:array', ArrayController, { instantiate: false }); container.typeInjection('route', 'router', 'router:main'); return container; }; function resolverFor(namespace) { return function(fullName) { var nameParts = fullName.split(":"), type = nameParts[0], name = nameParts[1]; if (type === 'template') { var templateName = decamelize(name); if (Ember.TEMPLATES[templateName]) { return Ember.TEMPLATES[templateName]; } } var className = classify(name) + classify(type); var factory = get(namespace, className); if (factory) { return factory; } }; } var appendView = function(view) { run(function() { view.appendTo('#qunit-fixture'); }); }; var compile = EmberHandlebars.compile; var trim = jQuery.trim; var view, container, originalOutletHelper; QUnit.module("Handlebars {{outlet}} helpers", { setup: function() { originalOutletHelper = EmberHandlebars.helpers['outlet']; EmberHandlebars.registerHelper('outlet', outletHelper); var namespace = Namespace.create(); container = buildContainer(namespace); container.register('view:default', _MetamorphView); container.register('router:main', EmberRouter.extend()); }, teardown: function() { delete EmberHandlebars.helpers['outlet']; EmberHandlebars.helpers['outlet'] = originalOutletHelper; run(function () { if (container) { container.destroy(); } if (view) { view.destroy(); } }); } }); test("view should support connectOutlet for the main outlet", function() { var template = "

    HI

    {{outlet}}"; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(view); equal(view.$().text(), 'HI'); run(function() { view.connectOutlet('main', EmberView.create({ template: compile("

    BYE

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

    HI

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

    BYE

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

    HI

    {{outlet mainView}}"; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(view); equal(view.$().text(), 'HI'); run(function() { view.connectOutlet('mainView', EmberView.create({ template: compile("

    BYE

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

    HI

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

    BYE

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

    HI

    {{outlet view=containerView}}"; expectAssertion(function () { view = EmberView.create({ template: EmberHandlebars.compile(template), container : container }); appendView(view); }); }); test("outlet should assert view path is successfully resolved", function() { var template = "

    HI

    {{outlet view='someViewNameHere'}}"; expectAssertion(function () { view = EmberView.create({ template: EmberHandlebars.compile(template), container : container }); appendView(view); }); }); test("outlet should support an optional view class", function() { var template = "

    HI

    {{outlet viewClass=view.outletView}}"; view = EmberView.create({ template: EmberHandlebars.compile(template), outletView: EmberContainerView.extend() }); appendView(view); equal(view.$().text(), 'HI'); var childView = EmberView.create({ template: compile("

    BYE

    ") }); run(function() { view.connectOutlet('main', childView); }); ok(view.outletView.detectInstance(childView.get('_parentView')), "The custom view class should be used for the outlet"); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); 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.create({ template: compile(parentTemplate) }); var middleView = _MetamorphView.create({ template: compile(middleTemplate) }); var bottomView = _MetamorphView.create({ template: compile(bottomTemplate) }); appendView(view); run(function() { view.connectOutlet('main', middleView); }); run(function() { middleView.connectOutlet('main', bottomView); }); var output = jQuery('#qunit-fixture h1 ~ h2 ~ h3').text(); equal(output, "BOTTOM", "all templates were rendered"); }); test("view should support disconnectOutlet for the main outlet", function() { var template = "

    HI

    {{outlet}}"; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(view); equal(view.$().text(), 'HI'); run(function() { view.connectOutlet('main', EmberView.create({ template: compile("

    BYE

    ") })); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); run(function() { view.disconnectOutlet('main'); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HI'); }); test("Outlets bind to the current template's view, not inner contexts", function() { var parentTemplate = "

    HI

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

    BOTTOM

    "; view = EmberView.create({ alwaysTrue: true, template: compile(parentTemplate) }); var bottomView = _MetamorphView.create({ template: compile(bottomTemplate) }); appendView(view); run(function() { view.connectOutlet('main', bottomView); }); var output = jQuery('#qunit-fixture h1 ~ h3').text(); equal(output, "BOTTOM", "all templates were rendered"); }); test("should support layouts", function() { var template = "{{outlet}}", layout = "

    HI

    {{yield}}"; view = EmberView.create({ template: EmberHandlebars.compile(template), layout: EmberHandlebars.compile(layout) }); appendView(view); equal(view.$().text(), 'HI'); run(function() { view.connectOutlet('main', EmberView.create({ template: compile("

    BYE

    ") })); }); // Replace whitespace for older IE equal(trim(view.$().text()), 'HIBYE'); }); }); define("ember-routing-handlebars/tests/helpers/outlet_test.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/tests/helpers'); test('ember-routing-handlebars/tests/helpers/outlet_test.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/tests/helpers/outlet_test.js should pass jshint.'); }); }); define("ember-routing-handlebars/tests/helpers/render_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/platform","ember-metal/mixin","container/container","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/router","ember-routing/location/hash_location","ember-handlebars","ember-routing/ext/view","ember-handlebars/views/metamorph_view","ember-views/system/jquery","ember-routing-handlebars/helpers/render","ember-routing-handlebars/helpers/action","ember-routing-handlebars/helpers/outlet"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__) { "use strict"; var Ember = __dependency1__["default"]; // TEMPLATES var get = __dependency2__.get; var emberSet = __dependency3__.set; var run = __dependency4__["default"]; var create = __dependency5__.create; var observer = __dependency6__.observer; var Container = __dependency7__["default"]; var Namespace = __dependency8__["default"]; var classify = __dependency9__.classify; var decamelize = __dependency9__.decamelize; var EmberController = __dependency10__["default"]; var EmberObjectController = __dependency11__["default"]; var EmberArrayController = __dependency12__["default"]; var EmberRouter = __dependency13__["default"]; var HashLocation = __dependency14__["default"]; var EmberHandlebars = __dependency15__["default"]; var EmberView = __dependency16__["default"]; var _MetamorphView = __dependency17__["default"]; var jQuery = __dependency18__["default"]; var renderHelper = __dependency19__["default"]; var ActionHelper = __dependency20__.ActionHelper; var actionHelper = __dependency20__.actionHelper; var outletHelper = __dependency21__.outletHelper; function appendView(view) { run(function() { view.appendTo('#qunit-fixture'); }); } function set(object, key, value) { run(function() { emberSet(object, key, value); }); } function compile(template) { return EmberHandlebars.compile(template); } function buildContainer(namespace) { var container = new Container(); container.set = emberSet; container.resolver = resolverFor(namespace); container.optionsForType('view', { singleton: false }); container.optionsForType('template', { instantiate: false }); container.register('application:main', namespace, { instantiate: false }); container.injection('router:main', 'namespace', 'application:main'); container.register('location:hash', HashLocation); container.register('controller:basic', EmberController, { instantiate: false }); container.register('controller:object', EmberObjectController, { instantiate: false }); container.register('controller:array', EmberArrayController, { instantiate: false }); container.typeInjection('route', 'router', 'router:main'); return container; } function resolverFor(namespace) { return function(fullName) { var nameParts = fullName.split(":"), type = nameParts[0], name = nameParts[1]; if (type === 'template') { var templateName = decamelize(name); if (Ember.TEMPLATES[templateName]) { return Ember.TEMPLATES[templateName]; } } var className = classify(name) + classify(type); var factory = get(namespace, className); if (factory) { return factory; } }; } var view, container, originalRenderHelper, originalActionHelper, originalOutletHelper; QUnit.module("Handlebars {{render}} helper", { setup: function() { originalOutletHelper = EmberHandlebars.helpers['outlet']; EmberHandlebars.registerHelper('outlet', outletHelper); originalRenderHelper = EmberHandlebars.helpers['render']; EmberHandlebars.registerHelper('render', renderHelper); originalActionHelper = EmberHandlebars.helpers['action']; EmberHandlebars.registerHelper('action', actionHelper); var namespace = Namespace.create(); container = buildContainer(namespace); container.register('view:default', _MetamorphView); container.register('router:main', EmberRouter.extend()); }, teardown: function() { delete EmberHandlebars.helpers['render']; EmberHandlebars.helpers['render'] = originalRenderHelper; delete EmberHandlebars.helpers['action']; EmberHandlebars.helpers['action'] = originalActionHelper; delete EmberHandlebars.helpers['outlet']; EmberHandlebars.helpers['outlet'] = originalOutletHelper; run(function () { if (container) { container.destroy(); } if (view) { view.destroy(); } container = view = null; }); Ember.TEMPLATES = {}; } }); test("{{render}} helper should render given template", function() { var template = "

    HI

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

    BYE

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

    HI

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

    HI

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

    HI

    {{render 'oops'}}"; var controller = EmberController.extend({container: container}); view = EmberView.create({ controller: controller.create(), template: compile(template) }); container.register('view:oops', EmberView.extend()); appendView(view); equal(view.$().text(), 'HI'); }); 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 = EmberController.extend({ container: container, post: post }); var controller = Controller.create({ }); view = EmberView.create({ controller: controller, template: compile(template) }); var PostController = EmberObjectController.extend(); container.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile("

    {{title}}

    "); appendView(view); var postController = view.get('_childViews')[0].get('controller'); equal(view.$().text(), 'HIRails is omakase'); equal(postController.get('model'), post); set(controller, 'post', { title: "Rails is unagi" }); equal(view.$().text(), 'HIRails is unagi'); if (create.isSimulated) { equal(postController.get('model').title, "Rails is unagi"); } else { deepEqual(postController.get('model'), { title: "Rails is unagi" }); } }); 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.create({ controller: EmberController.create({ container: container, post: post }), template: compile(template) }); var PostController = EmberObjectController.extend({ modelDidChange: observer('model', function(){ modelDidChange++; }) }); container.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile("

    {{title}}

    "); var modelDidChange = 0; appendView(view); equal(modelDidChange, 0, "model observer did not fire"); }); 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 = EmberController.extend({container: container}); container.register('controller:posts', EmberArrayController.extend()); view = EmberView.create({ controller: controller.create(), template: compile(template) }); Ember.TEMPLATES['home'] = compile("

    BYE

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

    HI

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

    BYE

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

    HI

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

    BYE

    "); expectAssertion(function() { appendView(view); }, /\{\{render\}\} helper once/i); }); 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 = EmberController.extend({ container: container, post1: post1, post2: post2 }); var controller = Controller.create(); view = EmberView.create({ controller: controller, template: compile(template) }); var PostController = EmberObjectController.extend(); container.register('controller:post', PostController, {singleton: false}); Ember.TEMPLATES['post'] = compile("

    {{title}}

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

    HI

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

    {{title}}

    "); appendView(view); var postController1 = view.get('_childViews')[0].get('controller'); run(view, 'destroy'); ok(postController1.isDestroyed, 'expected postController to be destroyed'); }); 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.create({ controller: EmberController.createWithMixins({ container: container, zero: false }), template: compile(template) }); var PostController = EmberObjectController.extend(); container.register('controller:post', PostController, {singleton: false}); Ember.TEMPLATES['post'] = compile("

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

    "); appendView(view); var postController1 = view.get('_childViews')[0].get('controller'); var postController2 = view.get('_childViews')[1].get('controller'); ok(view.$().text().match(/^HI ?NOTHING ?NOTHING$/)); equal(postController1.get('model'), 0); equal(postController2.get('model'), undefined); }); 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 = EmberController.extend({ container: container, post: post }); var controller = Controller.create(); view = EmberView.create({ controller: controller, template: compile(template) }); var PostController = EmberObjectController.extend(); container.register('controller:post', PostController, {singleton: false}); Ember.TEMPLATES['post'] = compile("

    Title:{{title}}

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

    HI

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

    HI

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

    BYE

    "); appendView(view); run(function() { view.connectOutlet('main', EmberView.create({ controller: controller.create(), template: compile("

    1{{render 'home'}}

    ") })); }); equal(view.$().text(), 'HI1BYE'); run(function() { view.connectOutlet('main', EmberView.create({ controller: controller.create(), template: compile("

    2{{render 'home'}}

    ") })); }); equal(view.$().text(), 'HI2BYE'); }); test("{{render}} works with dot notation", function() { var template = '

    BLOG

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

    POST

    "); appendView(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'); }); test("{{render}} works with slash notation", function() { var template = '

    BLOG

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

    POST

    "); appendView(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'); }); test("Using quoteless templateName works properly (DEPRECATED)", function(){ var template = '

    HI

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

    BYE

    "); expectDeprecation("Using a quoteless parameter with {{render}} is deprecated. Please update to quoted usage '{{render \"home\"}}."); appendView(view); equal(view.$('p:contains(BYE)').length, 1, "template was rendered"); }); }); define("ember-routing-handlebars/tests/helpers/render_test.jshint", [], function() { "use strict"; module('JSHint - ember-routing-handlebars/tests/helpers'); test('ember-routing-handlebars/tests/helpers/render_test.js should pass jshint', function() { ok(true, 'ember-routing-handlebars/tests/helpers/render_test.js should pass jshint.'); }); }); define("ember-routing.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-routing.js should pass jshint', function() { ok(true, 'ember-routing.js should pass jshint.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-routing/tests/location/auto_location_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-runtime/copy","ember-runtime/system/object","ember-routing/location/auto_location","ember-routing/location/api"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var copy = __dependency4__["default"]; var EmberObject = __dependency5__["default"]; var AutoLocation = __dependency6__["default"]; var EmberLocation = __dependency7__["default"]; var AutoTestLocation, location, supportsHistory, supportsHashChange, getSupportsHistory = AutoLocation._getSupportsHistory, getSupportsHashChange = AutoLocation._getSupportsHashChange; var FakeHistoryLocation = EmberObject.extend({ implementation: 'history' }); var FakeHashLocation = EmberObject.extend({ implementation: 'hash' }); var FakeNoneLocation = EmberObject.extend({ implementation: 'none' }); function createLocation(options) { if (!options) { options = {}; } location = AutoTestLocation.create(options); } QUnit.module("Ember.AutoLocation", { setup: function() { supportsHistory = supportsHashChange = null; AutoTestLocation = copy(AutoLocation); AutoTestLocation._HistoryLocation = FakeHistoryLocation; AutoTestLocation._HashLocation = FakeHashLocation; AutoTestLocation._NoneLocation = FakeNoneLocation; AutoTestLocation._getSupportsHistory = function () { if (supportsHistory !== null) { return supportsHistory; } else { return getSupportsHistory.call(this); } }; AutoTestLocation._getSupportsHashChange = function () { if (supportsHashChange !== null) { return supportsHashChange; } else { return getSupportsHashChange.call(this); } }; AutoTestLocation._window = { document: {}, navigator: { userAgent: '' } }; AutoTestLocation._location = { href: 'http://test.com/', pathname: '/', hash: '', search: '', replace: function () { ok(false, 'location.replace should not be called'); } }; AutoTestLocation._history = { pushState: function () { ok(false, 'history.pushState should not be called'); }, replaceState: function () { ok(false, 'history.replaceState should not be called'); } }; }, teardown: function() { run(function() { if (location && location.destroy) { location.destroy(); } location = AutoTestLocation = null; }); } }); test("_replacePath cannot be used to redirect to a different origin (website)", function() { expect(1); var expectedURL; AutoTestLocation._location = { protocol: 'http:', hostname: 'emberjs.com', port: '1337', replace: function (url) { equal(url, expectedURL); } }; expectedURL = 'http://emberjs.com:1337//google.com'; AutoTestLocation._replacePath('//google.com'); }); test("AutoLocation.create() should return a HistoryLocation instance when pushStates are supported", function() { expect(2); supportsHistory = true; createLocation(); equal(get(location, 'implementation'), 'history'); equal(location instanceof FakeHistoryLocation, true); }); 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); supportsHistory = false; supportsHashChange = true; AutoTestLocation._location.hash = '#/testd'; createLocation(); equal(get(location, 'implementation'), 'hash'); equal(location instanceof FakeHashLocation, true); }); test("AutoLocation.create() should return a NoneLocation instance when neither history nor hashchange is supported.", function() { expect(2); supportsHistory = false; supportsHashChange = false; AutoTestLocation._location.hash = '#/testd'; createLocation(); equal(get(location, 'implementation'), 'none'); equal(location instanceof FakeNoneLocation, true); }); test("AutoLocation.create() should consider an index path (i.e. '/\') without any location.hash as OK for HashLocation", function() { expect(2); supportsHistory = false; supportsHashChange = true; AutoTestLocation._location = { href: 'http://test.com/', pathname: '/', hash: '', search: '', replace: function (path) { ok(false, 'location.replace should not be called'); } }; createLocation(); equal(get(location, 'implementation'), 'hash'); equal(location instanceof FakeHashLocation, true); }); test("AutoLocation._getSupportsHistory() should use `history.pushState` existance as proof of support", function() { expect(3); AutoTestLocation._history.pushState = function () {}; equal(AutoTestLocation._getSupportsHistory(), true, 'Returns true if `history.pushState` exists'); delete AutoTestLocation._history.pushState; equal(AutoTestLocation._getSupportsHistory(), false, 'Returns false if `history.pushState` does not exist'); AutoTestLocation._history = undefined; equal(AutoTestLocation._getSupportsHistory(), false, 'Returns false if `history` does not exist'); }); test("AutoLocation.create() should transform the URL for hashchange-only browsers viewing a HistoryLocation-formatted path", function() { expect(4); supportsHistory = false; supportsHashChange = true; AutoTestLocation._location = { 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(); equal(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(get(location, 'cancelRouterSetup'), true, 'cancelRouterSetup should be set so the router knows.'); }); test("AutoLocation.create() should transform the URL for pushState-supported browsers viewing a HashLocation-formatted url", function() { expect(4); supportsHistory = true; supportsHashChange = true; AutoTestLocation._location = { hash: '#/test', hostname: 'test.com', href: 'http://test.com/#/test', pathname: '/', protocol: 'http:', port: '', search: '', replace: function (path) { equal(path, 'http://test.com/test', 'location.replace should be called with normalized HistoryLocation url'); } }; createLocation(); equal(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(get(location, 'cancelRouterSetup'), true, 'cancelRouterSetup should be set so the router knows.'); }); test("AutoLocation._getSupportsHistory() should handle false positive for Android 2.2/2.3, returning false", function() { expect(1); var fakeNavigator = { userAgent: 'Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1' }; AutoTestLocation._window.navigator = fakeNavigator; equal(AutoTestLocation._getSupportsHistory(), false); }); test("AutoLocation._getSupportsHashChange() should use `onhashchange` event existance as proof of support", function() { expect(2); AutoTestLocation._window.onhashchange = null; equal(AutoTestLocation._getSupportsHashChange(), true, 'Returns true if `onhashchange` exists'); AutoTestLocation._window = { navigator: window.navigator, document: {} }; equal(AutoTestLocation._getSupportsHashChange(), false, 'Returns false if `onhashchange` does not exist'); }); test("AutoLocation._getSupportsHashChange() should handle false positive for IE8 running in IE7 compatibility mode, returning false", function() { expect(1); AutoTestLocation._window = { onhashchange: null, document: { documentMode: 7 } }; equal(AutoTestLocation._getSupportsHashChange(), false); }); test("AutoLocation._getPath() should normalize location.pathname, making sure it always returns a leading slash", function() { expect(2); AutoTestLocation._location = { pathname: 'test' }; equal(AutoTestLocation._getPath(), '/test', 'When there is no leading slash, one is added.'); AutoTestLocation._location = { pathname: '/test' }; equal(AutoTestLocation._getPath(), '/test', 'When a leading slash is already there, it isn\'t added again'); }); test("AutoLocation._getHash() should be an alias to Ember.Location._getHash, otherwise it needs its own test!", function() { expect(1); equal(AutoTestLocation._getHash, EmberLocation._getHash); }); test("AutoLocation._getQuery() should return location.search as-is", function() { expect(1); AutoTestLocation._location = { search: '?foo=bar' }; equal(AutoTestLocation._getQuery(), '?foo=bar'); }); test("AutoLocation._getFullPath() should return full pathname including query and hash", function() { expect(1); AutoTestLocation._location = { href: 'http://test.com/about?foo=bar#foo', pathname: '/about', search: '?foo=bar', hash: '#foo' }; equal(AutoTestLocation._getFullPath(), '/about?foo=bar#foo'); }); test("AutoLocation._getHistoryPath() should return a normalized, HistoryLocation-supported path", function() { expect(3); AutoTestLocation.rootURL = '/app/'; AutoTestLocation._location = { 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'); AutoTestLocation._location = { 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'); AutoTestLocation._location = { 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'); }); test("AutoLocation._getHashPath() should return a normalized, HashLocation-supported path", function() { expect(3); AutoTestLocation.rootURL = '/app/'; AutoTestLocation._location = { 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'); AutoTestLocation._location = { 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'); AutoTestLocation._location = { 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'); }); test("AutoLocation.create requires any rootURL given to end in a trailing forward slash", function() { expect(3); 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); }); }); define("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.'); }); }); define("ember-routing/tests/location/history_location_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-routing/location/history_location"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var HistoryLocation = __dependency4__["default"]; var FakeHistory, HistoryTestLocation, location, rootURL = window.location.pathname; function createLocation(options){ if (!options) { options = {}; } location = HistoryTestLocation.create(options); } 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.extend({ history: FakeHistory }); }, teardown: function() { run(function() { if (location) { location.destroy(); } }); } }); test("HistoryLocation initState does not get fired on init", function() { expect(1); HistoryTestLocation.reopen({ init: function(){ ok(true, 'init was called'); this._super(); }, initState: function() { ok(false, 'initState() should not be called automatically'); } }); createLocation(); }); test("webkit doesn't fire popstate on page load", function() { expect(1); HistoryTestLocation.reopen({ initState: function() { this._super(); // these two should be equal to be able // to successfully detect webkit initial popstate equal(this._previousURL, this.getURL()); } }); createLocation(); location.initState(); }); test("base URL is removed when retrieving the current pathname", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super(); set(this, 'location', { pathname: '/base/foo/bar' }); set(this, 'baseURL', '/base/'); }, initState: function() { this._super(); equal(this.getURL(), '/foo/bar'); } }); createLocation(); location.initState(); }); test("base URL is preserved when moving around", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super(); set(this, 'location', { pathname: '/base/foo/bar' }); set(this, 'baseURL', '/base/'); } }); createLocation(); location.initState(); location.setURL('/one/two'); equal(FakeHistory.state.path, '/base/one/two'); }); 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(FakeHistory.state && FakeHistory.state.path, '/three/four'); }); 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(FakeHistory.state && FakeHistory.state.path, '/three/four'); }); test("HistoryLocation.getURL() returns the current url, excluding both rootURL and baseURL", function() { expect(1); HistoryTestLocation.reopen({ init: function() { this._super(); set(this, 'location', { pathname: '/base/foo/bar' }); set(this, 'rootURL', '/app/'); set(this, 'baseURL', '/base/'); } }); createLocation(); equal(location.getURL(), '/foo/bar'); }); }); define("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.'); }); }); define("ember-routing/tests/system/controller_for_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","container/container","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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__) { "use strict"; var Ember = __dependency1__["default"]; // A var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var Container = __dependency5__["default"]; var Namespace = __dependency6__["default"]; var classify = __dependency7__.classify; var Controller = __dependency8__["default"]; var ObjectController = __dependency9__["default"]; var ArrayController = __dependency10__["default"]; var controllerFor = __dependency11__["default"]; var generateControllerFactory = __dependency12__.generateControllerFactory; var generateController = __dependency12__["default"]; var buildContainer = function(namespace) { var container = new Container(); container.set = set; container.resolver = resolverFor(namespace); container.optionsForType('view', { singleton: false }); container.register('application:main', namespace, { instantiate: false }); container.register('controller:basic', Controller, { instantiate: false }); container.register('controller:object', ObjectController, { instantiate: false }); container.register('controller:array', ArrayController, { instantiate: false }); return container; }; function resolverFor(namespace) { return function(fullName) { var nameParts = fullName.split(":"), type = nameParts[0], name = nameParts[1]; if (name === 'basic') { name = ''; } var className = classify(name) + classify(type); var factory = get(namespace, className); if (factory) { return factory; } }; } var container, appController, namespace; QUnit.module("Ember.controllerFor", { setup: function() { namespace = Namespace.create(); container = buildContainer(namespace); container.register('controller:app', Controller.extend()); appController = container.lookup('controller:app'); }, teardown: function() { run(function () { container.destroy(); namespace.destroy(); }); } }); test("controllerFor should lookup for registered controllers", function() { var controller = controllerFor(container, 'app'); equal(appController, controller, 'should find app controller'); }); QUnit.module("Ember.generateController", { setup: function() { namespace = Namespace.create(); container = buildContainer(namespace); }, teardown: function() { run(function () { container.destroy(); namespace.destroy(); }); } }); test("generateController and generateControllerFactory are properties on the root namespace", function() { equal(Ember.generateController, generateController, 'should export generateController'); equal(Ember.generateControllerFactory, generateControllerFactory, 'should export generateControllerFactory'); }); test("generateController should create Ember.Controller", function() { var controller = generateController(container, 'home'); ok(controller instanceof Controller, 'should create controller'); }); test("generateController should create Ember.ObjectController", function() { var context = {}; var controller = generateController(container, 'home', context); ok(controller instanceof ObjectController, 'should create controller'); }); test("generateController should create Ember.ArrayController", function() { var context = Ember.A(); var controller = generateController(container, 'home', context); ok(controller instanceof ArrayController, 'should create controller'); }); test("generateController should create App.Controller if provided", function() { var controller; namespace.Controller = Controller.extend(); controller = generateController(container, 'home'); ok(controller instanceof namespace.Controller, 'should create controller'); }); test("generateController should create App.ObjectController if provided", function() { var context = {}, controller; namespace.ObjectController = ObjectController.extend(); controller = generateController(container, 'home', context); ok(controller instanceof namespace.ObjectController, 'should create controller'); }); test("generateController should create App.ArrayController if provided", function() { var context = Ember.A(), controller; namespace.ArrayController = ArrayController.extend(); controller = generateController(container, 'home', context); ok(controller instanceof namespace.ArrayController, 'should create controller'); }); }); define("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.'); }); }); define("ember-routing/tests/system/dsl_test", ["ember-routing/system/router"], function(__dependency1__) { "use strict"; var EmberRouter = __dependency1__["default"]; var Router; QUnit.module("Ember Router DSL", { setup: function() { Router = EmberRouter.extend(); }, teardown: function() { Router = null; } }); test("should fail when using a reserved route name", function() { expect(2); expectAssertion(function() { Router.map(function() { this.route('basic'); }); }, "'basic' cannot be used as a route name."); expectAssertion(function() { Router.map(function() { this.resource('basic'); }); }, "'basic' cannot be used as a resource name."); }); test("should reset namespace if nested with resource", function(){ var router = Router.map(function(){ this.resource('bleep', function(){ this.resource('bloop', function() { this.resource('blork'); }); }); }); ok(router.recognizer.names['bleep'], 'nested resources do not contain parent name'); ok(router.recognizer.names['bloop'], 'nested resources do not contain parent name'); ok(router.recognizer.names['blork'], 'nested resources do not contain parent name'); }); test("should retain resource namespace if nested with routes", function(){ var router = Router.map(function(){ this.route('bleep', function(){ this.route('bloop', function() { this.route('blork'); }); }); }); ok(router.recognizer.names['bleep'], 'parent name was used as base of nested routes'); ok(router.recognizer.names['bleep.bloop'], 'parent name was used as base of nested routes'); ok(router.recognizer.names['bleep.bloop.blork'], 'parent name was used as base of nested routes'); }); }); define("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.'); }); }); define("ember-routing/tests/system/route_test", ["ember-metal/core","ember-metal/run_loop","container/container","ember-runtime/system/object","ember-routing/system/route"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; // assert var run = __dependency2__["default"]; var Container = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var EmberRoute = __dependency5__["default"]; var route, routeOne, routeTwo, router, container, lookupHash; function createRoute(){ route = EmberRoute.create(); } function cleanupRoute(){ run(route, 'destroy'); } QUnit.module("Ember.Route", { setup: createRoute, teardown: cleanupRoute }); test("default store utilizes the container to acquire the model factory", function() { var Post, post; expect(4); post = {}; Post = EmberObject.extend(); Post.reopenClass({ find: function(id) { return post; } }); container = { has: function() { return true; }, lookupFactory: lookupFactory }; route.container = container; route.set('_qp', null); equal(route.model({ post_id: 1}), post); equal(route.findModel('post', 1), post, '#findModel returns the correct post'); function lookupFactory(fullName) { equal(fullName, "model:post", "correct factory was looked up"); return Post; } }); test("'store' can be injected by data persistence frameworks", function() { expect(8); run(route, 'destroy'); var container = new Container(); var post = { id: 1 }; var Store = EmberObject.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; } }); container.register('route:index', EmberRoute); container.register('store:main', Store); container.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'); }); test("assert if 'store.find' method is not found", function() { expect(1); run(route, 'destroy'); var container = new Container(); var Post = EmberObject.extend(); container.register('route:index', EmberRoute); container.register('model:post', Post); route = container.lookup('route:index'); expectAssertion(function() { route.findModel('post', 1); }, 'Post has no method `find`.'); }); test("asserts if model class is not found", function() { expect(1); run(route, 'destroy'); var container = new Container(); container.register('route:index', EmberRoute); 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."); }); test("'store' does not need to be injected", function() { expect(1); run(route, 'destroy'); var originalAssert = Ember.assert; var container = new Container(); container.register('route:index', EmberRoute); route = container.lookup('route:index'); ignoreAssertion(function(){ route.model({ post_id: 1}); }); ok(true, 'no error was raised'); }); test("modelFor doesn't require the router", function() { var container = new Container(); route.container = container; var foo = { name: 'foo' }; var fooRoute = EmberRoute.extend({ container: container, currentModel: foo }); container.register('route:foo', fooRoute); equal(route.modelFor('foo'), foo); }); QUnit.module("Ember.Route serialize", { setup: createRoute, teardown: cleanupRoute }); 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"); }); test("returns model.id if params include *_id", function(){ var model = {id: 2}; deepEqual(route.serialize(model, ['post_id']), {post_id: 2}, "serialized correctly"); }); 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() { container = { lookup: function(fullName) { return lookupHash[fullName]; } }; routeOne = EmberRoute.create({ container: container, routeName: 'one' }); routeTwo = EmberRoute.create({ container: container, routeName: 'two' }); lookupHash = { 'route:one': routeOne, 'route:two': routeTwo }; }, teardown: function() { run(function() { routeOne.destroy(); routeTwo.destroy(); }); } }); test("controllerFor uses route's controllerName if specified", function() { var testController = {}; lookupHash['controller:test'] = testController; routeOne.controllerName = 'test'; equal(routeTwo.controllerFor('one'), testController); }); }); define("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.'); }); }); define("ember-routing/tests/system/router_test", ["ember-metal/run_loop","ember-runtime/copy","ember-metal/merge","ember-metal/enumerable_utils","container/container","ember-routing/location/hash_location","ember-routing/location/auto_location","ember-routing/system/router"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var run = __dependency1__["default"]; var copy = __dependency2__["default"]; var merge = __dependency3__["default"]; var map = __dependency4__.map; var Container = __dependency5__["default"]; var HashLocation = __dependency6__["default"]; var AutoLocation = __dependency7__["default"]; var EmberRouter = __dependency8__["default"]; var container, Router, router; function createRouter(overrides) { var opts = merge({ container: container }, overrides); router = Router.create(opts); } QUnit.module("Ember Router", { setup: function() { container = new Container(); //register the HashLocation (the default) container.register('location:hash', HashLocation); // ensure rootURL is injected into any locations container.injection('location', 'rootURL', '-location-setting:root-url'); Router = EmberRouter.extend(); }, teardown: function() { container = Router = router = null; } }); test("can create a router without a container", function() { var router = Router.create(); ok(router.router); }); test("should create a router if one does not exist on the constructor", function() { createRouter(); ok(router.router); }); test("should destroy its location upon destroying the routers container.", function() { createRouter(); var location = router.get('location'); run(container, 'destroy'); ok(location.isDestroyed, "location should be destroyed"); }); test("should instantiate its location with its `rootURL`", function() { createRouter({ rootURL: '/rootdir/' }); var location = router.get('location'); equal(location.get('rootURL'), '/rootdir/'); }); test("Ember.AutoLocation._replacePath should be called with the right path", function() { expect(1); var AutoTestLocation = copy(AutoLocation); 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; }; container.register('location:auto', AutoTestLocation); createRouter({ location: 'auto', rootURL: '/rootdir/' }); }); test("Ember.Router._routePath should consume identical prefixes", function() { createRouter(); expect(8); function routePath(s1, s2, s3) { var handlerInfos = map(arguments, function(s) { return { name: s }; }); handlerInfos.unshift({ name: 'ignored' }); return EmberRouter._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'); }); test("Router should cancel routing setup when the Location class says so via cancelRouterSetup", function() { expect(0); var FakeLocation = { cancelRouterSetup: true, create: function () { return this; } }; container.register('location:fake', FakeLocation); router = Router.create({ container: container, location: 'fake', _setupRouter: function () { ok(false, '_setupRouter should not be called'); } }); router.startRouting(); }); test("AutoLocation should replace the url when it's not in the preferred format", function() { expect(1); var AutoTestLocation = copy(AutoLocation); 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; }; container.register('location:auto', AutoTestLocation); createRouter({ location: 'auto', rootURL: '/rootdir/' }); }); }); define("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.'); }); }); define("ember-runtime.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-runtime.js should pass jshint', function() { ok(true, 'ember-runtime.js should pass jshint.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-runtime/keys.jshint", [], function() { "use strict"; module('JSHint - ember-runtime'); test('ember-runtime/keys.js should pass jshint', function() { ok(true, 'ember-runtime/keys.js should pass jshint.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-runtime/tests/computed/compose_computed_test", ["ember-metal/core","ember-metal/utils","ember-metal/observer","ember-metal/computed","ember-runtime/computed/reduce_computed_macros","ember-metal/run_loop","ember-metal/properties","ember-runtime/compare","ember-metal/tests/props_helper","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; var Ember = __dependency1__["default"]; var metaFor = __dependency2__.metaFor; var addObserver = __dependency3__.addObserver; var computed = __dependency4__.computed; var mapBy = __dependency5__.mapBy; var union = __dependency5__.union; var sort = __dependency5__.sort; var run = __dependency6__["default"]; var defineProperty = __dependency7__.defineProperty; var compare = __dependency8__["default"]; var testBoth = __dependency9__["default"]; var EmberObject = __dependency10__["default"]; }); define("ember-runtime/tests/computed/compose_computed_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/computed'); test('ember-runtime/tests/computed/compose_computed_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/computed/compose_computed_test.js should pass jshint.'); }); }); define("ember-runtime/tests/computed/computed_macros_test", ["ember-metal/computed","ember-runtime/system/object","ember-runtime/tests/props_helper"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var computed = __dependency1__.computed; var EmberObject = __dependency2__["default"]; var testBoth = __dependency3__.testBoth; QUnit.module('CP macros'); testBoth('Ember.computed.empty', function (get, set) { var obj = EmberObject.extend({ bestLannister: null, lannisters: null, bestLannisterUnspecified: computed.empty('bestLannister'), noLannistersKnown: computed.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"); }); }); define("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.'); }); }); define("ember-runtime/tests/computed/reduce_computed_macros_test", ["ember-metal/core","ember-metal/enumerable_utils","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/computed","ember-metal/observer","ember-metal/property_events","ember-metal/array","ember-metal/mixin","ember-runtime/computed/reduce_computed_macros","ember-runtime/system/native_array"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__) { "use strict"; var Ember = __dependency1__["default"]; var EnumerableUtils = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var setProperties = __dependency4__["default"]; var ObjectProxy = __dependency5__["default"]; var get = __dependency6__.get; var set = __dependency7__.set; var run = __dependency8__["default"]; var computed = __dependency9__.computed; var addObserver = __dependency10__.addObserver; var beginPropertyChanges = __dependency11__.beginPropertyChanges; var endPropertyChanges = __dependency11__.endPropertyChanges; var forEach = __dependency12__.forEach; var observer = __dependency13__.observer; var computedSum = __dependency14__.sum; var computedMin = __dependency14__.min; var computedMax = __dependency14__.max; var computedMap = __dependency14__.map; var computedSort = __dependency14__.sort; var computedSetDiff = __dependency14__.setDiff; var computedMapBy = __dependency14__.mapBy; var mapProperty = __dependency14__.mapProperty; var computedFilter = __dependency14__.filter; var computedFilterBy = __dependency14__.filterBy; var computedUniq = __dependency14__.uniq; var computedUnion = __dependency14__.union; var computedIntersect = __dependency14__.intersect; var NativeArray = __dependency15__["default"]; var obj, sorted, sortProps, items, userFnCalls, todos, filtered; QUnit.module('computedMap', { setup: function() { run(function() { userFnCalls = 0; obj = EmberObject.createWithMixins({ array: Ember.A([{ v: 1 }, { v: 3}, { v: 2 }, { v: 1 }]), mapped: computedMap('array.@each.v', function(item) { ++userFnCalls; return item.v; }), arrayObjects: Ember.A([ EmberObject.create({ v: { name: 'Robert' }}), EmberObject.create({ v: { name: 'Leanna' }})]), mappedObjects: computedMap('arrayObjects.@each.v', function (item) { return { name: item.v.name }; }) }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it maps simple properties", function() { deepEqual(get(obj, 'mapped'), [1, 3, 2, 1]); run(function() { obj.get('array').pushObject({ v: 5 }); }); deepEqual(get(obj, 'mapped'), [1, 3, 2, 1, 5]); run(function() { obj.get('array').removeAt(3); }); deepEqual(get(obj, 'mapped'), [1, 3, 2, 5]); }); test("it caches properly", function() { var array = get(obj, 'array'), mapped = get(obj, 'mapped'); equal(userFnCalls, 4, "precond - mapper called expected number of times"); run(function() { array.addObject({v: 7}); }); equal(userFnCalls, 5, "precond - mapper called expected number of times"); get(obj, 'mapped'); equal(userFnCalls, 5, "computedMap caches properly"); }); test("it maps simple unshifted properties", function() { var array = Ember.A([]); run(function() { obj = EmberObject.createWithMixins({ array: array, mapped: computedMap('array', function (item) { return item.toUpperCase(); }) }); get(obj, 'mapped'); }); run(function() { array.unshiftObject('c'); array.unshiftObject('b'); array.unshiftObject('a'); array.popObject(); }); deepEqual(get(obj, 'mapped'), ['A', 'B'], "properties unshifted in sequence are mapped correctly"); }); test("it maps objects", function() { deepEqual(get(obj, 'mappedObjects'), [{ name: 'Robert'}, { name: 'Leanna' }]); run(function() { obj.get('arrayObjects').pushObject({ v: { name: 'Eddard' }}); }); deepEqual(get(obj, 'mappedObjects'), [{ name: 'Robert' }, { name: 'Leanna' }, { name: 'Eddard' }]); run(function() { obj.get('arrayObjects').removeAt(1); }); deepEqual(get(obj, 'mappedObjects'), [{ name: 'Robert' }, { name: 'Eddard' }]); run(function() { obj.get('arrayObjects').objectAt(0).set('v', { name: 'Stannis' }); }); deepEqual(get(obj, 'mappedObjects'), [{ name: 'Stannis' }, { name: 'Eddard' }]); }); test("it maps unshifted objects with property observers", function() { var array = Ember.A([]), cObj = { v: 'c' }; run(function() { obj = EmberObject.createWithMixins({ array: array, mapped: computedMap('array.@each.v', function (item) { return get(item, 'v').toUpperCase(); }) }); get(obj, 'mapped'); }); run(function() { array.unshiftObject(cObj); array.unshiftObject({ v: 'b' }); array.unshiftObject({ v: 'a' }); set(cObj, 'v', 'd'); }); deepEqual(array.mapBy('v'), ['a', 'b', 'd'], "precond - unmapped array is correct"); deepEqual(get(obj, 'mapped'), ['A', 'B', 'D'], "properties unshifted in sequence are mapped correctly"); }); QUnit.module('computedMapBy', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ array: Ember.A([{ v: 1 }, { v: 3}, { v: 2 }, { v: 1 }]), mapped: computedMapBy('array', 'v') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it maps properties", function() { var mapped = get(obj, 'mapped'); deepEqual(get(obj, 'mapped'), [1, 3, 2, 1]); run(function() { obj.get('array').pushObject({ v: 5 }); }); deepEqual(get(obj, 'mapped'), [1, 3, 2, 1, 5]); run(function() { obj.get('array').removeAt(3); }); deepEqual(get(obj, 'mapped'), [1, 3, 2, 5]); }); test("it is observerable", function() { var mapped = get(obj, 'mapped'), calls = 0; deepEqual(get(obj, 'mapped'), [1, 3, 2, 1]); addObserver(obj, 'mapped.@each', function() { calls++; }); run(function() { obj.get('array').pushObject({ v: 5 }); }); equal(calls, 1, 'computedMapBy is observerable'); }); QUnit.module('computedFilter', { setup: function() { run(function() { userFnCalls = 0; obj = EmberObject.createWithMixins({ array: Ember.A([1, 2, 3, 4, 5, 6, 7, 8]), filtered: computedFilter('array', function(item) { ++userFnCalls; return item % 2 === 0; }) }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it filters according to the specified filter function", function() { var filtered = get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "computedFilter filters by the specified function"); }); test("it caches properly", function() { var array = get(obj, 'array'), filtered = get(obj, 'filtered'); equal(userFnCalls, 8, "precond - filter called expected number of times"); run(function() { array.addObject(11); }); equal(userFnCalls, 9, "precond - filter called expected number of times"); get(obj, 'filtered'); equal(userFnCalls, 9, "computedFilter caches properly"); }); test("it updates as the array is modified", function() { var array = get(obj, 'array'), filtered = get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run(function() { array.addObject(11); }); deepEqual(filtered, [2,4,6,8], "objects not passing the filter are not added"); run(function() { array.addObject(12); }); deepEqual(filtered, [2,4,6,8,12], "objects passing the filter are added"); run(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"); }); test("the dependent array can be cleared one at a time", function() { var array = get(obj, 'array'), filtered = get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run(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"); }); test("the dependent array can be `clear`ed directly (#3272)", function() { var array = get(obj, 'array'), filtered = get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run(function() { array.clear(); }); deepEqual(filtered, [], "filtered array cleared correctly"); }); test("it updates as the array is replaced", function() { var array = get(obj, 'array'), filtered = get(obj, 'filtered'); deepEqual(filtered, [2,4,6,8], "precond - filtered array is initially correct"); run(function() { set(obj, 'array', Ember.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.createWithMixins({ array: Ember.A([ {name: "one", a:1, b:false}, {name: "two", a:2, b:false}, {name: "three", a:1, b:true}, {name: "four", b:true} ]), a1s: computedFilterBy('array', 'a', 1), as: computedFilterBy('array', 'a'), bs: computedFilterBy('array', 'b') }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("properties can be filtered by truthiness", function() { var array = get(obj, 'array'), as = get(obj, 'as'), bs = 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(function() { set(array.objectAt(0), 'a', undefined); set(array.objectAt(3), 'a', true); set(array.objectAt(0), 'b', true); 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(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(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(function() { set(obj, 'array', Ember.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"); }); test("properties can be filtered by values", function() { var array = get(obj, 'array'), a1s = get(obj, 'a1s'); deepEqual(a1s.mapBy('name'), ['one', 'three'], "properties can be filtered by matching value"); run(function() { array.pushObject({ name: "five", a:1 }); }); deepEqual(a1s.mapBy('name'), ['one', 'three', 'five'], "arrays computed by matching value respond to added objects"); run(function() { array.popObject(); }); deepEqual(a1s.mapBy('name'), ['one', 'three'], "arrays computed by matching value respond to removed objects"); run(function() { set(array.objectAt(1), 'a', 1); set(array.objectAt(2), 'a', 2); }); deepEqual(a1s.mapBy('name'), ['one', 'two'], "arrays computed by matching value respond to modified properties"); }); test("properties values can be replaced", function() { obj = EmberObject.createWithMixins({ array: Ember.A([]), a1s: computedFilterBy('array', 'a', 1), a1bs: computedFilterBy('a1s', 'b') }); var a1bs = get(obj, 'a1bs'); deepEqual(a1bs.mapBy('name'), [], "properties can be filtered by matching value"); run(function() { set(obj, 'array', Ember.A([{name: 'item1', a:1, b:true}])); }); a1bs = get(obj, 'a1bs'); deepEqual(a1bs.mapBy('name'), ['item1'], "properties can be filtered by matching value"); }); forEach.call([['uniq', computedUniq], ['union', computedUnion]], function (tuple) { var alias = tuple[0], testedFunc = tuple[1]; QUnit.module('computed.' + alias, { setup: function() { run(function() { obj = EmberObject.createWithMixins({ array: Ember.A([1,2,3,4,5,6]), array2: Ember.A([4,5,6,7,8,9,4,5,6,7,8,9]), array3: Ember.A([1,8,10]), union: testedFunc('array', 'array2', 'array3') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("does not include duplicates", function() { var array = get(obj, 'array'), array2 = get(obj, 'array2'), array3 = get(obj, 'array3'), union = get(obj, 'union'); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], alias + " does not include duplicates"); run(function() { array.pushObject(8); }); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], alias + " does not add existing items"); run(function() { array.pushObject(11); }); deepEqual(union, [1,2,3,4,5,6,7,8,9,10,11], alias + " adds new items"); run(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(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"); }); test("has set-union semantics", function() { var array = get(obj, 'array'), array2 = get(obj, 'array2'), array3 = get(obj, 'array3'), union = get(obj, 'union'); deepEqual(union, [1,2,3,4,5,6,7,8,9,10], alias + " is initially correct"); run(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(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.module('computed.intersect', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ array: Ember.A([1,2,3,4,5,6]), array2: Ember.A([3,3,3,4,5]), array3: Ember.A([3,5,6,7,8]), intersection: computedIntersect('array', 'array2', 'array3') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it has set-intersection semantics", function() { var array = get(obj, 'array'), array2 = get(obj, 'array2'), array3 = get(obj, 'array3'), intersection = get(obj, 'intersection'); deepEqual(intersection, [3,5], "intersection is initially correct"); run(function() { array2.shiftObject(); }); deepEqual(intersection, [3,5], "objects are not removed when they are still in all dependent arrays"); run(function() { array2.shiftObject(); }); deepEqual(intersection, [3,5], "objects are not removed when they are still in all dependent arrays"); run(function() { array2.shiftObject(); }); deepEqual(intersection, [5], "objects are removed once they are gone from all dependent arrays"); run(function() { array2.pushObject(1); }); deepEqual(intersection, [5], "objects are not added as long as they are missing from any dependent array"); run(function() { array3.pushObject(1); }); deepEqual(intersection, [5,1], "objects added once they belong to all dependent arrays"); }); QUnit.module('computedSetDiff', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ array: Ember.A([1,2,3,4,5,6,7]), array2: Ember.A([3,4,5,10]), diff: computedSetDiff('array', 'array2') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it throws an error if given fewer or more than two dependent properties", function() { throws(function () { EmberObject.createWithMixins({ array: Ember.A([1,2,3,4,5,6,7]), array2: Ember.A([3,4,5]), diff: computedSetDiff('array') }); }, /requires exactly two dependent arrays/, "setDiff requires two dependent arrays"); throws(function () { EmberObject.createWithMixins({ array: Ember.A([1,2,3,4,5,6,7]), array2: Ember.A([3,4,5]), array3: Ember.A([7]), diff: computedSetDiff('array', 'array2', 'array3') }); }, /requires exactly two dependent arrays/, "setDiff requires two dependent arrays"); }); test("it has set-diff semantics", function() { var array1 = get(obj, 'array'), array2 = get(obj, 'array2'), diff = get(obj, 'diff'); deepEqual(diff, [1, 2, 6, 7], "set-diff is initially correct"); run(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(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(function() { array1.removeObject(3); }); deepEqual(diff, [1, 2, 6, 7], "removing objects from the keep array removes them from the computed array"); run(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(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() { test("arrays are initially sorted", function() { run(function() { sorted = get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "array is initially sorted"); }); test("changing the dependent array updates the sorted array", function() { run(function() { sorted = get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { set(obj, 'items', Ember.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"); }); test("adding to the dependent array updates the sorted array", function() { run(function() { sorted = get(obj, 'sortedItems'); items = get(obj, 'items'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(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"); }); test("removing from the dependent array updates the sorted array", function() { run(function() { sorted = get(obj, 'sortedItems'); items = get(obj, 'items'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { items.popObject(); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Robb'], "Removing from the dependent array updates the sorted array"); }); test("distinct items may be sort-equal, although their relative order will not be guaranteed", function() { var jaime, jaimeInDisguise; run(function() { // We recreate jaime and "Cersei" here only for test stability: we want // their guid-ordering to be deterministic jaimeInDisguise = EmberObject.create({ fname: 'Cersei', lname: 'Lannister', age: 34 }); jaime = EmberObject.create({ fname: 'Jaime', lname: 'Lannister', age: 34 }); items = get(obj, 'items'); items.replace(0, 1, jaime); items.replace(1, 1, jaimeInDisguise); sorted = get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(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(function() { // comparator will again return non-zero jaimeInDisguise.set('fname', 'Cersei'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "sorted array is updated"); }); test("guid sort-order fallback with a serach proxy is not confused by non-search ObjectProxys", function() { var tyrion = { fname: "Tyrion", lname: "Lannister" }, tyrionInDisguise = ObjectProxy.create({ fname: "Yollo", lname: "", content: tyrion }); items = get(obj, 'items'); sorted = get(obj, 'sortedItems'); run(function() { items.pushObject(tyrion); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']); run(function() { items.pushObject(tyrionInDisguise); }); deepEqual(sorted.mapBy('fname'), ['Yollo', 'Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']); }); } QUnit.module('computedSort - sortProperties', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ itemSorting: Ember.A(['lname', 'fname']), items: Ember.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: computedSort('items', 'itemSorting') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); commonSortTests(); test("updating sort properties updates the sorted array", function() { run(function() { sorted = get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { set(obj, 'itemSorting', Ember.A(['fname:desc'])); }); deepEqual(sorted.mapBy('fname'), ['Robb', 'Jaime', 'Cersei', 'Bran'], "after updating sort properties array is updated"); }); test("updating sort properties in place updates the sorted array", function() { run(function() { sorted = get(obj, 'sortedItems'); sortProps = get(obj, 'itemSorting'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { sortProps.clear(); sortProps.pushObject('fname'); }); deepEqual(sorted.mapBy('fname'), ['Bran', 'Cersei', 'Jaime', 'Robb'], "after updating sort properties array is updated"); }); test("updating new sort properties in place updates the sorted array", function() { run(function() { sorted = get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { set(obj, 'itemSorting', Ember.A(['age:desc', 'fname:asc'])); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Robb', 'Bran'], "precond - array is correct after item sorting is changed"); run(function() { items = get(obj, 'items'); var cersei = items.objectAt(1); set(cersei, 'age', 29); // how vain }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Cersei', 'Robb', 'Bran'], "after updating sort properties array is updated"); }); test("sort direction defaults to ascending", function() { run(function() { sorted = get(obj, 'sortedItems'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { set(obj, 'itemSorting', Ember.A(['fname'])); }); deepEqual(sorted.mapBy('fname'), ['Bran', 'Cersei', 'Jaime', 'Robb'], "sort direction defaults to ascending"); }); test("updating an item's sort properties updates the sorted array", function() { var tyrionInDisguise; run(function() { sorted = get(obj, 'sortedItems'); items = get(obj, 'items'); }); tyrionInDisguise = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { set(tyrionInDisguise, 'fname', 'Tyrion'); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Tyrion', 'Bran', 'Robb'], "updating an item's sort properties updates the sorted array"); }); test("updating several of an item's sort properties updated the sorted array", function() { var sansaInDisguise; run(function() { sorted = get(obj, 'sortedItems'); items = get(obj, 'items'); }); sansaInDisguise = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { setProperties(sansaInDisguise, { fname: 'Sansa', lname: 'Stark' }); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Bran', 'Robb', 'Sansa'], "updating an item's sort properties updates the sorted array"); }); test("updating an item's sort properties does not error when binary search does a self compare (#3273)", function() { var jaime, cersei; run(function() { jaime = EmberObject.create({ name: 'Jaime', status: 1 }); cersei = EmberObject.create({ name: 'Cersei', status: 2 }); obj = EmberObject.createWithMixins({ people: Ember.A([jaime, cersei]), sortProps: Ember.A(['status']), sortedPeople: computedSort('people', 'sortProps') }); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei], "precond - array is initially sorted"); run(function() { cersei.set('status', 3); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei], "array is sorted correctly"); run(function() { cersei.set('status', 2); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei], "array is sorted correctly"); }); test("property paths in sort properties update the sorted array", function () { var jaime, cersei, sansa; run(function () { jaime = EmberObject.create({ relatedObj: EmberObject.create({ status: 1, firstName: 'Jaime', lastName: 'Lannister' }) }); cersei = EmberObject.create({ relatedObj: EmberObject.create({ status: 2, firstName: 'Cersei', lastName: 'Lannister' }) }); sansa = EmberObject.create({ relatedObj: EmberObject.create({ status: 3, firstName: 'Sansa', lastName: 'Stark' }) }); obj = EmberObject.createWithMixins({ people: Ember.A([jaime, cersei, sansa]), sortProps: Ember.A(['relatedObj.status']), sortedPeople: computedSort('people', 'sortProps') }); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei, sansa], "precond - array is initially sorted"); run(function () { cersei.set('status', 3); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei, sansa], "array is sorted correctly"); run(function () { cersei.set('status', 1); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei, sansa], "array is sorted correctly"); run(function () { sansa.set('status', 1); }); deepEqual(get(obj, 'sortedPeople'), [jaime, cersei, sansa], "array is sorted correctly"); run(function () { obj.set('sortProps', Ember.A(['relatedObj.firstName'])); }); deepEqual(get(obj, 'sortedPeople'), [cersei, jaime, sansa], "array is sorted correctly"); }); function sortByLnameFname(a, b) { var lna = get(a, 'lname'), lnb = get(b, 'lname'); if (lna !== lnb) { return lna > lnb ? 1 : -1; } return sortByFnameAsc(a,b); } function sortByFnameAsc(a, b) { var fna = get(a, 'fname'), fnb = get(b, 'fname'); if (fna === fnb) { return 0; } return fna > fnb ? 1 : -1; } function sortByFnameDesc(a, b) { return -sortByFnameAsc(a,b); } QUnit.module('computedSort - sort function', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ items: Ember.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: computedSort('items.@each.fname', sortByLnameFname) }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); commonSortTests(); test("changing item properties specified via @each triggers a resort of the modified item", function() { var tyrionInDisguise; run(function() { sorted = get(obj, 'sortedItems'); items = get(obj, 'items'); }); tyrionInDisguise = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { set(tyrionInDisguise, 'fname', 'Tyrion'); }); deepEqual(sorted.mapBy('fname'), ['Jaime', 'Tyrion', 'Bran', 'Robb'], "updating a specified property on an item resorts it"); }); test("changing item properties not specified via @each does not trigger a resort", function() { var cersei; run(function() { sorted = get(obj, 'sortedItems'); items = get(obj, 'items'); }); cersei = items.objectAt(1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "precond - array is initially sorted"); run(function() { 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('computedMax', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ items: Ember.A([1,2,3]), max: computedMax('items') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("max tracks the max number as objects are added", function() { equal(get(obj, 'max'), 3, "precond - max is initially correct"); run(function() { items = get(obj, 'items'); }); run(function() { items.pushObject(5); }); equal(get(obj, 'max'), 5, "max updates when a larger number is added"); run(function() { items.pushObject(2); }); equal(get(obj, 'max'), 5, "max does not update when a smaller number is added"); }); test("max recomputes when the current max is removed", function() { equal(get(obj, 'max'), 3, "precond - max is initially correct"); run(function() { items = get(obj, 'items'); items.removeObject(2); }); equal(get(obj, 'max'), 3, "max is unchanged when a non-max item is removed"); run(function() { items.removeObject(3); }); equal(get(obj, 'max'), 1, "max is recomputed when the current max is removed"); }); QUnit.module('computedMin', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ items: Ember.A([1,2,3]), min: computedMin('items') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("min tracks the min number as objects are added", function() { equal(get(obj, 'min'), 1, "precond - min is initially correct"); run(function() { items = get(obj, 'items'); }); run(function() { items.pushObject(-2); }); equal(get(obj, 'min'), -2, "min updates when a smaller number is added"); run(function() { items.pushObject(2); }); equal(get(obj, 'min'), -2, "min does not update when a larger number is added"); }); test("min recomputes when the current min is removed", function() { equal(get(obj, 'min'), 1, "precond - min is initially correct"); run(function() { items = get(obj, 'items'); items.removeObject(2); }); equal(get(obj, 'min'), 1, "min is unchanged when a non-min item is removed"); run(function() { items.removeObject(1); }); equal(get(obj, 'min'), 3, "min is recomputed when the current min is removed"); }); QUnit.module('Ember.arrayComputed - mixed sugar', { setup: function() { run(function() { obj = EmberObject.createWithMixins({ items: Ember.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: computedFilterBy('items', 'lname', 'Lannister'), lannisterSorting: Ember.A(['fname']), sortedLannisters: computedSort('lannisters', 'lannisterSorting'), starks: computedFilterBy('items', 'lname', 'Stark'), starkAges: computedMapBy('starks', 'age'), oldestStarkAge: computedMax('starkAges') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("filtering and sorting can be combined", function() { run(function() { items = get(obj, 'items'); sorted = get(obj, 'sortedLannisters'); }); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime'], "precond - array is initially filtered and sorted"); run(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"); }); test("filtering, sorting and reduce (max) can be combined", function() { run(function() { items = get(obj, 'items'); }); equal(16, get(obj, 'oldestStarkAge'), "precond - end of chain is initially correct"); run(function() { items.pushObject({fname: 'Rickon', lname: 'Stark', age: 5}); }); equal(16, get(obj, 'oldestStarkAge'), "chain is updated correctly"); run(function() { items.pushObject({fname: 'Eddard', lname: 'Stark', age: 35}); }); equal(35, get(obj, 'oldestStarkAge'), "chain is updated correctly"); }); function todo(name, priority) { return EmberObject.create({name: name, priority: priority}); } function priorityComparator(todoA, todoB) { var pa = parseInt(get(todoA, 'priority'), 10), pb = parseInt(get(todoB, 'priority'), 10); return pa - pb; } function evenPriorities(todo) { var p = parseInt(get(todo, 'priority'), 10); return p % 2 === 0; } QUnit.module('Ember.arrayComputed - chains', { setup: function() { obj = EmberObject.createWithMixins({ todos: Ember.A([todo('E', 4), todo('D', 3), todo('C', 2), todo('B', 1), todo('A', 0)]), sorted: computedSort('todos.@each.priority', priorityComparator), filtered: computedFilter('sorted.@each.priority', evenPriorities) }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it can filter and sort when both depend on the same item property", function() { filtered = get(obj, 'filtered'); sorted = get(obj, 'sorted'); todos = 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(function() { 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); 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(function() { userFnCalls = 0; obj = EmberObject.createWithMixins({ array: Ember.A([{ v: 1 }, { v: 3}, { v: 2 }, { v: 1 }]), mapped: computedMapBy('array', 'v'), max: computedMax('mapped'), maxDidChange: observer('max', function(){ userFnCalls++; }) }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("it computes interdependent array computed properties", function() { var mapped = get(obj, 'mapped'); equal(get(obj, 'max'), 3, 'sanity - it properly computes the maximum value'); equal(userFnCalls, 0, 'observer is not called on initialisation'); var calls = 0; addObserver(obj, 'max', function(){ calls++; }); run(function() { obj.get('array').pushObject({ v: 5 }); }); equal(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(function() { obj = EmberObject.createWithMixins({ array: Ember.A([ 1, 2, 3 ]), total: computedSum('array') }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test('sums the values in the dependentKey', function(){ var sum = get(obj, 'total'); equal(sum, 6, 'sums the values'); }); test('updates when array is modified', function(){ var sum = function(){ return get(obj, 'total'); }; run(function(){ get(obj, 'array').pushObject(1); }); equal(sum(), 7, 'recomputed when elements are added'); run(function(){ get(obj, 'array').popObject(); }); equal(sum(), 6, 'recomputes when elements are removed'); }); }); define("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.'); }); }); define("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-runtime/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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__) { "use strict"; var Ember = __dependency1__["default"]; var map = __dependency2__.map; var get = __dependency3__.get; var getWithDefault = __dependency3__.getWithDefault; var set = __dependency4__.set; var metaFor = __dependency5__.meta; var run = __dependency6__["default"]; var observer = __dependency7__.observer; var keys = __dependency8__["default"]; var EmberObject = __dependency9__["default"]; var ComputedProperty = __dependency10__.ComputedProperty; var computed = __dependency10__.computed; var arrayComputed = __dependency11__.arrayComputed; var reduceComputed = __dependency12__.reduceComputed; var ArrayProxy = __dependency13__["default"]; var SubArray = __dependency14__["default"]; var obj, addCalls, removeCalls, callbackItems, shared; QUnit.module('arrayComputed', { setup: function () { addCalls = removeCalls = 0; obj = EmberObject.createWithMixins({ numbers: Ember.A([ 1, 2, 3, 4, 5, 6 ]), otherNumbers: Ember.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: 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: arrayComputed('numbers', 'otherNumbers', { addedItem: function (array, item) { if (item % 2 === 0) { array.pushObject(item); } return array; } }), nestedNumbers: Ember.A(map([1,2,3,4,5,6], function (n) { return EmberObject.create({ p: 'otherProperty', v: n }); })), evenNestedNumbers: 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(function() { obj.destroy(); }); } }); test("array computed properties are instances of ComputedProperty", function() { ok(arrayComputed({}) instanceof ComputedProperty); }); test("when the dependent array is null or undefined, `addedItem` is not called and only the initial value is returned", function() { obj = EmberObject.createWithMixins({ numbers: null, doubledNumbers: arrayComputed('numbers', { addedItem: function (array, n) { addCalls++; array.pushObject(n * 2); return array; } }) }); deepEqual(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(function() { set(obj, 'numbers', Ember.A([1,2])); }); deepEqual(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"); }); test("on first retrieval, array computed properties are computed", function() { deepEqual(get(obj, 'evenNumbers'), [2,4,6], "array computed properties are correct on first invocation"); }); test("on first retrieval, array computed properties with multiple dependent keys are computed", function() { deepEqual(get(obj, 'evenNumbersMultiDep'), [2, 4, 6, 8], "array computed properties are correct on first invocation"); }); test("on first retrieval, array computed properties dependent on nested objects are computed", function() { deepEqual(get(obj, 'evenNestedNumbers'), [2,4,6], "array computed properties are correct on first invocation"); }); test("after the first retrieval, array computed properties observe additions to dependent arrays", function() { var numbers = get(obj, 'numbers'), // set up observers evenNumbers = get(obj, 'evenNumbers'); run(function() { numbers.pushObjects([7, 8]); }); deepEqual(evenNumbers, [2, 4, 6, 8], "array computed properties watch dependent arrays"); }); test("after the first retrieval, array computed properties observe removals from dependent arrays", function() { var numbers = get(obj, 'numbers'), // set up observers evenNumbers = get(obj, 'evenNumbers'); run(function() { numbers.removeObjects([3, 4]); }); deepEqual(evenNumbers, [2, 6], "array computed properties watch dependent arrays"); }); test("after first retrieval, array computed properties can observe properties on array items", function() { var nestedNumbers = get(obj, 'nestedNumbers'), evenNestedNumbers = get(obj, 'evenNestedNumbers'); deepEqual(evenNestedNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run(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'); }); test("changes to array computed properties happen synchronously", function() { var nestedNumbers = get(obj, 'nestedNumbers'), evenNestedNumbers = get(obj, 'evenNestedNumbers'); deepEqual(evenNestedNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run(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'); }); }); test("multiple dependent keys can be specified via brace expansion", function() { var obj = EmberObject.createWithMixins({ bar: Ember.A(), baz: Ember.A(), foo: reduceComputed({ initialValue: Ember.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(get(obj, 'foo'), [], "initially empty"); get(obj, 'bar').pushObject(1); deepEqual(get(obj, 'foo'), ['a:1'], "added item from brace-expanded dependency"); get(obj, 'baz').pushObject(2); deepEqual(get(obj, 'foo'), ['a:1', 'a:2'], "added item from brace-expanded dependency"); get(obj, 'bar').popObject(); deepEqual(get(obj, 'foo'), ['a:1', 'a:2', 'r:1'], "removed item from brace-expanded dependency"); get(obj, 'baz').popObject(); deepEqual(get(obj, 'foo'), ['a:1', 'a:2', 'r:1', 'r:2'], "removed item from brace-expanded dependency"); }); test("multiple item property keys can be specified via brace expansion", function() { var addedCalls = 0, removedCalls = 0, expected = Ember.A(), item = { propA: 'A', propB: 'B', propC: 'C' }, obj = EmberObject.createWithMixins({ bar: Ember.A([item]), foo: reduceComputed({ initialValue: Ember.A(), addedItem: function(array, item, changeMeta) { array.pushObject('a:' + get(item, 'propA') + ':' + get(item, 'propB') + ':' + get(item, 'propC')); return array; }, removedItem: function(array, item, changeMeta) { array.pushObject('r:' + get(item, 'propA') + ':' + get(item, 'propB') + ':' + get(item, 'propC')); return array; } }).property('bar.@each.{propA,propB}') }); expected.pushObjects(['a:A:B:C']); deepEqual(get(obj, 'foo'), expected, "initially added dependent item"); set(item, 'propA', 'AA'); expected.pushObjects(['r:AA:B:C', 'a:AA:B:C']); deepEqual(get(obj, 'foo'), expected, "observing item property key specified via brace expansion"); set(item, 'propB', 'BB'); expected.pushObjects(['r:AA:BB:C', 'a:AA:BB:C']); deepEqual(get(obj, 'foo'), expected, "observing item property key specified via brace expansion"); set(item, 'propC', 'CC'); deepEqual(get(obj, 'foo'), expected, "not observing unspecified item properties"); }); test("doubly nested item property keys (@each.foo.@each) are not supported", function() { run(function() { obj = EmberObject.createWithMixins({ peopleByOrdinalPosition: Ember.A([{ first: Ember.A([EmberObject.create({ name: "Jaime Lannister" })])}]), people: arrayComputed({ addedItem: function (array, item) { array.pushObject(get(item, 'first.firstObject')); return array; } }).property('peopleByOrdinalPosition.@each.first'), names: arrayComputed({ addedItem: function (array, item) { equal(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.createWithMixins({ people: [{ first: Ember.A([EmberObject.create({ name: "Jaime Lannister" })])}], names: 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"); }); test("after the first retrieval, array computed properties observe dependent arrays", function() { var numbers = get(obj, 'numbers'), evenNumbers = get(obj, 'evenNumbers'); deepEqual(evenNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run(function() { set(obj, 'numbers', Ember.A([20, 23, 28])); }); deepEqual(evenNumbers, [20, 28], "array computed properties watch dependent arrays"); }); test("array observers are torn down when dependent arrays change", function() { var numbers = get(obj, 'numbers'), evenNumbers = 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(function() { set(obj, 'numbers', Ember.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.A([7,8,9,10])); equal(addCalls, 9, 'add is not called'); equal(removeCalls, 0, 'remove is not called'); }); test("modifying properties on dependent array items triggers observers exactly once", function() { var numbers = get(obj, 'numbers'), evenNumbers = 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(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'); }); test("multiple array computed properties on the same object can observe dependent arrays", function() { var numbers = get(obj, 'numbers'), otherNumbers = get(obj, 'otherNumbers'); deepEqual(get(obj, 'evenNumbers'), [2,4,6], "precond - evenNumbers is initially correct"); deepEqual(get(obj, 'evenNumbersMultiDep'), [2, 4, 6, 8], "precond - evenNumbersMultiDep is initially correct"); run(function() { numbers.pushObject(12); otherNumbers.pushObject(14); }); deepEqual(get(obj, 'evenNumbers'), [2,4,6,12], "evenNumbers is updated"); deepEqual(get(obj, 'evenNumbersMultiDep'), [2, 4, 6, 8, 12, 14], "evenNumbersMultiDep is updated"); }); test("an error is thrown when a reduceComputed is defined without an initialValue property", function() { var defineExploder = function() { EmberObject.createWithMixins({ collection: Ember.A(), exploder: 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"); }); test("dependent arrays with multiple item properties are not double-counted", function() { var obj = EmberObject.extend({ items: Ember.A([{ foo: true }, { bar: false }, { bar: true }]), countFooOrBar: 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"); get(obj, 'countFooOrBar'); equal(3, addCalls, "all items added once"); equal(0, removeCalls, "no removes yet"); }); test("dependent arrays can use `replace` with an out of bounds index to add items", function() { var dependentArray = Ember.A(), array; obj = EmberObject.extend({ dependentArray: dependentArray, computed: arrayComputed('dependentArray', { addedItem: function (acc, item, changeMeta) { acc.insertAt(changeMeta.index, item); return acc; }, removedItem: function (acc) { return acc; } }) }).create(); array = 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"); }); test("dependent arrays can use `replace` with a negative index to remove items indexed from the right", function() { var dependentArray = Ember.A([1,2,3,4,5]), array; obj = EmberObject.extend({ dependentArray: dependentArray, computed: arrayComputed('dependentArray', { addedItem: function (acc, item) { return acc; }, removedItem: function (acc, item) { acc.pushObject(item); return acc; } }) }).create(); array = 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"); }); test("dependent arrays that call `replace` with an out of bounds index to remove items is a no-op", function() { var dependentArray = Ember.A([1, 2]), array; obj = EmberObject.extend({ dependentArray: dependentArray, computed: arrayComputed('dependentArray', { addedItem: function (acc, item, changeMeta) { return acc; }, removedItem: function (acc) { ok(false, "no items have been removed"); } }) }).create(); array = get(obj, 'computed'); deepEqual(array, [], "precond - computed array is initially empty"); dependentArray.replace(100, 2); }); test("dependent arrays that call `replace` with a too-large removedCount a) works and b) still right-truncates", function() { var dependentArray = Ember.A([1, 2]), array; obj = EmberObject.extend({ dependentArray: dependentArray, computed: arrayComputed('dependentArray', { addedItem: function (acc, item) { return acc; }, removedItem: function (acc, item) { acc.pushObject(item); return acc; } }) }).create(); array = get(obj, 'computed'); deepEqual(array, [], "precond - computed array is initially empty"); dependentArray.replace(1, 200); deepEqual(array, [2], "array was correctly right-truncated"); }); 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(get(array, 'length') > changeMeta.index, "removedItem not called with invalid index"); array.removeAt(changeMeta.index, 1); return array; } var dependentArray = Ember.A(), options = { addedItem: addedItem, removedItem: removedItem }; obj = EmberObject.extend({ dependentArray: Ember.A([1, 2]), identity0: arrayComputed('dependentArray', options), identity1: arrayComputed('identity0', options) }).create(); get(obj, 'identity1'); run(function() { obj.notifyPropertyChange('dependentArray'); }); ok(true, "removedItem not invoked with invalid index"); }); QUnit.module('arrayComputed - recomputation DKs', { setup: function() { obj = EmberObject.extend({ people: Ember.A([{ name: 'Jaime Lannister', title: 'Kingsguard' }, { name: 'Cersei Lannister', title: 'Queen' }]), titles: arrayComputed('people', { addedItem: function (acc, person) { acc.pushObject(get(person, 'title')); return acc; } }) }).create(); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("recomputations from `arrayComputed` observers add back dependent keys", function() { var meta = metaFor(obj), people = get(obj, 'people'), titles; equal(meta.deps, undefined, "precond - nobody depends on people'"); equal(meta.watching.people, undefined, "precond - nobody is watching people"); titles = get(obj, 'titles'); deepEqual(titles, ["Kingsguard", "Queen"], "precond - value is correct"); ok(meta.deps.people !== undefined, "people has dependencies"); deepEqual(keys(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(function() { set(obj, 'people', Ember.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(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.create({ name: 'a' }), b = EmberObject.create({ name: 'b' }); obj = ArrayProxy.createWithMixins({ content: Ember.A([a, b]), names: arrayComputed('@this.@each.name', { addedItem: function (array, item, changeMeta, instanceMeta) { var mapped = 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(function() { obj.destroy(); }); } }); test("@this can be used to treat the object as the array itself", function() { var names = get(obj, 'names'); deepEqual(names, ['a', 'b'], "precond - names is initially correct"); run(function() { obj.objectAt(1).set('name', 'c'); }); deepEqual(names, ['a', 'c'], "@this can be used with item property observers"); run(function() { obj.pushObject({ name: 'd' }); }); deepEqual(names, ['a', 'c', 'd'], "@this observes new items"); }); QUnit.module('arrayComputed - changeMeta property observers', { setup: function() { callbackItems = []; run(function() { obj = EmberObject.createWithMixins({ items: Ember.A([EmberObject.create({ n: 'zero' }), EmberObject.create({ n: 'one' })]), itemsN: arrayComputed('items.@each.n', { addedItem: function (array, item, changeMeta, instanceMeta) { callbackItems.push('add:' + changeMeta.index + ":" + get(changeMeta.item, 'n')); }, removedItem: function (array, item, changeMeta, instanceMeta) { callbackItems.push('remove:' + changeMeta.index + ":" + get(changeMeta.item, 'n')); } }) }); }); }, teardown: function() { run(function() { obj.destroy(); }); } }); test("changeMeta includes item and index", function() { var expected, items, item; items = get(obj, 'items'); // initial computation add0 add1 run(function() { obj.get('itemsN'); }); // add2 run(function() { items.pushObject(EmberObject.create({ n: 'two' })); }); // remove2 run(function() { items.popObject(); }); // remove0 add0 run(function() { set(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(function() { items.pushObject(EmberObject.create({ n: 'five' })); items.pushObject(EmberObject.create({ n: 'six' })); }); // remove0 add0 run(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(function() { item = items.objectAt(1); items.removeAt(1, 1); }); run(function() { // observer should have been removed from the deleted item item.set('n', 'ten thousand'); }); // [zero'', five, six] -> [zero'', five, seven] // remove2 add2 run(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(function() { item = items.objectAt(1); set(obj, 'items', Ember.A([])); }); run(function() { // observers should have been removed from the items in the old array set(item, 'n', 'eleven thousand'); }); deepEqual(callbackItems, expected, "items removed from the array had observers removed"); }); test("changeMeta includes changedCount and arrayChanged", function() { var callbackLetters = []; var obj = EmberObject.createWithMixins({ letters: Ember.A(['a', 'b']), lettersArrayComputed: 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 = 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"); }); test("`updateIndexes` is not over-eager about skipping retain:n (#4620)", function() { var tracked = Ember.A(); obj = EmberObject.extend({ content: Ember.A([{ n: "one" }, { n: "two" }]), items: arrayComputed('content.@each.n', { addedItem: function (array, item, changeMeta) { tracked.push('+' + get(item, 'n') + '@' + changeMeta.index); array.insertAt(changeMeta.index, item); return array; }, removedItem: function (array, item, changeMeta) { tracked.push('-' + (changeMeta.previousValues ? changeMeta.previousValues.n : get(item, 'n')) + '@' + changeMeta.index); array.removeAt(changeMeta.index); return array; } }) }).create(); run(function () { obj.get('items'); }); deepEqual(tracked, ["+one@0", "+two@1"], "precond - array is set up correctly"); run(function () { obj.get('content').shiftObject(); }); deepEqual(tracked, ["+one@0", "+two@1", "-one@0"], "array handles unshift correctly"); run(function () { 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"); }); test("when initialValue is undefined, everything works as advertised", function() { var chars = EmberObject.createWithMixins({ letters: Ember.A(), firstUpper: reduceComputed('letters', { initialValue: undefined, initialize: function(initialValue, changeMeta, instanceMeta) { instanceMeta.matchingItems = Ember.A(); instanceMeta.subArray = new SubArray(); instanceMeta.firstMatch = function() { return 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(get(chars, 'firstUpper'), undefined, "initialValue is undefined"); get(chars, 'letters').pushObjects(['a', 'b', 'c']); equal(get(chars, 'firstUpper'), undefined, "result is undefined when no matches are present"); get(chars, 'letters').pushObjects(['A', 'B', 'C']); equal(get(chars, 'firstUpper'), 'A', "result is the first match when matching objects are present"); get(chars, 'letters').removeAt(3); equal(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; } }); test("non-array dependencies completely invalidate a reduceComputed CP", function() { var dependentArray = Ember.A(); obj = EmberObject.extend({ nonArray: 'v0', dependentArray: dependentArray, computed: arrayComputed('dependentArray', 'nonArray', { addedItem: function (array) { ++addCalls; return array; }, removedItem: function (array) { --removeCalls; return array; } }) }).create(); 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(function() { set(obj, 'nonArray', 'v1'); }); equal(addCalls, 4, "array completely recomputed when non-array dependency changed"); equal(removeCalls, 0, "remove not called"); }); test("array dependencies specified with `.[]` completely invalidate a reduceComputed CP", function() { var dependentArray = Ember.A(), totallyInvalidatingDependentArray = Ember.A(); obj = EmberObject.extend({ totallyInvalidatingDependentArray: totallyInvalidatingDependentArray, dependentArray: dependentArray, 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(); 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(function() { totallyInvalidatingDependentArray.pushObject(3); }); equal(addCalls, 4, "array completely recomputed when totally invalidating dependent array modified"); equal(removeCalls, 0, "remove not called"); }); test("returning undefined in addedItem/removedItem completely invalidates a reduceComputed CP", function() { var dependentArray = Ember.A([3,2,1]), counter = 0; obj = EmberObject.extend({ dependentArray: dependentArray, 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: observer('computed', function() { counter++; }) }).create(); get(obj, 'computed'); equal(get(obj, 'computed'), 1); equal(counter, 0); dependentArray.pushObject(10); equal(get(obj, 'computed'), 1); equal(counter, 0); dependentArray.removeObject(10); equal(get(obj, 'computed'), 1); equal(counter, 0); dependentArray.removeObject(1); equal(get(obj, 'computed'), 2); equal(counter, 1); }); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.Array) { test("reduceComputed complains about array dependencies that are not `Ember.Array`s", function() { var Type = EmberObject.extend({ rc: reduceComputed('array', { initialValue: 0, addedItem: function(v){ return v; }, removedItem: function(v){ return v; } }) }); expectAssertion(function() { obj = Type.create({ array: [] }); 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.Object.create({ flag: false }); var Item = Ember.Object.extend({ shared: shared, flag: computed('shared.flag', function () { return this.get('shared.flag'); }) }); obj = Ember.Object.extend({ upstream: Ember.A([ Item.create(), Item.create() ]), arrayCP: 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(function () { obj.destroy(); }); } }); 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"); }); }); define("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.'); }); }); define("ember-runtime/tests/controllers/array_controller_test", ["ember-metal/core","ember-runtime/tests/suites/mutable_array","ember-runtime/controllers/array_controller"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var MutableArrayTests = __dependency2__["default"]; var ArrayController = __dependency3__["default"]; QUnit.module("ember-runtime/controllers/array_controller_test"); MutableArrayTests.extend({ name: 'Ember.ArrayController', newObject: function(ary) { var ret = ary ? ary.slice() : this.newFixture(3); return ArrayController.create({ model: Ember.A(ret) }); }, mutate: function(obj) { obj.pushObject(Ember.get(obj, 'length')+1); }, toArray: function(obj) { return obj.toArray ? obj.toArray() : obj.slice(); } }).run(); test("defaults it's `model` to an empty array", function () { var Controller = ArrayController.extend(); deepEqual(Controller.create().get("model"), [], "`ArrayController` defaults it's model to an empty array"); equal(Controller.create().get('firstObject'), undefined, 'can fetch firstObject'); equal(Controller.create().get('lastObject'), undefined, 'can fetch lastObject'); }); test("Ember.ArrayController length property works even if model was not set initially", function() { var controller = ArrayController.create(); controller.pushObject('item'); equal(controller.get('length'), 1); }); }); define("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.'); }); }); define("ember-runtime/tests/controllers/controller_test", ["ember-metal/core","ember-runtime/controllers/controller","ember-runtime/controllers/object_controller","ember-metal/mixin"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var Controller = __dependency2__["default"]; var ObjectController = __dependency3__["default"]; var Mixin = __dependency4__["default"]; QUnit.module('Controller event handling'); test("Action can be handled by a function on actions object", function() { expect(1); var TestController = Controller.extend({ actions: { poke: function() { ok(true, 'poked'); } } }); var controller = TestController.create({}); controller.send("poke"); }); // TODO: Can we support this? // 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"); // }); test("When `_actions` is provided, `actions` is left alone", function() { expect(2); var TestController = Controller.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'); }); test("Actions object doesn't shadow a proxied object's 'actions' property", function() { var TestController = ObjectController.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"); }); test("A handled action can be bubbled to the target for continued processing", function() { expect(2); var TestController = Controller.extend({ actions: { poke: function() { ok(true, 'poked 1'); return true; } } }); var controller = TestController.create({ target: Controller.extend({ actions: { poke: function() { ok(true, 'poked 2'); } } }).create() }); controller.send("poke"); }); test("Action can be handled by a superclass' actions object", function() { expect(4); var SuperController = Controller.extend({ actions: { foo: function() { ok(true, 'foo'); }, bar: function(msg) { equal(msg, "HELLO"); } } }); var BarControllerMixin = Mixin.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'); if (!Ember.FEATURES.isEnabled('ember-routing-drop-deprecated-action-style')) { test("Action can be handled by method directly on controller (DEPRECATED)", function() { expectDeprecation(/Action handlers implemented directly on controllers are deprecated/); var TestController = Controller.extend({ poke: function() { ok(true, 'poked'); } }); var controller = TestController.create({}); controller.send("poke"); }); } QUnit.module('Controller Content -> Model Alias'); test("`model` is aliased as `content`", function() { expect(1); var controller = Controller.extend({ model: 'foo-bar' }).create(); equal(controller.get('content'), 'foo-bar', 'content is an alias of model'); }); test("`content` is moved to `model` when `model` is unset", function() { expect(2); var controller; ignoreDeprecation(function() { controller = Controller.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'); }); test("specifying `content` (without `model` specified) results in deprecation", function() { expect(1); var controller; expectDeprecation(function() { controller = Controller.extend({ content: 'foo-bar' }).create(); }, 'Do not specify `content` on a Controller, use `model` instead.'); }); test("specifying `content` (with `model` specified) does not result in deprecation", function() { expect(1); expectNoDeprecation(); Controller.extend({ content: 'foo-bar', model: 'blammo' }).create(); }); }); define("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.'); }); }); define("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/object_controller","ember-runtime/computed/reduce_computed_macros","container"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; var Ember = __dependency1__["default"]; var guidFor = __dependency2__.guidFor; var run = __dependency3__["default"]; var get = __dependency4__.get; var computed = __dependency5__.computed; var compare = __dependency6__["default"]; var EmberObject = __dependency7__["default"]; var ArrayController = __dependency8__["default"]; var ObjectController = __dependency9__["default"]; var sort = __dependency10__.sort; var Container = __dependency11__["default"]; var lannisters, arrayController, controllerClass, otherControllerClass, container, itemControllerCount, tywin, jaime, cersei, tyrion; QUnit.module("Ember.ArrayController - itemController", { setup: function() { container = new Container(); tywin = EmberObject.create({ name: 'Tywin' }); jaime = EmberObject.create({ name: 'Jaime' }); cersei = EmberObject.create({ name: 'Cersei' }); tyrion = EmberObject.create({ name: 'Tyrion' }); lannisters = Ember.A([ tywin, jaime, cersei ]); itemControllerCount = 0; controllerClass = ObjectController.extend({ init: function() { ++itemControllerCount; this._super(); }, toString: function() { return "itemController for " + this.get('name'); } }); otherControllerClass = ObjectController.extend({ toString: function() { return "otherItemController for " + this.get('name'); } }); container.register("controller:Item", controllerClass); container.register("controller:OtherItem", otherControllerClass); }, teardown: function() { run(function() { container.destroy(); }); } }); function createUnwrappedArrayController() { arrayController = ArrayController.create({ container: container, model: lannisters }); } function createArrayController() { arrayController = ArrayController.create({ container: container, itemController: 'Item', model: lannisters }); } function createDynamicArrayController() { arrayController = ArrayController.create({ container: container, lookupItemController: function(object) { if ("Tywin" === object.get("name")) { return "Item"; } else { return "OtherItem"; } }, model: lannisters }); } 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); 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 = get(arrayController, '_subControllers'), jaimeController = subControllers[1], cerseiController = subControllers[2]; equal(!!jaimeController.isDestroying, false, "precond - nobody is destroyed yet"); equal(!!cerseiController.isDestroying, false, "precond - nobody is destroyed yet"); run(function() { arrayController.set('model', Ember.A()); }); equal(!!jaimeController.isDestroying, true, "old subcontainers are destroyed"); equal(!!cerseiController.isDestroying, true, "old subcontainers are destroyed"); }); 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"); }); test("when items are removed from the arrayController, their respective subcontainers are destroyed", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1), cerseiController = arrayController.objectAtContent(2), subControllers = get(arrayController, '_subControllers'); equal(!!jaimeController.isDestroyed, false, "precond - nobody is destroyed yet"); equal(!!cerseiController.isDestroyed, false, "precond - nobody is destroyed yet"); run(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"); }); test("one cannot remove wrapped model directly when specifying `itemController`", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1), 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(function() { arrayController.removeObject(cerseiController); }); equal(arrayController.get('length'), 2, "can remove wrapper objects"); }); test("when items are removed from the underlying array, their respective subcontainers are destroyed", function() { createArrayController(); var jaimeController = arrayController.objectAtContent(1), cerseiController = arrayController.objectAtContent(2), subContainers = get(arrayController, 'subContainers'); equal(!!jaimeController.isDestroying, false, "precond - nobody is destroyed yet"); equal(!!cerseiController.isDestroying, false, "precond - nobody is destroyed yet"); run(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"); }); test("`itemController` can be dynamic by overwriting `lookupItemController`", function() { createDynamicArrayController(); var tywinController = arrayController.objectAtContent(0), 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"); }); test("when `idx` is out of range, `lookupItemController` is not called", function() { arrayController = ArrayController.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"); }); test("if `lookupItemController` returns a string, it must be resolvable by the container", function() { arrayController = ArrayController.create({ container: container, lookupItemController: function(object) { return "NonExistant"; }, model: lannisters }); throws(function() { arrayController.objectAtContent(1); }, /NonExistant/, "`lookupItemController` must return either null or a valid controller name"); }); test("target and parentController are set to the concrete parentController", function() { var parent = ArrayController.create({ }); // typically controller created for {{each itemController="foo"}} var virtual = ArrayController.create({ itemController: 'Item', container: container, target: parent, parentController: parent, _isVirtual: true, model: Ember.A([ { name: 'kris seldenator' } ]) }); var itemController = virtual.objectAtContent(0); equal(itemController.get('parentController'), parent); equal(itemController.get('target'), parent); run(function() { parent.destroy(); virtual.destroy(); }); }); test("array observers can invoke `objectAt` without overwriting existing item controllers", function() { createArrayController(); var tywinController = arrayController.objectAtContent(0), arrayObserverCalled = false; arrayController.reopen({ lannistersWillChange: Ember.K, lannistersDidChange: function(_, idx, removedAmt, addedAmt) { arrayObserverCalled = true; equal(this.objectAt(idx).get('name'), "Tyrion", "Array observers get the right object via `objectAt`"); } }); arrayController.addArrayObserver(arrayController, { willChange: 'lannistersWillChange', didChange: 'lannistersDidChange' }); run(function() { lannisters.unshiftObject(tyrion); }); equal(arrayObserverCalled, true, "Array observers are called normally"); equal(tywinController.get('name'), "Tywin", "Array observers calling `objectAt` does not overwrite existing controllers' model"); }); test("`itemController`'s life cycle should be entangled with its parent controller", function() { createDynamicArrayController(); var tywinController = arrayController.objectAtContent(0), jaimeController = arrayController.objectAtContent(1); run(arrayController, 'destroy'); equal(tywinController.get('isDestroyed'), true); equal(jaimeController.get('isDestroyed'), true); }); QUnit.module('Ember.ArrayController - itemController with arrayComputed', { setup: function() { container = new Container(); cersei = EmberObject.create({ name: 'Cersei' }); jaime = EmberObject.create({ name: 'Jaime' }); lannisters = Ember.A([ jaime, cersei ]); controllerClass = ObjectController.extend({ title: computed(function () { switch (get(this, 'name')) { case 'Jaime': return 'Kingsguard'; case 'Cersei': return 'Queen'; } }).property('name'), toString: function() { return "itemController for " + this.get('name'); } }); container.register("controller:Item", controllerClass); }, teardown: function() { run(function() { container.destroy(); }); } }); test("item controllers can be used to provide properties for array computed macros", function() { createArrayController(); ok(compare(guidFor(cersei), guidFor(jaime)) < 0, "precond - guid tiebreaker would fail test"); arrayController.reopen({ sortProperties: Ember.A(['title']), sorted: sort('@this', 'sortProperties') }); deepEqual(arrayController.get('sorted').mapProperty('name'), ['Jaime', 'Cersei'], "ArrayController items can be sorted on itemController properties"); }); }); define("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.'); }); }); define("ember-runtime/tests/controllers/object_controller_test", ["ember-runtime/controllers/object_controller","ember-metal/mixin"], function(__dependency1__, __dependency2__) { "use strict"; var ObjectController = __dependency1__["default"]; var observer = __dependency2__.observer; QUnit.module("Ember.ObjectController"); test("should be able to set the target property of an ObjectController", function() { var controller = ObjectController.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 test("can observe a path on an ObjectController", function() { var controller = ObjectController.extend({ baz: observer('foo.bar', function() {}) }).create(); controller.set('model', {}); ok(true, "should not fail"); }); }); define("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.'); }); }); define("ember-runtime/tests/core/compare_test", ["ember-metal/utils","ember-runtime/system/object","ember-runtime/compare"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var typeOf = __dependency1__.typeOf; var EmberObject = __dependency2__["default"]; var compare = __dependency3__["default"]; var data = []; 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.create(); data[13] = function (a) {return a;}; data[14] = new Date('2012/01/01'); data[15] = new Date('2012/06/06'); } }); test("ordering should work", function() { var suspect, comparable, failureMessage, suspectIndex, comparableIndex; for (suspectIndex = 0; suspectIndex < data.length; suspectIndex++) { suspect = data[suspectIndex]; equal(compare(suspect, suspect), 0, suspectIndex + ' should equal itself'); for (comparableIndex = suspectIndex + 1; comparableIndex < data.length; comparableIndex++) { comparable = data[comparableIndex]; failureMessage = 'data[' + suspectIndex + '] (' + typeOf(suspect) + ') should be smaller than data[' + comparableIndex + '] (' + typeOf(comparable) + ')'; equal(compare(suspect, comparable), -1, failureMessage); } } }); }); define("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.'); }); }); define("ember-runtime/tests/core/copy_test", ["ember-runtime/copy"], function(__dependency1__) { "use strict"; var copy = __dependency1__["default"]; QUnit.module("Ember Copy Method"); test("Ember.copy null", function() { var obj = {field: null}; equal(copy(obj, true).field, null, "null should still be null"); }); test("Ember.copy date", function() { var date = new Date(2014, 7, 22), dateCopy = copy(date); equal(date.getTime(), dateCopy.getTime(), "dates should be equivalent"); }); }); define("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.'); }); }); define("ember-runtime/tests/core/isEqual_test", ["ember-runtime/core"], function(__dependency1__) { "use strict"; var isEqual = __dependency1__.isEqual; QUnit.module("isEqual"); test("undefined and null", function() { ok( isEqual(undefined, undefined), "undefined is equal to undefined" ); ok( !isEqual(undefined, null), "undefined is not equal to null" ); ok( isEqual(null, null), "null is equal to null" ); ok( !isEqual(null, undefined), "null is not equal to undefined" ); }); test("strings should be equal",function() { ok( !isEqual("Hello", "Hi"), "different Strings are unequal" ); ok( isEqual("Hello", "Hello"), "same Strings are equal" ); }); test("numericals should be equal",function() { ok( isEqual(24, 24), "same numbers are equal" ); ok( !isEqual(24, 21), "different numbers are inequal" ); }); test("dates should be equal",function() { ok ( isEqual(new Date(1985, 7, 22), new Date(1985, 7, 22)), "same dates are equal" ); ok ( !isEqual(new Date(2014, 7, 22), new Date(1985, 7, 22)), "different dates are not equal" ); }); test("array should be equal",function() { // NOTE: We don't test for array contents -- that would be too expensive. ok( !isEqual( [1,2], [1,2] ), 'two array instances with the same values should not be equal' ); ok( !isEqual( [1,2], [1] ), 'two array instances with different values should not be equal' ); }); test("first object implements isEqual should use it", function() { ok(isEqual({ isEqual: function() { return true; } }, null), 'should return true always'); var obj = { isEqual: function() { return false; } }; equal(isEqual(obj, obj), false, 'should return false because isEqual returns false'); }); }); define("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.'); }); }); define("ember-runtime/tests/core/is_array_test", ["ember-metal/core","ember-metal/utils","ember-runtime/system/array_proxy"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var isArray = __dependency2__.isArray; var ArrayProxy = __dependency3__["default"]; QUnit.module("Ember Type Checking"); test("Ember.isArray" ,function() { var arrayProxy = ArrayProxy.create({ content: Ember.A() }); equal(isArray(arrayProxy), true, "[]"); }); }); define("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.'); }); }); define("ember-runtime/tests/core/is_empty_test", ["ember-metal/core","ember-metal/is_empty","ember-runtime/system/array_proxy"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var isEmpty = __dependency2__.isEmpty; var ArrayProxy = __dependency3__["default"]; QUnit.module("Ember.isEmpty"); test("Ember.isEmpty", function() { var arrayProxy = ArrayProxy.create({ content: Ember.A() }); equal(true, isEmpty(arrayProxy), "for an ArrayProxy that has empty content"); }); }); define("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.'); }); }); define("ember-runtime/tests/core/keys_test", ["ember-metal/property_set","ember-runtime/keys","ember-metal/observer","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var set = __dependency1__.set; var keys = __dependency2__["default"]; var addObserver = __dependency3__.addObserver; var removeObserver = __dependency3__.removeObserver; var EmberObject = __dependency4__["default"]; QUnit.module("Fetch Keys "); 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(object1); deepEqual(object2, ['names','age','place']); }); test("should get a key array for a specified Ember.Object", function() { var object1 = EmberObject.create({ names: "Rahul", age: "23", place: "Mangalore" }); var object2 = keys(object1); deepEqual(object2, ['names','age','place']); }); // This test is for IE8. test("should get a key array for property that is named the same as prototype property", function() { var object1 = { toString: function() {} }; var object2 = keys(object1); deepEqual(object2, ['toString']); }); test('should not contain properties declared in the prototype', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); deepEqual(keys(beer), []); }); test('should return properties that were set after object creation', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); set(beer, 'brand', 'big daddy'); deepEqual(keys(beer), ['brand']); }); QUnit.module('Keys behavior with observers'); test('should not leak properties on the prototype', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); addObserver(beer, 'type', Ember.K); deepEqual(keys(beer), []); removeObserver(beer, 'type', Ember.K); }); test('observing a non existent property', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); addObserver(beer, 'brand', Ember.K); deepEqual(keys(beer), []); set(beer, 'brand', 'Corona'); deepEqual(keys(beer), ['brand']); removeObserver(beer, 'brand', Ember.K); }); test('with observers switched on and off', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); addObserver(beer, 'type', Ember.K); removeObserver(beer, 'type', Ember.K); deepEqual(keys(beer), []); }); test('observers switched on and off with setter in between', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); addObserver(beer, 'type', Ember.K); set(beer, 'type', 'ale'); removeObserver(beer, 'type', Ember.K); deepEqual(keys(beer), ['type']); }); test('observer switched on and off and then setter', function () { var beer = EmberObject.extend({ type: 'ipa' }).create(); addObserver(beer, 'type', Ember.K); removeObserver(beer, 'type', Ember.K); set(beer, 'type', 'ale'); deepEqual(keys(beer), ['type']); }); }); define("ember-runtime/tests/core/keys_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/core'); test('ember-runtime/tests/core/keys_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/core/keys_test.js should pass jshint.'); }); }); define("ember-runtime/tests/core/type_test", ["ember-metal/utils","ember-runtime/system/object"], function(__dependency1__, __dependency2__) { "use strict"; var typeOf = __dependency1__.typeOf; var EmberObject = __dependency2__["default"]; QUnit.module("Ember Type Checking"); test("Ember.typeOf", function() { var a = null, arr = [1,2,3], obj = {}, object = EmberObject.create({ method: function() {} }); equal(typeOf(undefined), 'undefined', "item of type undefined"); equal(typeOf(a), 'null', "item of type null"); equal(typeOf(arr), 'array', "item of type array"); equal(typeOf(obj), 'object', "item of type object"); equal(typeOf(object), 'instance', "item of type instance"); equal(typeOf(object.method), 'function', "item of type function") ; equal(typeOf(EmberObject), 'class', "item of type class"); equal(typeOf(new Error()), 'error', "item of type error"); }); }); define("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.'); }); }); define("ember-runtime/tests/ext/function_test", ["ember-runtime/tests/props_helper"], function(__dependency1__) { "use strict"; var testWithDefault = __dependency1__.testWithDefault; var testBoth = __dependency1__.testBoth; QUnit.module('Function.prototype.observes() 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'); 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'); }); 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'); 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'); }); }); define("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.'); }); }); define("ember-runtime/tests/ext/mixin_test", ["ember-metal/property_set","ember-metal/property_get","ember-metal/mixin","ember-metal/platform","ember-metal/binding","ember-metal/run_loop"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var set = __dependency1__.set; var get = __dependency2__.get; var Mixin = __dependency3__.Mixin; var create = __dependency4__.create; var platform = __dependency4__.platform; var Binding = __dependency5__.Binding; var isGlobalPath = __dependency5__.isGlobalPath; var bind = __dependency5__.bind; var oneWay = __dependency5__.oneWay; var run = __dependency6__["default"]; QUnit.module('system/mixin/binding_test'); test('Defining a property ending in Binding should setup binding when applied', function() { var MyMixin = Mixin.create({ fooBinding: 'bar.baz' }); var obj = { bar: { baz: 'BIFF' } }; run(function() { MyMixin.apply(obj); }); ok(get(obj, 'fooBinding') instanceof Binding, 'should be a binding object'); equal(get(obj, 'foo'), 'BIFF', 'binding should be created and synced'); }); test('Defining a property ending in Binding should apply to prototype children', function() { var MyMixin, obj, obj2; run(function() { MyMixin = Mixin.create({ fooBinding: 'bar.baz' }); }); obj = { bar: { baz: 'BIFF' } }; run(function() { MyMixin.apply(obj); }); obj2 = create(obj); run(function() { set(get(obj2, 'bar'), 'baz', 'BARG'); }); ok(get(obj2, 'fooBinding') instanceof Binding, 'should be a binding object'); equal(get(obj2, 'foo'), 'BARG', 'binding should be created and synced'); }); }); define("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.'); }); }); define("ember-runtime/tests/ext/rsvp_test", ["ember-metal/run_loop","ember-runtime/ext/rsvp"], function(__dependency1__, __dependency2__) { "use strict"; /* global Promise:true */ var run = __dependency1__["default"]; var RSVP = __dependency2__["default"]; QUnit.module('Ember.RSVP'); 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(function(){ new RSVP.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.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; } }); test("given `Ember.testing = true`, correctly informs the test suite about async steps", function() { expect(19); ok(!run.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); }); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var EmberObject = __dependency5__["default"]; var addObserver = __dependency6__.addObserver; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests are the same except for places where we intend to break the API we instead validate that we warn the developer appropriately. CHANGES FROM 1.6: * changed obj.set() and obj.get() to Ember.set() and Ember.get() * changed obj.addObserver() to addObserver() */ QUnit.module("Ember.Observable - Observing with @each"); test("chained observers on enumerable properties are triggered when the observed property of any item changes", function() { var family = EmberObject.create({ momma: null }); var momma = EmberObject.create({ children: [] }); var child1 = EmberObject.create({ name: "Bartholomew" }); var child2 = EmberObject.create({ name: "Agnes" }); var child3 = EmberObject.create({ name: "Dan" }); var child4 = EmberObject.create({ name: "Nancy" }); set(family, 'momma', momma); set(momma, 'children', Ember.A([child1, child2, child3])); var observerFiredCount = 0; addObserver(family, 'momma.children.@each.name', this, function() { observerFiredCount++; }); observerFiredCount = 0; run(function() { get(momma, 'children').setEach('name', 'Juan'); }); equal(observerFiredCount, 3, "observer fired after changing child names"); observerFiredCount = 0; run(function() { get(momma, 'children').pushObject(child4); }); equal(observerFiredCount, 1, "observer fired after adding a new item"); observerFiredCount = 0; run(function() { set(child4, 'name', "Herbert"); }); equal(observerFiredCount, 1, "observer fired after changing property on new object"); set(momma, 'children', []); observerFiredCount = 0; run(function() { set(child1, 'name', "Hanna"); }); equal(observerFiredCount, 0, "observer did not fire after removing changing property on a removed object"); }); }); define("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.'); }); }); define("ember-runtime/tests/legacy_1x/mixins/observable/observable_test", ["ember-metal/property_get","ember-metal/property_set","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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var forEach = __dependency3__.forEach; var computed = __dependency4__.computed; var run = __dependency5__["default"]; var typeOf = __dependency6__.typeOf; var observer = __dependency7__.observer; var fmt = __dependency8__.fmt; var w = __dependency8__.w; var EmberObject = __dependency9__["default"]; var Observable = __dependency10__["default"]; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests are the same except for places where we intend to break the API we instead validate that we warn the developer appropriately. CHANGES FROM 1.6: * Added ObservableObject which applies the Ember.Observable mixin. * Changed reference to Ember.T_FUNCTION to 'function' * Changed all references to sc_super to this._super() * Changed Ember.objectForPropertyPath() to Ember.getPath() * Removed allPropertiesDidChange test - no longer supported * Changed test that uses 'ObjectE' as path to 'objectE' to reflect new rule on using capital letters for property paths. * Removed test passing context to addObserver. context param is no longer supported. * Changed calls to Ember.Binding.flushPendingChanges() -> run.sync() * removed test in observer around line 862 that expected key/value to be the last item in the chained path. Should be root and chained path */ // ======================================================================== // Ember.Observable Tests // ======================================================================== var object, ObjectC, ObjectD, objectA, objectB; var ObservableObject = EmberObject.extend(Observable); var originalLookup = Ember.lookup, lookup; // .......................................................... // GET() // QUnit.module("object.get()", { setup: function() { object = ObservableObject.createWithMixins(Observable, { normal: 'value', numberVal: 24, toggleVal: true, computed: computed(function() { return 'value'; })["volatile"](), method: function() { return "value"; }, nullProperty: null, unknownProperty: function(key, value) { this.lastUnknownProperty = key ; return "unknown" ; } }); } }); test("should get normal properties", function() { equal(object.get('normal'), 'value') ; }); test("should call computed properties and return their result", function() { equal(object.get("computed"), "value") ; }); test("should return the function for a non-computed property", function() { var value = object.get("method") ; equal(typeOf(value), 'function') ; }); test("should return null when property value is null", function() { equal(object.get("nullProperty"), null) ; }); 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(function() { return 'value'; })["volatile"](), method: function() { return "value"; }, nullProperty: null, unknownProperty: function(key, value) { this.lastUnknownProperty = key ; return "unknown" ; } }); objectB = { normal: 'value', nullProperty: null }; } }); test("should get normal properties on Ember.Observable", function() { equal(get(objectA, 'normal'), 'value') ; }); test("should call computed properties on Ember.Observable and return their result", function() { equal(get(objectA, "computed"), "value") ; }); test("should return the function for a non-computed property on Ember.Observable", function() { var value = get(objectA, "method") ; equal(typeOf(value), 'function') ; }); test("should return null when property value is null on Ember.Observable", function() { equal(get(objectA, "nullProperty"), null) ; }); test("should call unknownProperty when value is undefined on Ember.Observable", function() { equal(get(object, "unknown"), "unknown") ; equal(object.lastUnknownProperty, "unknown") ; }); test("should get normal properties on standard objects", function() { equal(get(objectB, 'normal'), 'value'); }); test("should return null when property is null on standard objects", function() { equal(get(objectB, 'nullProperty'), null); }); /* test("raise if the provided object is null", function() { raises(function() { get(null, 'key'); }); }); */ test("raise if the provided object is undefined", function() { expectAssertion(function() { get(undefined, 'key'); }, /Cannot call get with 'key' on an undefined object/i); }); test("should work when object is Ember (used in Ember.get)", function() { equal(get('Ember.RunLoop'), Ember.RunLoop, 'Ember.get'); equal(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; } }); test("should return a property at a given path relative to the lookup", function() { lookup.Foo = ObservableObject.create({ Bar: ObservableObject.createWithMixins({ Baz: computed(function() { return "blargh"; })["volatile"]() }) }); equal(get('Foo.Bar.Baz'), "blargh"); }); test("should return a property at a given path relative to the passed object", function() { var foo = ObservableObject.create({ bar: ObservableObject.createWithMixins({ baz: computed(function() { return "blargh"; })["volatile"]() }) }); equal(get(foo, 'bar.baz'), "blargh"); }); test("should return a property at a given path relative to the lookup - JavaScript hash", function() { lookup.Foo = { Bar: { Baz: "blargh" } }; equal(get('Foo.Bar.Baz'), "blargh"); }); test("should return a property at a given path relative to the passed object - JavaScript hash", function() { var foo = { bar: { baz: "blargh" } }; equal(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(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 ; } }); } }); test("should change normal properties and return this", function() { var ret = object.set("normal", "changed") ; equal(object.normal, "changed") ; equal(ret, object) ; }); test("should call computed properties passing value and return this", function() { var ret = object.set("computed", "changed") ; equal(object._computed, "changed") ; equal(ret, object) ; }); test("should change normal properties when passing undefined", function() { var ret = object.set('normal', undefined); equal(object.normal, undefined); equal(ret, object); }); 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(typeOf(object.method) !== 'function') ; equal(ret, object) ; }); test("should replace prover when property value is null", function() { var ret = object.set("nullProperty", "changed") ; equal(object.nullProperty, "changed") ; equal(ret, object) ; }); 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(function(key, value) { this.computedCalls.push(value); return 'computed'; })["volatile"](), computedCachedCalls: [], computedCached: computed(function(key, value) { this.computedCachedCalls.push(value); return 'computedCached'; }), // DEPENDENT KEYS changer: 'foo', dependentCalls: [], dependent: computed(function(key, value) { this.dependentCalls.push(value); return 'dependent'; }).property('changer')["volatile"](), dependentFrontCalls: [], dependentFront: computed('changer', function(key, value) { this.dependentFrontCalls.push(value); return 'dependentFront'; })["volatile"](), dependentCachedCalls: [], dependentCached: computed(function(key, value) { this.dependentCachedCalls.push(value); return 'dependentCached'; }).property('changer'), // everytime it is recomputed, increments call incCallCount: 0, inc: computed(function() { return this.incCallCount++; }).property('changer'), // depends on cached property which depends on another property... nestedIncCallCount: 0, nestedInc: computed(function(key, value) { get(this, 'inc'); return this.nestedIncCallCount++; }).property('inc'), // two computed properties that depend on a third property state: 'on', isOn: computed(function(key, value) { if (value !== undefined) this.set('state', 'on'); return this.get('state') === 'on'; }).property('state')["volatile"](), isOff: computed(function(key, value) { if (value !== undefined) this.set('state', 'off'); return this.get('state') === 'off'; }).property('state')["volatile"]() }) ; }, teardown: function() { Ember.lookup = originalLookup; } }); test("getting values should call function return value", function() { // get each property twice. Verify return. var keys = w('computed computedCached dependent dependentFront dependentCached'); forEach(keys, function(key) { equal(object.get(key), key, fmt('Try #1: object.get(%@) should run function', [key])); equal(object.get(key), key, fmt('Try #2: object.get(%@) should run function', [key])); }); // verify each call count. cached should only be called once forEach(w('computedCalls dependentFrontCalls dependentCalls'), function(key) { equal(object[key].length, 2, fmt('non-cached property %@ should be called 2x', [key])); }); forEach(w('computedCachedCalls dependentCachedCalls'), function(key) { equal(object[key].length, 1, fmt('non-cached property %@ should be called 1x', [key])); }); }); test("setting values should call function return value", function() { // get each property twice. Verify return. var keys = w('computed dependent dependentFront computedCached dependentCached'); var values = w('value1 value2'); forEach(keys, function(key) { equal(object.set(key, values[0]), object, fmt('Try #1: object.set(%@, %@) should run function', [key, values[0]])); equal(object.set(key, values[1]), object, fmt('Try #2: object.set(%@, %@) should run function', [key, values[1]])); equal(object.set(key, values[1]), object, 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 forEach(keys, function(key) { var calls = object[key + 'Calls'], idx; var expectedLength; // Cached properties first check their cached value before setting the // property. Other properties blindly call set. expectedLength = 3; equal(calls.length, expectedLength, fmt('set(%@) should be called the right amount of times', [key])); for(idx=0;idx<2;idx++) { equal(calls[idx], values[idx], fmt('call #%@ to set(%@) should have passed value %@', [idx+1, key, values[idx]])); } }); }); 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'); }); 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 }); 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 }); 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 }); 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. 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'); }); 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'); }); test("dependent keys should be able to be specified as property paths", function() { var depObj = ObservableObject.createWithMixins({ menu: ObservableObject.create({ price: 5 }), menuPrice: 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"); }); test("nested dependent keys should propagate after they update", function() { var bindObj; run(function () { lookup.DepObj = ObservableObject.createWithMixins({ restaurant: ObservableObject.create({ menu: ObservableObject.create({ price: 5 }) }), price: 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(function () { lookup.DepObj.set('restaurant.menu.price', 10); }); equal(bindObj.get('price'), 10, "binding propagates after a nested dependent keys updates"); run(function () { lookup.DepObj.set('restaurant.menu', ObservableObject.create({ price: 15 })); }); equal(bindObj.get('price'), 15, "binding propagates after a middle dependent keys updates"); }); test("cacheable nested dependent keys should clear after their dependencies update", function() { ok(true); var DepObj; run(function() { lookup.DepObj = DepObj = ObservableObject.createWithMixins({ restaurant: ObservableObject.create({ menu: ObservableObject.create({ price: 5 }) }), price: computed(function() { return this.get('restaurant.menu.price'); }).property('restaurant.menu.price') }); }); equal(DepObj.get('price'), 5, "precond - computed property is correct"); run(function() { DepObj.set('restaurant.menu.price', 10); }); equal(DepObj.get('price'), 10, "cacheable computed properties are invalidated even if no run loop occurred"); run(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(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(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 run.sync(); * changes obj.set() and obj.get() to Ember.set() and Ember.get() * Fixed an actual bug in unit tests around line 133 * fixed 'bindings should disconnect on destroy' test to use destroy. */ // ======================================================================== // EmberObject bindings Tests // ======================================================================== var testObject, fromObject, extraObject, TestObject; var TestNamespace, originalLookup, lookup; var bindModuleOpts = { setup: function() { originalLookup = Ember.lookup; Ember.lookup = lookup = {}; testObject = EmberObject.create({ foo: "bar", bar: "foo", extraObject: null }); fromObject = EmberObject.create({ bar: "foo", extraObject: null }) ; extraObject = EmberObject.create({ foo: "extraObjectValue" }) ; lookup['TestNamespace'] = TestNamespace = { fromObject: fromObject, testObject: testObject } ; }, teardown: function() { testObject = fromObject = extraObject = null ; Ember.lookup = originalLookup; } }; QUnit.module("bind() method", bindModuleOpts); test("bind(TestNamespace.fromObject.bar) should follow absolute path", function() { run(function() { // create binding testObject.bind("foo", "TestNamespace.fromObject.bar"); // now make a change to see if the binding triggers. set(fromObject, "bar", "changedValue"); }); equal("changedValue", get(testObject, "foo"), "testObject.foo"); }); test("bind(.bar) should bind to relative path", function() { run(function() { // create binding testObject.bind("foo", "bar") ; // now make a change to see if the binding triggers. set(testObject, "bar", "changedValue") ; }); equal("changedValue", get(testObject, "foo"), "testObject.foo"); }); var fooBindingModuleOpts = { setup: function() { originalLookup = Ember.lookup; Ember.lookup = lookup = {}; TestObject = EmberObject.extend({ foo: "bar", bar: "foo", extraObject: null }); fromObject = EmberObject.create({ bar: "foo", extraObject: null }) ; extraObject = EmberObject.create({ foo: "extraObjectValue" }) ; lookup['TestNamespace'] = TestNamespace = { fromObject: fromObject, testObject: TestObject } ; }, teardown: function() { Ember.lookup = originalLookup; TestObject = fromObject = extraObject = null ; // delete TestNamespace ; } }; QUnit.module("fooBinding method", fooBindingModuleOpts); test("fooBinding: TestNamespace.fromObject.bar should follow absolute path", function() { // create binding run(function() { testObject = TestObject.createWithMixins({ fooBinding: "TestNamespace.fromObject.bar" }) ; // now make a change to see if the binding triggers. set(fromObject, "bar", "changedValue") ; }); equal("changedValue", get(testObject, "foo"), "testObject.foo"); }); test("fooBinding: .bar should bind to relative path", function() { run(function() { testObject = TestObject.createWithMixins({ fooBinding: "bar" }); // now make a change to see if the binding triggers. set(testObject, "bar", "changedValue"); }); equal("changedValue", get(testObject, "foo"), "testObject.foo"); }); test('fooBinding: should disconnect bindings when destroyed', function () { run(function() { testObject = TestObject.createWithMixins({ fooBinding: "TestNamespace.fromObject.bar" }); set(TestNamespace.fromObject, 'bar', 'BAZ'); }); equal(get(testObject, 'foo'), 'BAZ', 'binding should have synced'); destroy(testObject); run(function() { set(TestNamespace.fromObject, 'bar', 'BIFF'); }); ok(get(testObject, 'foo') !== 'bar', 'binding should not have synced'); }); }); define("ember-runtime/tests/legacy_1x/system/object/bindings_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/legacy_1x/system/object'); test('ember-runtime/tests/legacy_1x/system/object/bindings_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/legacy_1x/system/object/bindings_test.js should pass jshint.'); }); }); define("ember-runtime/tests/legacy_1x/system/object/concatenated_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var EmberStringUtils = __dependency4__["default"]; var EmberObject = __dependency5__["default"]; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests are the same except for places where we intend to break the API we instead validate that we warn the developer appropriately. CHANGES FROM 1.6: * changed get(obj, ) and set(obj, ) to Ember.get() and Ember.set() * converted uses of obj.isEqual() to use deepEqual() test since isEqual is not always defined */ var klass; QUnit.module("EmberObject Concatenated Properties", { setup: function() { klass = EmberObject.extend({ concatenatedProperties: ['values', 'functions'], values: ['a', 'b', 'c'], functions: [Ember.K] }); } }); test("concatenates instances", function() { var obj = klass.create({ values: ['d', 'e', 'f'] }); var values = get(obj, 'values'), expected = ['a', 'b', 'c', 'd', 'e', 'f']; deepEqual(values, expected, EmberStringUtils.fmt("should concatenate values property (expected: %@, got: %@)", [expected, values])); }); test("concatenates subclasses", function() { var subKlass = klass.extend({ values: ['d', 'e', 'f'] }); var obj = subKlass.create(); var values = get(obj, 'values'), expected = ['a', 'b', 'c', 'd', 'e', 'f']; deepEqual(values, expected, EmberStringUtils.fmt("should concatenate values property (expected: %@, got: %@)", [expected, values])); }); test("concatenates reopen", function() { klass.reopen({ values: ['d', 'e', 'f'] }); var obj = klass.create(); var values = get(obj, 'values'), expected = ['a', 'b', 'c', 'd', 'e', 'f']; deepEqual(values, expected, EmberStringUtils.fmt("should concatenate values property (expected: %@, got: %@)", [expected, values])); }); test("concatenates mixin", function() { var mixin = { values: ['d', 'e'] }; var subKlass = klass.extend(mixin, { values: ['f'] }); var obj = subKlass.create(); var values = get(obj, 'values'), expected = ['a', 'b', 'c', 'd', 'e', 'f']; deepEqual(values, expected, EmberStringUtils.fmt("should concatenate values property (expected: %@, got: %@)", [expected, values])); }); test("concatenates reopen, subclass, and instance", function() { klass.reopen({ values: ['d'] }); var subKlass = klass.extend({ values: ['e'] }); var obj = subKlass.create({ values: ['f'] }); var values = get(obj, 'values'), expected = ['a', 'b', 'c', 'd', 'e', 'f']; deepEqual(values, expected, EmberStringUtils.fmt("should concatenate values property (expected: %@, got: %@)", [expected, values])); }); test("concatenates subclasses when the values are functions", function() { var subKlass = klass.extend({ functions: Ember.K }); var obj = subKlass.create(); var values = get(obj, 'functions'), expected = [Ember.K, Ember.K]; deepEqual(values, expected, EmberStringUtils.fmt("should concatenate functions property (expected: %@, got: %@)", [expected, values])); }); }); define("ember-runtime/tests/legacy_1x/system/object/concatenated_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/legacy_1x/system/object'); test('ember-runtime/tests/legacy_1x/system/object/concatenated_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/legacy_1x/system/object/concatenated_test.js should pass jshint.'); }); }); define("ember-runtime/tests/legacy_1x/system/run_loop_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-metal/run_loop","ember-metal/binding","ember-runtime/mixins/observable","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var emberObserver = __dependency4__.observer; var run = __dependency5__["default"]; var Binding = __dependency6__.Binding; var Observable = __dependency7__["default"]; var EmberObject = __dependency8__["default"]; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests are the same except for places where we intend to break the API we instead validate that we warn the developer appropriately. CHANGES FROM 1.6: * Updated the API usage for setting up and syncing Binding since these are not the APIs this file is testing. * Disabled a call to invokeOnce() around line 127 because it appeared to be broken anyway. I don't think it ever even worked. */ var MyApp, binding1, binding2, previousPreventRunloop; QUnit.module("System:run_loop() - chained binding", { setup: function() { MyApp = {}; MyApp.first = EmberObject.createWithMixins(Observable, { output: 'MyApp.first' }) ; MyApp.second = EmberObject.createWithMixins(Observable, { input: 'MyApp.second', output: 'MyApp.second', inputDidChange: emberObserver("input", function() { this.set("output", this.get("input")) ; }) }) ; MyApp.third = EmberObject.createWithMixins(Observable, { input: "MyApp.third" }) ; } }); test("Should propagate bindings after the RunLoop completes (using Ember.RunLoop)", function() { run(function () { //Binding of output of MyApp.first object to input of MyApp.second object binding1 = Binding.from("first.output") .to("second.input").connect(MyApp) ; //Binding of output of MyApp.second object to input of MyApp.third object binding2 = Binding.from("second.output") .to("third.input").connect(MyApp) ; }); run(function () { // Based on the above binding if you change the output of MyApp.first // object it should change the all the variable of // MyApp.first,MyApp.second and MyApp.third object MyApp.first.set("output", "change") ; //Changes the output of the MyApp.first object equal(MyApp.first.get("output"), "change") ; //since binding has not taken into effect the value still remains as change. equal(MyApp.second.get("output"), "MyApp.first") ; }); // allows bindings to trigger... //Value of the output variable changed to 'change' equal(MyApp.first.get("output"), "change") ; //Since binding triggered after the end loop the value changed to 'change'. equal(MyApp.second.get("output"), "change") ; }); test("Should propagate bindings after the RunLoop completes", function() { run(function () { //Binding of output of MyApp.first object to input of MyApp.second object binding1 = Binding.from("first.output") .to("second.input").connect(MyApp) ; //Binding of output of MyApp.second object to input of MyApp.third object binding2 = Binding.from("second.output") .to("third.input").connect(MyApp) ; }); run(function () { //Based on the above binding if you change the output of MyApp.first object it should //change the all the variable of MyApp.first,MyApp.second and MyApp.third object MyApp.first.set("output", "change") ; //Changes the output of the MyApp.first object equal(MyApp.first.get("output"), "change") ; //since binding has not taken into effect the value still remains as change. equal(MyApp.second.get("output"), "MyApp.first") ; }); //Value of the output variable changed to 'change' equal(MyApp.first.get("output"), "change") ; //Since binding triggered after the end loop the value changed to 'change'. equal(MyApp.second.get("output"), "change") ; }); }); define("ember-runtime/tests/legacy_1x/system/run_loop_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/legacy_1x/system'); test('ember-runtime/tests/legacy_1x/system/run_loop_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/legacy_1x/system/run_loop_test.js should pass jshint.'); }); }); define("ember-runtime/tests/legacy_1x/system/set_test", ["ember-metal/core","ember-metal/is_none","ember-runtime/system/set","ember-runtime/system/object","ember-runtime/mixins/array"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var isNone = __dependency2__.isNone; var none = __dependency2__.none; var Set = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var EmberArray = __dependency5__["default"]; // NOTE: This test is adapted from the 1.x series of unit tests. The tests // are the same except for places where we intend to break the API we instead // validate that we warn the developer appropriately. // // * Changed Set.clone() call to Set.copy() // ======================================================================== // Set Tests // ======================================================================== var a, b, c ; // global variables QUnit.module("creating Set instances", { setup: function() { // create objects... a = { name: "a" } ; b = { name: "b" } ; c = { name: "c" } ; }, teardown: function() { a = undefined ; b = undefined ; c = undefined ; } }); test("new Set() should create empty set", function() { var set = new Set() ; equal(set.length, 0) ; }); test("new Set([1,2,3]) should create set with three items in them", function() { var set = new Set(Ember.A([a,b,c])) ; equal(set.length, 3) ; equal(set.contains(a), true) ; equal(set.contains(b), true) ; equal(set.contains(c), true) ; }); test("new Set() should accept anything that implements EmberArray", function() { var arrayLikeObject = EmberObject.createWithMixins(EmberArray, { _content: [a,b,c], length: 3, objectAt: function(idx) { return this._content[idx]; } }) ; var set = new Set(arrayLikeObject) ; equal(set.length, 3) ; equal(set.contains(a), true) ; equal(set.contains(b), true) ; equal(set.contains(c), true) ; }); var set ; // global variables // The tests below also end up testing the contains() method pretty // exhaustively. QUnit.module("Set.add + Set.contains", { setup: function() { set = new Set() ; }, teardown: function() { set = undefined ; } }); test("should add an EmberObject", function() { var obj = EmberObject.create() ; var oldLength = set.length ; set.add(obj) ; equal(set.contains(obj), true, "contains()") ; equal(set.length, oldLength+1, "new set length") ; }); test("should add a regular hash", function() { var obj = {} ; var oldLength = set.length ; set.add(obj) ; equal(set.contains(obj), true, "contains()") ; equal(set.length, oldLength+1, "new set length") ; }); test("should add a string", function() { var obj = "String!" ; var oldLength = set.length ; set.add(obj) ; equal(set.contains(obj), true, "contains()") ; equal(set.length, oldLength+1, "new set length") ; }); test("should add a number", function() { var obj = 23 ; var oldLength = set.length ; set.add(obj) ; equal(set.contains(obj), true, "contains()") ; equal(set.length, oldLength+1, "new set length") ; }); test("should add bools", function() { var oldLength = set.length ; set.add(true) ; equal(set.contains(true), true, "contains(true)"); equal(set.length, oldLength+1, "new set length"); set.add(false); equal(set.contains(false), true, "contains(false)"); equal(set.length, oldLength+2, "new set length"); }); test("should add 0", function() { var oldLength = set.length ; set.add(0) ; equal(set.contains(0), true, "contains(0)"); equal(set.length, oldLength+1, "new set length"); }); test("should add a function", function() { var obj = function() { return "Test function"; } ; var oldLength = set.length ; set.add(obj) ; equal(set.contains(obj), true, "contains()") ; equal(set.length, oldLength+1, "new set length") ; }); test("should NOT add a null", function() { set.add(null) ; equal(set.length, 0) ; equal(set.contains(null), false) ; }); test("should NOT add an undefined", function() { set.add(undefined) ; equal(set.length, 0) ; equal(set.contains(undefined), false) ; }); test("adding an item, removing it, adding another item", function() { var item1 = "item1" ; var item2 = "item2" ; set.add(item1) ; // add to set set.remove(item1) ; //remove from set set.add(item2) ; equal(set.contains(item1), false, "set.contains(item1)") ; set.add(item1) ; // re-add to set equal(set.length, 2, "set.length") ; }); QUnit.module("Set.remove + Set.contains", { // generate a set with every type of object, but none of the specific // ones we add in the tests below... setup: function() { set = new Set(Ember.A([ EmberObject.create({ dummy: true }), { isHash: true }, "Not the String", 16, true, false, 0])) ; }, teardown: function() { set = undefined ; } }); test("should remove an EmberObject and reduce length", function() { var obj = EmberObject.create() ; set.add(obj) ; equal(set.contains(obj), true) ; var oldLength = set.length ; set.remove(obj) ; equal(set.contains(obj), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; }); test("should remove a regular hash and reduce length", function() { var obj = {} ; set.add(obj) ; equal(set.contains(obj), true) ; var oldLength = set.length ; set.remove(obj) ; equal(set.contains(obj), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; }); test("should remove a string and reduce length", function() { var obj = "String!" ; set.add(obj) ; equal(set.contains(obj), true) ; var oldLength = set.length ; set.remove(obj) ; equal(set.contains(obj), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; }); test("should remove a number and reduce length", function() { var obj = 23 ; set.add(obj) ; equal(set.contains(obj), true) ; var oldLength = set.length ; set.remove(obj) ; equal(set.contains(obj), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; }); test("should remove a bools and reduce length", function() { var oldLength = set.length ; set.remove(true) ; equal(set.contains(true), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; set.remove(false); equal(set.contains(false), false, "should be removed") ; equal(set.length, oldLength-2, "should be 2 shorter") ; }); test("should remove 0 and reduce length", function() { var oldLength = set.length; set.remove(0) ; equal(set.contains(0), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; }); test("should remove a function and reduce length", function() { var obj = function() { return "Test function"; } ; set.add(obj) ; equal(set.contains(obj), true) ; var oldLength = set.length ; set.remove(obj) ; equal(set.contains(obj), false, "should be removed") ; equal(set.length, oldLength-1, "should be 1 shorter") ; }); test("should NOT remove a null", function() { var oldLength = set.length ; set.remove(null) ; equal(set.length, oldLength) ; }); test("should NOT remove an undefined", function() { var oldLength = set.length ; set.remove(undefined) ; equal(set.length, oldLength) ; }); test("should ignore removing an object not in the set", function() { var obj = EmberObject.create() ; var oldLength = set.length ; set.remove(obj) ; equal(set.length, oldLength) ; }); QUnit.module("Set.pop + Set.copy", { // generate a set with every type of object, but none of the specific // ones we add in the tests below... setup: function() { set = new Set(Ember.A([ EmberObject.create({ dummy: true }), { isHash: true }, "Not the String", 16, false])) ; }, teardown: function() { set = undefined ; } }); test("the pop() should remove an arbitrary object from the set", function() { var oldLength = set.length ; var obj = set.pop(); ok(!isNone(obj), 'pops up an item'); equal(set.length, oldLength-1, 'length shorter by 1'); }); test("should pop false and 0", function() { set = new Set(Ember.A([false])); ok(set.pop() === false, "should pop false"); set = new Set(Ember.A([0])); ok(set.pop() === 0, "should pop 0"); }); test("the copy() should return an indentical set", function() { var oldLength = set.length ; var obj = set.copy(); equal(oldLength,obj.length,'length of the clone should be same'); equal(obj.contains(set[0]), true); equal(obj.contains(set[1]), true); equal(obj.contains(set[2]), true); equal(obj.contains(set[3]), true); equal(obj.contains(set[4]), true); }); }); define("ember-runtime/tests/legacy_1x/system/set_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/legacy_1x/system'); test('ember-runtime/tests/legacy_1x/system/set_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/legacy_1x/system/set_test.js should pass jshint.'); }); }); define("ember-runtime/tests/mixins/action_handler_test", ["ember-metal/run_loop","ember-runtime/controllers/controller"], function(__dependency1__, __dependency2__) { "use strict"; var run = __dependency1__["default"]; var Controller = __dependency2__["default"]; test("passing a function for the actions hash triggers an assertion", function() { expect(1); var controller = Controller.extend({ actions: function(){} }); expectAssertion(function(){ run(function(){ controller.create(); }); }); }); }); define("ember-runtime/tests/mixins/action_handler_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/action_handler_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/action_handler_test.js should pass jshint.'); }); }); define("ember-runtime/tests/mixins/array_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/observer","ember-metal/mixin","ember-metal/computed","ember-runtime/tests/props_helper","ember-runtime/tests/suites/array","ember-runtime/system/object","ember-runtime/mixins/array"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.A var get = __dependency2__.get; var set = __dependency3__.set; var addObserver = __dependency4__.addObserver; var emberObserver = __dependency5__.observer; var computed = __dependency6__.computed; var testWithDefault = __dependency7__.testWithDefault; var testBoth = __dependency7__.testBoth; var ArrayTests = __dependency8__.ArrayTests; var EmberObject = __dependency9__["default"]; var EmberArray = __dependency10__["default"]; /* Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ var TestArray = EmberObject.extend(EmberArray, { _content: null, init: function(ary) { this._content = ary || []; }, // some methods to modify the array so we can test changes. Note that // arrays can be modified even if they don't implement MutableArray. The // MutableArray is just a standard API for mutation but not required. addObject: function(obj) { var idx = this._content.length; this.arrayContentWillChange(idx, 0, 1); this._content.push(obj); this.arrayContentDidChange(idx, 0, 1); }, removeFirst: function(idx) { this.arrayContentWillChange(0, 1, 0); this._content.shift(); this.arrayContentDidChange(0, 1, 0); }, objectAt: function(idx) { return this._content[idx]; }, length: computed(function() { return this._content.length; }) }); ArrayTests.extend({ name: 'Basic Mutable Array', newObject: function(ary) { ary = ary ? ary.slice() : this.newFixture(3); return new TestArray(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(); test("the return value of slice has Ember.Array applied", function() { var x = EmberObject.createWithMixins(EmberArray, { length: 0 }); var y = x.slice(1); equal(EmberArray.detect(y), true, "mixin should be applied"); }); test("slice supports negative index arguments", function() { var testArray = new TestArray([1,2,3,4]); deepEqual(testArray.slice(-2), [3, 4], 'slice(-2)'); deepEqual(testArray.slice(-2, -1), [3], 'slice(-2, -1'); deepEqual(testArray.slice(-2, -2), [], 'slice(-2, -2)'); deepEqual(testArray.slice(-1, -2), [], 'slice(-1, -2)'); deepEqual(testArray.slice(-4, 1), [1], 'slice(-4, 1)'); deepEqual(testArray.slice(-4, 5), [1,2,3,4], 'slice(-4, 5)'); deepEqual(testArray.slice(-4), [1,2,3,4], 'slice(-4)'); deepEqual(testArray.slice(0, -1), [1,2,3], 'slice(0, -1)'); deepEqual(testArray.slice(0, -4), [], 'slice(0, -4)'); deepEqual(testArray.slice(0, -3), [1], 'slice(0, -3)'); }); // .......................................................... // CONTENT DID CHANGE // var DummyArray = EmberObject.extend(EmberArray, { nextObject: function() {}, length: 0, objectAt: function(idx) { return 'ITEM-'+idx; } }); var obj, observer; // .......................................................... // NOTIFY ARRAY OBSERVERS // QUnit.module('mixins/array/arrayContent[Will|Did]Change'); test('should notify observers of []', function() { obj = DummyArray.createWithMixins({ _count: 0, enumerablePropertyDidChange: emberObserver('[]', function() { this._count++; }) }); equal(obj._count, 0, 'should not have invoked yet'); obj.arrayContentWillChange(0, 1, 1); obj.arrayContentDidChange(0, 1, 1); equal(obj._count, 1, 'should have invoked'); }); // .......................................................... // NOTIFY CHANGES TO LENGTH // QUnit.module('notify observers of length', { setup: function() { obj = DummyArray.createWithMixins({ _after: 0, lengthDidChange: emberObserver('length', function() { this._after++; }) }); equal(obj._after, 0, 'should not have fired yet'); }, teardown: function() { obj = null; } }); test('should notify observers when call with no params', function() { obj.arrayContentWillChange(); equal(obj._after, 0); obj.arrayContentDidChange(); equal(obj._after, 1); }); // API variation that included items only test('should not notify when passed lengths are same', function() { obj.arrayContentWillChange(0, 1, 1); equal(obj._after, 0); obj.arrayContentDidChange(0, 1, 1); equal(obj._after, 0); }); test('should notify when passed lengths are different', function() { obj.arrayContentWillChange(0, 1, 2); equal(obj._after, 0); obj.arrayContentDidChange(0, 1, 2); equal(obj._after, 1); }); // .......................................................... // NOTIFY ARRAY OBSERVER // QUnit.module('notify array observers', { setup: function() { obj = DummyArray.create(); observer = EmberObject.createWithMixins({ _before: null, _after: null, 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); } }); obj.addArrayObserver(observer); }, teardown: function() { obj = observer = null; } }); test('should notify enumerable observers when called with no params', function() { obj.arrayContentWillChange(); deepEqual(observer._before, [obj, 0, -1, -1]); obj.arrayContentDidChange(); deepEqual(observer._after, [obj, 0, -1, -1]); }); // API variation that included items only test('should notify when called with same length items', function() { obj.arrayContentWillChange(0, 1, 1); deepEqual(observer._before, [obj, 0, 1, 1]); obj.arrayContentDidChange(0, 1, 1); deepEqual(observer._after, [obj, 0, 1, 1]); }); test('should notify when called with diff length items', function() { obj.arrayContentWillChange(0, 2, 1); deepEqual(observer._before, [obj, 0, 2, 1]); obj.arrayContentDidChange(0, 2, 1); deepEqual(observer._after, [obj, 0, 2, 1]); }); test('removing enumerable observer should disable', function() { obj.removeArrayObserver(observer); obj.arrayContentWillChange(); deepEqual(observer._before, null); obj.arrayContentDidChange(); deepEqual(observer._after, null); }); // .......................................................... // NOTIFY ENUMERABLE OBSERVER // QUnit.module('notify enumerable observers as well', { setup: function() { obj = DummyArray.create(); observer = EmberObject.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; } }); test('should notify enumerable observers when called with no params', function() { obj.arrayContentWillChange(); deepEqual(observer._before, [obj, null, null], 'before'); obj.arrayContentDidChange(); deepEqual(observer._after, [obj, null, null], 'after'); }); // API variation that included items only test('should notify when called with same length items', function() { obj.arrayContentWillChange(0, 1, 1); deepEqual(observer._before, [obj, ['ITEM-0'], 1], 'before'); obj.arrayContentDidChange(0, 1, 1); deepEqual(observer._after, [obj, 1, ['ITEM-0']], 'after'); }); test('should notify when called with diff length items', function() { obj.arrayContentWillChange(0, 2, 1); deepEqual(observer._before, [obj, ['ITEM-0', 'ITEM-1'], 1], 'before'); obj.arrayContentDidChange(0, 2, 1); deepEqual(observer._after, [obj, 2, ['ITEM-0']], 'after'); }); test('removing enumerable observer should disable', function() { obj.removeEnumerableObserver(observer); obj.arrayContentWillChange(); deepEqual(observer._before, null, 'before'); obj.arrayContentDidChange(); deepEqual(observer._after, null, 'after'); }); // .......................................................... // @each // var ary; QUnit.module('EmberArray.@each support', { setup: function() { ary = new TestArray([ { isDone: true, desc: 'Todo 1' }, { isDone: false, desc: 'Todo 2' }, { isDone: true, desc: 'Todo 3' }, { isDone: false, desc: 'Todo 4' } ]); }, teardown: function() { ary = null; } }); test('adding an object should notify (@each)', function() { var called = 0; var observerObject = EmberObject.create({ wasCalled: function() { called++; } }); // get(ary, '@each'); addObserver(ary, '@each', observerObject, 'wasCalled'); ary.addObject(EmberObject.create({ desc: "foo", isDone: false })); equal(called, 1, "calls observer when object is pushed"); }); test('adding an object should notify (@each.isDone)', function() { var called = 0; var observerObject = EmberObject.create({ wasCalled: function() { called++; } }); addObserver(ary, '@each.isDone', observerObject, 'wasCalled'); ary.addObject(EmberObject.create({ desc: "foo", isDone: false })); equal(called, 1, "calls observer when object is pushed"); }); test('using @each to observe arrays that does not return objects raise error', function() { var called = 0; var observerObject = EmberObject.create({ wasCalled: function() { called++; } }); ary = TestArray.create({ objectAt: function(idx) { return get(this._content[idx], 'desc'); } }); addObserver(ary, '@each.isDone', observerObject, 'wasCalled'); expectAssertion(function() { ary.addObject(EmberObject.create({ desc: "foo", isDone: false })); }, /When using @each to observe the array/); equal(called, 0, 'not calls observer when object is pushed'); }); test('modifying the array should also indicate the isDone prop itself has changed', function() { // NOTE: we never actually get the '@each.isDone' property here. This is // important because it tests the case where we don't have an isDone // EachArray materialized but just want to know when the property has // changed. var each = get(ary, '@each'); var count = 0; addObserver(each, 'isDone', function() { count++; }); count = 0; var item = ary.objectAt(2); set(item, 'isDone', !get(item, 'isDone')); equal(count, 1, '@each.isDone should have notified'); }); testBoth("should be clear caches for computed properties that have dependent keys on arrays that are changed after object initialization", function(get, set) { var obj = EmberObject.createWithMixins({ init: function() { set(this, 'resources', Ember.A()); }, common: computed(function() { return get(get(this, 'resources').objectAt(0), 'common'); }).property('resources.@each.common') }); get(obj, 'resources').pushObject(EmberObject.create({ common: "HI!" })); equal("HI!", get(obj, 'common')); set(get(obj, 'resources').objectAt(0), 'common', "BYE!"); equal("BYE!", get(obj, 'common')); }); testBoth("observers that contain @each in the path should fire only once the first time they are accessed", function(get, set) { var count = 0; var obj = EmberObject.createWithMixins({ init: function() { // Observer does not fire on init set(this, 'resources', Ember.A()); }, commonDidChange: emberObserver('resources.@each.common', function() { count++; }) }); // Observer fires second time when new object is added get(obj, 'resources').pushObject(EmberObject.create({ common: "HI!" })); // Observer fires third time when property on an object is changed set(get(obj, 'resources').objectAt(0), 'common', "BYE!"); equal(count, 2, "observers should only be called once"); }); }); define("ember-runtime/tests/mixins/array_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/array_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/array_test.js should pass jshint.'); }); }); define("ember-runtime/tests/mixins/comparable_test", ["ember-metal/property_get","ember-runtime/system/object","ember-runtime/compare","ember-runtime/mixins/comparable"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var get = __dependency1__.get; var EmberObject = __dependency2__["default"]; var compare = __dependency3__["default"]; var Comparable = __dependency4__["default"]; var Rectangle = EmberObject.extend(Comparable, { length: 0, width: 0, area: function() { return get(this,'length') * get(this, 'width'); }, compare: function(a, b) { return compare(a.area(), b.area()); } }); var r1, r2; QUnit.module("Comparable", { setup: function() { r1 = Rectangle.create({length: 6, width: 12}); r2 = Rectangle.create({length: 6, width: 13}); }, teardown: function() { } }); test("should be comparable and return the correct result", function() { equal(Comparable.detect(r1), true); equal(compare(r1, r1), 0); equal(compare(r1, r2), -1); equal(compare(r2, r1), 1); }); }); define("ember-runtime/tests/mixins/comparable_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/comparable_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/comparable_test.js should pass jshint.'); }); }); define("ember-runtime/tests/mixins/copyable_test", ["ember-runtime/tests/suites/copyable","ember-runtime/mixins/copyable","ember-runtime/system/object","ember-metal/utils","ember-metal/property_set","ember-metal/property_get"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var CopyableTests = __dependency1__["default"]; var Copyable = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var generateGuid = __dependency4__.generateGuid; var set = __dependency5__.set; var get = __dependency6__.get; var CopyableObject = EmberObject.extend(Copyable, { id: null, init: function() { this._super(); set(this, 'id', generateGuid()); }, copy: function() { var ret = new CopyableObject(); set(ret, 'id', get(this, 'id')); return ret; } }); CopyableTests.extend({ name: 'Copyable Basic Test', newObject: function() { return new CopyableObject(); }, isEqual: function(a, b) { if (!(a instanceof CopyableObject) || !(b instanceof CopyableObject)) return false; return get(a, 'id') === get(b,'id'); } }).run(); }); define("ember-runtime/tests/mixins/copyable_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/copyable_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/copyable_test.js should pass jshint.'); }); }); define("ember-runtime/tests/mixins/deferred_test", ["ember-metal/core","ember-metal/run_loop","ember-runtime/system/object","ember-runtime/mixins/deferred","ember-runtime/ext/rsvp"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; /* global Promise:true,EmberDev */ var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var Deferred = __dependency4__["default"]; var RSVP = __dependency5__["default"]; var originalDeprecate; QUnit.module("Deferred", { setup: function() { originalDeprecate = Ember.deprecate; Ember.deprecate = function() { }; }, teardown: function() { Ember.deprecate = originalDeprecate; } }); test("can resolve deferred", function() { var deferred, count = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function(a) { count++; }); run(deferred, 'resolve', deferred); equal(count, 1, "was fulfilled"); }); test("can reject deferred", function() { var deferred, count = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(null, function() { count++; }); run(deferred, 'reject'); equal(count, 1, "fail callback was called"); }); test("can resolve with then", function() { var deferred, count1 = 0 ,count2 = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { count1++; }, function() { count2++; }); run(deferred, 'resolve', deferred); equal(count1, 1, "then were resolved"); equal(count2, 0, "then was not rejected"); }); test("can reject with then", function() { var deferred, count1 = 0 ,count2 = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { count1++; }, function() { count2++; }); run(deferred, 'reject'); equal(count1, 0, "then was not resolved"); equal(count2, 1, "then were rejected"); }); test("can call resolve multiple times", function() { var deferred, count = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { count++; }); run(function() { deferred.resolve(deferred); deferred.resolve(deferred); deferred.resolve(deferred); }); equal(count, 1, "calling resolve multiple times has no effect"); }); test("resolve prevent reject", function() { var deferred, resolved = false, rejected = false, progress = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { resolved = true; }, function() { rejected = true; }); run(deferred, 'resolve', deferred); run(deferred, 'reject'); equal(resolved, true, "is resolved"); equal(rejected, false, "is not rejected"); }); test("reject prevent resolve", function() { var deferred, resolved = false, rejected = false, progress = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { resolved = true; }, function() { rejected = true; }); run(deferred, 'reject'); run(deferred, 'reject', deferred); equal(resolved, false, "is not resolved"); equal(rejected, true, "is rejected"); }); test("will call callbacks if they are added after resolution", function() { var deferred, count1 = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); run(deferred, 'resolve', 'toto'); run(function() { deferred.then(function(context) { if (context === 'toto') { count1++; } }); deferred.then(function(context) { if (context === 'toto') { count1++; } }); }); equal(count1, 2, "callbacks called after resolution"); }); test("then is chainable", function() { var deferred, count = 0; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { eval('error'); // Use eval to pass JSHint }).then(null, function() { count++; }); run(deferred, 'resolve', deferred); equal(count, 1, "chained callback was called"); }); test("can self fulfill", function() { expect(1); var deferred; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function(value) { equal(value, deferred, "successfully resolved to itself"); }); run(deferred, 'resolve', deferred); }); test("can self reject", function() { expect(1); var deferred; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function() { ok(false, 'should not fulfill'); },function(value) { equal(value, deferred, "successfully rejected to itself"); }); run(deferred, 'reject', deferred); }); test("can fulfill to a custom value", function() { expect(1); var deferred, obj = {}; run(function() { deferred = EmberObject.createWithMixins(Deferred); }); deferred.then(function(value) { equal(value, obj, "successfully resolved to given value"); }); run(deferred, 'resolve', obj); }); test("can chain self fulfilling objects", function() { expect(2); var firstDeferred, secondDeferred; run(function() { firstDeferred = EmberObject.createWithMixins(Deferred); secondDeferred = EmberObject.createWithMixins(Deferred); }); firstDeferred.then(function(value) { equal(value, firstDeferred, "successfully resolved to the first deferred"); return secondDeferred; }).then(function(value) { equal(value, secondDeferred, "successfully resolved to the second deferred"); }); run(function() { firstDeferred.resolve(firstDeferred); secondDeferred.resolve(secondDeferred); }); }); test("can do multi level assimilation", function() { expect(1); var firstDeferred, secondDeferred, firstDeferredResolved = false; run(function() { firstDeferred = EmberObject.createWithMixins(Deferred); secondDeferred = EmberObject.createWithMixins(Deferred); }); firstDeferred.then(function() { firstDeferredResolved = true; }); secondDeferred.then(function() { ok(firstDeferredResolved, "first deferred already resolved"); }); run(secondDeferred, 'resolve', firstDeferred); run(firstDeferred, 'resolve', firstDeferred); }); test("can handle rejection without rejection handler", function() { expect(2); var reason = 'some reason'; var deferred = run(function() { return EmberObject.createWithMixins(Deferred); }); deferred.then().then(function() { ok(false, 'expected rejection, got fulfillment'); }, function(actualReason) { ok(true, 'expected fulfillment'); equal(actualReason, reason); }); run(deferred, 'reject', reason); }); test("can handle fulfillment without fulfillment handler", function() { expect(2); var fulfillment = 'some fulfillment'; var deferred = run(function() { return EmberObject.createWithMixins(Deferred); }); deferred.then().then(function(actualFulfillment) { ok(true, 'expected fulfillment'); equal(fulfillment, actualFulfillment); }, function(reason) { ok(false, 'expected fulfillment, got reason' + reason); }); run(deferred, 'resolve', fulfillment); }); if (!EmberDev.runningProdBuild){ test("causes a deprecation warning when used", function() { var deferred, deprecationMade, obj = {}; Ember.deprecate = function(message) { deprecationMade = message; }; deferred = EmberObject.createWithMixins(Deferred); equal(deprecationMade, undefined, 'no deprecation was made on init'); deferred.then(function(value) { equal(value, obj, "successfully resolved to given value"); }); equal(deprecationMade, 'Usage of Ember.DeferredMixin or Ember.Deferred is deprecated.'); run(deferred, 'resolve', obj); }); } }); define("ember-runtime/tests/mixins/deferred_test.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests/mixins'); test('ember-runtime/tests/mixins/deferred_test.js should pass jshint', function() { ok(true, 'ember-runtime/tests/mixins/deferred_test.js should pass jshint.'); }); }); define("ember-runtime/tests/mixins/enumerable_test", ["ember-metal/core","ember-runtime/tests/suites/enumerable","ember-metal/enumerable_utils","ember-runtime/system/object","ember-runtime/mixins/enumerable","ember-runtime/mixins/array","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/mixin"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; var Ember = __dependency1__["default"]; // for Ember.K and Ember.A var EnumerableTests = __dependency2__["default"]; var indexOf = __dependency3__.indexOf; var EmberObject = __dependency4__["default"]; var Enumerable = __dependency5__["default"]; var EmberArray = __dependency6__["default"]; var get = __dependency7__.get; var set = __dependency8__.set; var computed = __dependency9__.computed; var emberObserver = __dependency10__.observer; /* Implement a basic fake enumerable. This validates that any non-native enumerable can impl this API. */ var TestEnumerable = EmberObject.extend(Enumerable, { _content: null, init: function(ary) { this._content = ary || []; }, addObject: function(obj) { if (indexOf(this._content, obj)>=0) return this; this._content.push(obj); this.enumerableContentDidChange(); }, nextObject: function(idx) { return idx >= get(this, 'length') ? undefined : this._content[idx]; }, length: computed(function() { return this._content.length; }), slice: function() { return this._content.slice(); } }); EnumerableTests.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'); test("should apply Ember.Array to return value of map", function() { var x = EmberObject.createWithMixins(Enumerable); var y = x.map(Ember.K); equal(EmberArray.detect(y), true, "should have mixin applied"); }); test("should apply Ember.Array to return value of filter", function() { var x = EmberObject.createWithMixins(Enumerable); var y = x.filter(Ember.K); equal(EmberArray.detect(y), true, "should have mixin applied"); }); test("should apply Ember.Array to return value of invoke", function() { var x = EmberObject.createWithMixins(Enumerable); var y = x.invoke(Ember.K); equal(EmberArray.detect(y), true, "should have mixin applied"); }); test("should apply Ember.Array to return value of toArray", function() { var x = EmberObject.createWithMixins(Enumerable); var y = x.toArray(Ember.K); equal(EmberArray.detect(y), true, "should have mixin applied"); }); test("should apply Ember.Array to return value of without", function() { var x = EmberObject.createWithMixins(Enumerable, { contains: function() { return true; } }); var y = x.without(Ember.K); equal(EmberArray.detect(y), true, "should have mixin applied"); }); test("should apply Ember.Array to return value of uniq", function() { var x = EmberObject.createWithMixins(Enumerable); var y = x.uniq(Ember.K); equal(EmberArray.detect(y), true, "should have mixin applied"); }); test('any', function() { var kittens = Ember.A([{ color: 'white' }, { color: 'black' }, { color: 'white' }]), foundWhite = kittens.any(function(kitten) { return kitten.color === 'white'; }), foundWhite2 = kittens.isAny('color', 'white'); equal(foundWhite, true); equal(foundWhite2, true); }); test('any with NaN', function() { var numbers = Ember.A([1,2,NaN,4]); var hasNaN = numbers.any(function(n){ return isNaN(n); }); equal(hasNaN, true, "works when matching NaN"); }); test('every', function() { var allColorsKittens = Ember.A([{ color: 'white' }, { color: 'black' }, { color: 'white' }]), allWhiteKittens = Ember.A([{ color: 'white' }, { color: 'white' }, { color: 'white' }]), allWhite = false, 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.extend(Enumerable, { nextObject: function() {}, length: 0 }); var obj, observer; // .......................................................... // NOTIFY ENUMERABLE PROPERTY // QUnit.module('mixins/enumerable/enumerableContentDidChange'); test('should notify observers of []', function() { var obj = EmberObject.createWithMixins(Enumerable, { nextObject: function() {}, // avoid exceptions _count: 0, enumerablePropertyDidChange: emberObserver('[]', 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: emberObserver('length', function() { this._after++; }) }); equal(obj._after, 0, 'should not have fired yet'); }, teardown: function() { obj = null; } }); 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 test('should not notify when passed arrays of same length', function() { var added = ['foo'], removed = ['bar']; obj.enumerableContentWillChange(removed, added); equal(obj._after, 0); obj.enumerableContentDidChange(removed, added); equal(obj._after, 0); }); test('should notify when passed arrays of different length', function() { var added = ['foo'], removed = ['bar', 'baz']; obj.enumerableContentWillChange(removed, added); equal(obj._after, 0); obj.enumerableContentDidChange(removed, added); equal(obj._after, 1); }); // API variation passes indexes only 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); }); 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.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; } }); 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 test('should notify when called with same length items', function() { var added = ['foo'], removed = ['bar']; obj.enumerableContentWillChange(removed, added); deepEqual(observer._before, [obj, removed, added]); obj.enumerableContentDidChange(removed, added); deepEqual(observer._after, [obj, removed, added]); }); test('should notify when called with diff length items', function() { var added = ['foo', 'baz'], removed = ['bar']; obj.enumerableContentWillChange(removed, added); deepEqual(observer._before, [obj, removed, added]); obj.enumerableContentDidChange(removed, added); deepEqual(observer._after, [obj, removed, added]); }); 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]); }); test('removing enumerable observer should disable', function() { obj.removeEnumerableObserver(observer); obj.enumerableContentWillChange(); deepEqual(observer._before, null); obj.enumerableContentDidChange(); deepEqual(observer._after, null); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var MutableArrayTests = __dependency1__["default"]; var MutableArray = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var computed = __dependency4__.computed; /* Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ var TestMutableArray = EmberObject.extend(MutableArray, { _content: null, init: function(ary) { this._content = Ember.A(ary || []); }, replace: function(idx, amt, objects) { var args = objects ? objects.slice() : [], removeAmt = amt, 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(function() { return this._content.length; }), slice: function() { return this._content.slice(); } }); MutableArrayTests.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(); }); define("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.'); }); }); define("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","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var MutableEnumerableTests = __dependency1__["default"]; var MutableEnumerable = __dependency2__["default"]; var indexOf = __dependency3__.indexOf; var EmberObject = __dependency4__["default"]; var computed = __dependency5__.computed; var get = __dependency6__.get; var set = __dependency7__.set; /* Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ var TestMutableEnumerable = EmberObject.extend(MutableEnumerable, { _content: null, addObject: function(obj) { if (indexOf(this._content, obj)>=0) return this; this.enumerableContentWillChange(null, [obj]); this._content.push(obj); this.enumerableContentDidChange(null, [obj]); }, removeObject: function(obj) { var idx = 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>=get(this, 'length') ? undefined : this._content[idx]; }, length: computed(function() { return this._content.length; }), slice: function() { return this._content.slice(); } }); MutableEnumerableTests.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(); }); define("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.'); }); }); define("ember-runtime/tests/mixins/observable_test", ["ember-metal/computed","ember-metal/observer","ember-runtime/system/object","ember-runtime/tests/props_helper"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var computed = __dependency1__.computed; var addObserver = __dependency2__.addObserver; var EmberObject = __dependency3__["default"]; var testBoth = __dependency4__.testBoth; QUnit.module('mixins/observable'); test('should be able to use getProperties to get a POJO of provided keys', function() { var obj = EmberObject.create({ firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }); var pojo = obj.getProperties("firstName", "lastName"); equal("Steve", pojo.firstName); equal("Jobs", pojo.lastName); }); test('should be able to use getProperties with array parameter to get a POJO of provided keys', function() { var obj = EmberObject.create({ firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }); var pojo = obj.getProperties(["firstName", "lastName"]); equal("Steve", pojo.firstName); equal("Jobs", pojo.lastName); }); test('should be able to use setProperties to set multiple properties at once', function() { var obj = EmberObject.create({ firstName: "Steve", lastName: "Jobs", companyName: "Apple, Inc." }); obj.setProperties({firstName: "Tim", lastName: "Cook"}); equal("Tim", obj.get("firstName")); equal("Cook", obj.get("lastName")); }); testBoth('calling setProperties completes safely despite exceptions', function(get,set) { var exc = new Error("Something unexpected happened!"); var obj = EmberObject.createWithMixins({ firstName: "Steve", lastName: "Jobs", companyName: computed(function(key, value) { if (value !== undefined) { throw exc; } return "Apple, Inc."; }) }); var firstNameChangedCount = 0; 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'); }); testBoth("should be able to retrieve cached values of computed properties without invoking the computed property", function(get) { var obj = EmberObject.createWithMixins({ foo: 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"); }); test('incrementProperty should work even if value is number in string', function() { var obj = EmberObject.create({ age: "24" }); obj.incrementProperty('age'); equal(25, obj.get('age')); }); }); define("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.'); }); }); define("ember-runtime/tests/mixins/promise_proxy_test", ["ember-metal/core","ember-metal/platform","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/object_proxy","ember-runtime/mixins/promise_proxy","ember-runtime/ext/rsvp"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var Ember = __dependency1__["default"]; var create = __dependency2__.create; var get = __dependency3__.get; var run = __dependency4__["default"]; var ObjectProxy = __dependency5__["default"]; var PromiseProxyMixin = __dependency6__["default"]; var EmberRSVP = __dependency7__["default"]; var RSVP = requireModule("rsvp"); // jshint ignore:line var ObjectPromiseProxy; test("present on ember namespace", function(){ ok(PromiseProxyMixin, "expected PromiseProxyMixin to exist"); }); QUnit.module("Ember.PromiseProxy - ObjectProxy", { setup: function() { ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin); } }); test("no promise, invoking then should raise", function(){ var value = { firstName: 'stef', lastName: 'penner' }; var proxy = ObjectPromiseProxy.create(); raises(function(){ proxy.then(Ember.K, Ember.K); }, new RegExp("PromiseProxy's promise must be set")); }); 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(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); equal(get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(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(deferred, 'resolve', value); equal(didFulfillCount, 1, 'should have been fulfilled'); equal(didRejectCount, 0, 'should not have been rejected'); equal(get(proxy, 'content'), value, 'expects the proxy to have content'); equal(get(proxy, 'reason'), undefined, 'expects the proxy to still have no reason'); equal(get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading'); equal(get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(get(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); run(deferred, 'resolve', value); equal(didFulfillCount, 1, 'should still have been only fulfilled once'); equal(didRejectCount, 0, 'should still not have been rejected'); run(deferred, 'reject', value); equal(didFulfillCount, 1, 'should still have been only fulfilled once'); equal(didRejectCount, 0, 'should still not have been rejected'); equal(get(proxy, 'content'), value, 'expects the proxy to have still have same content'); equal(get(proxy, 'reason'), undefined, 'expects the proxy still to have no reason'); equal(get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading'); equal(get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(get(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); // rest of the promise semantics are tested in directly in RSVP }); 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(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); equal(get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(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(deferred, 'reject', reason); equal(didFulfillCount, 0, 'should not yet have been fulfilled'); equal(didRejectCount, 1, 'should have been rejected'); equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); equal(get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading'); equal(get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(get(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected'); equal(get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); run(deferred, 'reject', reason); equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); equal(didRejectCount, 1, 'should still remain rejected'); run(deferred, 'resolve', 1); equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); equal(didRejectCount, 1, 'should still remain rejected'); equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); equal(get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); equal(get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading'); equal(get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(get(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected'); equal(get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); }); test("unhandled rejects still propogate 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 }); var promise = proxy.get('promise'); function onerror(reason) { equal(reason, expectedReason, 'expected reason'); } RSVP.on('error', onerror); RSVP.off('error', RSVP.onerrorDefault); run(deferred, 'reject', expectedReason); RSVP.on('error', RSVP.onerrorDefault); RSVP.off('error', onerror); run(deferred, 'reject', expectedReason); RSVP.on('error', RSVP.onerrorDefault); RSVP.off('error', onerror); }); test("should work with promise inheritance", function(){ function PromiseSubclass() { RSVP.Promise.apply(this, arguments); } PromiseSubclass.prototype = create(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 inheritence'); }); test("should reset isFulfilled and isRejected when promise is reset", function() { var deferred = EmberRSVP.defer(); var proxy = ObjectPromiseProxy.create({ promise: deferred.promise }); equal(get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); run(deferred, 'resolve'); equal(get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading'); equal(get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(get(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); var anotherDeferred = EmberRSVP.defer(); proxy.set('promise', anotherDeferred.promise); equal(get(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading'); equal(get(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled'); equal(get(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected'); equal(get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); run(anotherDeferred, 'reject'); equal(get(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading'); equal(get(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled'); equal(get(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected'); equal(get(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled'); }); }); define("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.'); }); }); define("ember-runtime/tests/mixins/sortable_test", ["ember-metal/core","ember-metal/property_get","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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var emberObserver = __dependency5__.observer; var listenersFor = __dependency6__.listenersFor; var ArrayProxy = __dependency7__["default"]; var SortableMixin = __dependency8__["default"]; var EmberObject = __dependency9__["default"]; var ArrayController = __dependency10__["default"]; var unsortedArray, sortedArrayController; QUnit.module("Ember.Sortable"); QUnit.module("Ember.Sortable with content", { setup: function() { run(function() { var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember.A(Ember.A(array).copy()); sortedArrayController = ArrayProxy.createWithMixins(SortableMixin, { content: unsortedArray }); }); }, teardown: function() { run(function() { sortedArrayController.set('content', null); sortedArrayController.destroy(); }); } }); 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'); }); 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'); }); test("changing sort order triggers observers", function() { var observer, changeCount = 0; observer = EmberObject.createWithMixins({ array: sortedArrayController, arrangedDidChange: emberObserver('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(function() { observer.destroy(); }); }); 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'); }); 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(function() { var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember.A(Ember.A(array).copy()); sortedArrayController = ArrayController.create({ content: unsortedArray, sortProperties: ['name'] }); }); }, teardown: function() { run(function() { sortedArrayController.destroy(); }); } }); 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'); }); 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'); }); 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'); }); 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'); }); test("addObject does not insert duplicates", function() { var sortedArrayProxy, obj = {}; sortedArrayProxy = ArrayProxy.createWithMixins(SortableMixin, { content: Ember.A([obj]) }); equal(sortedArrayProxy.get('length'), 1, 'array has 1 item'); sortedArrayProxy.addObject(obj); equal(sortedArrayProxy.get('length'), 1, 'array still has 1 item'); }); 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'); 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'); }); 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'); set(sortedArrayController.objectAt(1), 'name', 'Alice'); // Change Dale to Alice equal(sortedArrayController.objectAt(0).name, 'Alice', 'Alice (previously Dale) is first now'); }); 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']); set(sortedArrayController.objectAt(0), 'name', 'Scumbag Brynjolfsson'); ok(!insertItemSortedCalled, "insertItemSorted should not have been called"); }); test("sortProperties observers removed on content removal", function() { var removedObject = unsortedArray.objectAt(2); equal(listenersFor(removedObject, 'name:change').length, 1, "Before removal, there should be one listener for sortProperty change."); unsortedArray.replace(2, 1, []); equal(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(function() { sortedArrayController = ArrayController.create({ sortProperties: ['name'] }); var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }]; unsortedArray = Ember.A(Ember.A(array).copy()); }); }, teardown: function() { run(function() { sortedArrayController.destroy(); }); } }); test("you can set content later and it will be sorted", function() { equal(sortedArrayController.get('length'), 0, 'array has 0 items'); run(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(function() { sortedArrayController = ArrayController.create({ sortProperties: ['name'], sortFunction: function(v, w) { var lowerV = v.toLowerCase(), 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.A(Ember.A(array).copy()); }); }, teardown: function() { run(function() { sortedArrayController.destroy(); }); } }); test("you can sort with custom sorting function", function() { equal(sortedArrayController.get('length'), 0, 'array has 0 items'); run(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'); }); test("Ember.Sortable with sortFunction on ArrayProxy should work like ArrayController", function() { run(function() { sortedArrayController = ArrayProxy.createWithMixins(SortableMixin, { sortProperties: ['name'], sortFunction: function(v, w) { var lowerV = v.toLowerCase(), 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.A(Ember.A(array).copy()); }); equal(sortedArrayController.get('length'), 0, 'array has 0 items'); run(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'); }); }); define("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.'); }); }); define("ember-runtime/tests/mixins/target_action_support_test", ["ember-metal/core","ember-runtime/system/object","ember-runtime/mixins/target_action_support"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var EmberObject = __dependency2__["default"]; var TargetActionSupport = __dependency3__["default"]; var originalLookup; QUnit.module("TargetActionSupport", { setup: function() { originalLookup = Ember.lookup; }, teardown: function() { Ember.lookup = originalLookup; } }); test("it should return false if no target or action are specified", function() { expect(1); var obj = EmberObject.createWithMixins(TargetActionSupport); ok(false === obj.triggerAction(), "no target or action was specified"); }); test("it should support actions specified as strings", function() { expect(2); var obj = EmberObject.createWithMixins(TargetActionSupport, { target: EmberObject.create({ anEvent: function() { ok(true, "anEvent method was called"); } }), action: 'anEvent' }); ok(true === obj.triggerAction(), "a valid target and action were specified"); }); test("it should invoke the send() method on objects that implement it", function() { expect(3); var obj = EmberObject.createWithMixins(TargetActionSupport, { target: EmberObject.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"); }); test("it should find targets specified using a property path", function() { expect(2); var Test = {}; Ember.lookup = { Test: Test }; Test.targetObj = EmberObject.create({ anEvent: function() { ok(true, "anEvent method was called on global object"); } }); var myObj = EmberObject.createWithMixins(TargetActionSupport, { target: 'Test.targetObj', action: 'anEvent' }); ok(true === myObj.triggerAction(), "a valid target and action were specified"); }); test("it should use an actionContext object specified as a property on the object", function() { expect(2); var obj = EmberObject.createWithMixins(TargetActionSupport,{ action: 'anEvent', actionContext: {}, target: EmberObject.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"); }); test("it should find an actionContext specified as a property path", function() { expect(2); var Test = {}; Ember.lookup = { Test: Test }; Test.aContext = {}; var obj = EmberObject.createWithMixins(TargetActionSupport,{ action: 'anEvent', actionContext: 'Test.aContext', target: EmberObject.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"); }); test("it should use the target specified in the argument", function() { expect(2); var targetObj = EmberObject.create({ anEvent: function() { ok(true, "anEvent method was called"); } }), obj = EmberObject.createWithMixins(TargetActionSupport,{ action: 'anEvent' }); ok(true === obj.triggerAction({target: targetObj}), "a valid target and action were specified"); }); test("it should use the action specified in the argument", function() { expect(2); var obj = EmberObject.createWithMixins(TargetActionSupport,{ target: EmberObject.create({ anEvent: function() { ok(true, "anEvent method was called"); } }) }); ok(true === obj.triggerAction({action: 'anEvent'}), "a valid target and action were specified"); }); test("it should use the actionContext specified in the argument", function() { expect(2); var context = {}, obj = EmberObject.createWithMixins(TargetActionSupport,{ target: EmberObject.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"); }); test("it should allow multiple arguments from actionContext", function() { expect(3); var param1 = 'someParam', param2 = 'someOtherParam', obj = EmberObject.createWithMixins(TargetActionSupport,{ target: EmberObject.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"); }); test("it should use a null value specified in the actionContext argument", function() { expect(2); var obj = EmberObject.createWithMixins(TargetActionSupport,{ target: EmberObject.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"); }); }); define("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.'); }); }); define("ember-runtime/tests/props_helper", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; var getFromEmberMetal = __dependency2__.get; var getWithDefaultFromEmberMetal = __dependency2__.getWithDefault; var setFromEmberMetal = __dependency3__.set; // used by unit tests to test both accessor mode and non-accessor mode var testBoth = function(testname, callback) { function emberget(x,y) { return getFromEmberMetal(x,y); } function emberset(x,y,z) { return setFromEmberMetal(x,y,z); } function aget(x,y) { return x[y]; } function aset(x,y,z) { return (x[y] = z); } test(testname+' using getFromEmberMetal()/Ember.set()', function() { callback(emberget, emberset); }); test(testname+' using accessors', function() { if (Ember.USES_ACCESSORS) callback(aget, aset); else ok('SKIPPING ACCESSORS'); }); }; var testWithDefault = function(testname, callback) { function get(x,y) { return x.get(y); } function emberget(x,y) { return getFromEmberMetal(x,y); } function embergetwithdefault(x,y,z) { return getWithDefaultFromEmberMetal(x,y,z); } function getwithdefault(x,y,z) { return x.getWithDefault(y,z); } function emberset(x,y,z) { return setFromEmberMetal(x,y,z); } function aget(x,y) { return x[y]; } function aset(x,y,z) { return (x[y] = z); } test(testname+' using obj.get()', function() { callback(emberget, emberset); }); test(testname+' using obj.getWithDefault()', function() { callback(getwithdefault, emberset); }); test(testname+' using getFromEmberMetal()', function() { callback(emberget, emberset); }); test(testname+' using Ember.getWithDefault()', function() { callback(embergetwithdefault, emberset); }); test(testname+' using accessors', function() { if (Ember.USES_ACCESSORS) callback(aget, aset); else ok('SKIPPING ACCESSORS'); }); }; __exports__.testWithDefault = testWithDefault; __exports__.testBoth = testBoth; }); define("ember-runtime/tests/props_helper.jshint", [], function() { "use strict"; module('JSHint - ember-runtime/tests'); test('ember-runtime/tests/props_helper.js should pass jshint', function() { ok(true, 'ember-runtime/tests/props_helper.js should pass jshint.'); }); }); define("ember-runtime/tests/suites/array", ["ember-runtime/tests/suites/enumerable","ember-runtime/tests/suites/array/indexOf","ember-runtime/tests/suites/array/lastIndexOf","ember-runtime/tests/suites/array/objectAt","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var EnumerableTests = __dependency1__.EnumerableTests; var ObserverClass = __dependency1__.ObserverClass; var indexOfTests = __dependency2__["default"]; var lastIndexOfTests = __dependency3__["default"]; var objectAtTests = __dependency4__["default"]; var EnumerableTestsObserverClass = ObserverClass; ObserverClass = EnumerableTestsObserverClass.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 = EnumerableTests.extend({ observerClass: ObserverClass }); ArrayTests.ObserverClass = ObserverClass; ArrayTests.importModuleTests(indexOfTests); ArrayTests.importModuleTests(lastIndexOfTests); ArrayTests.importModuleTests(objectAtTests); __exports__.ArrayTests = ArrayTests; __exports__.ObserverClass = ObserverClass; }); define("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.'); }); }); define("ember-runtime/tests/suites/array/indexOf", ["ember-runtime/tests/suites/suite","ember-runtime/system/string","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var fmt = __dependency2__.fmt; var suite = SuiteModuleBuilder.create(); suite.module('indexOf'); suite.test("should return index of object", function() { var expected = this.newFixture(3), obj = this.newObject(expected), len = 3, idx; for(idx=0;idx=0) obj.addBeforeObserver(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), 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.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: 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(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 = {}; 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: 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(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 = 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 }); var anyTests = __dependency7__["default"]; var isAnyTests = __dependency8__["default"]; var compactTests = __dependency9__["default"]; var containsTests = __dependency10__["default"]; var everyTests = __dependency11__["default"]; var filterTests = __dependency12__["default"]; var findTests = __dependency13__["default"]; var firstObjectTests = __dependency14__["default"]; var forEachTests = __dependency15__["default"]; var mapByTests = __dependency16__["default"]; var invokeTests = __dependency17__["default"]; var lastObjectTests = __dependency18__["default"]; var mapTests = __dependency19__["default"]; var reduceTests = __dependency20__["default"]; var rejectTests = __dependency21__["default"]; var sortByTests = __dependency22__["default"]; var toArrayTests = __dependency23__["default"]; var uniqTests = __dependency24__["default"]; var withoutTests = __dependency25__["default"]; EnumerableTests.importModuleTests(anyTests); EnumerableTests.importModuleTests(isAnyTests); EnumerableTests.importModuleTests(compactTests); EnumerableTests.importModuleTests(containsTests); EnumerableTests.importModuleTests(everyTests); EnumerableTests.importModuleTests(filterTests); EnumerableTests.importModuleTests(findTests); EnumerableTests.importModuleTests(firstObjectTests); EnumerableTests.importModuleTests(forEachTests); EnumerableTests.importModuleTests(mapByTests); EnumerableTests.importModuleTests(invokeTests); EnumerableTests.importModuleTests(lastObjectTests); EnumerableTests.importModuleTests(mapTests); EnumerableTests.importModuleTests(reduceTests); EnumerableTests.importModuleTests(rejectTests); EnumerableTests.importModuleTests(sortByTests); EnumerableTests.importModuleTests(toArrayTests); EnumerableTests.importModuleTests(uniqTests); EnumerableTests.importModuleTests(withoutTests); __exports__["default"] = EnumerableTests; __exports__.EnumerableTests = EnumerableTests; __exports__.ObserverClass = ObserverClass; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/any", ["ember-metal/core","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var 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(), ary = this.toArray(obj), found = [], 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(), ary = this.toArray(obj), cnt = ary.length - 2, exp = cnt, found = [], 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.A([0, 1, 2]), 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.A([0, null, false]), 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.A([undefined]), 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(), ary = this.toArray(obj), anyFound = [], anyResult, someFound = [], someResult, cnt = ary.length - 2, exp = cnt; 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/compact", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/contains", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/every", ["ember-runtime/system/object","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var 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(), ary = this.toArray(obj), found = [], 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(), ary = this.toArray(obj), cnt = ary.length - 2, exp = cnt, found = [], 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.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.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.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.create({ bar: undefined }) ]); equal(obj.isEvery('foo', undefined), true, "isEvery('foo', undefined)"); equal(obj.isEvery('bar', undefined), false, "isEvery('bar', undefined)"); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/filter", ["ember-runtime/system/object","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var suite = SuiteModuleBuilder.create(); // .......................................................... // filter() // suite.module('filter'); suite.test('filter should invoke on each item', function() { var obj = this.newObject(), ary = this.toArray(obj), cnt = ary.length - 2, found = [], 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.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.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.create({ name: 'obj2', foo: 2}), { name: 'obj3', foo: 2}, EmberObject.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.create({ name: 'obj2', foo: null}), { name: 'obj3', foo: null}, EmberObject.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.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.create({ name: 'obj2', foo: 3}), { name: 'obj3', foo: undefined}, EmberObject.create({ name: 'obj4', foo: undefined}), { name: 'obj5'}, EmberObject.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.create({ name: 'obj2', foo: 3}), { name: 'obj3', foo: undefined}, EmberObject.create({ name: 'obj4', foo: undefined}), { name: 'obj5'}, EmberObject.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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/find", ["ember-runtime/system/object","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var 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(), ary = this.toArray(obj), found = [], 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(), ary = this.toArray(obj), cnt = ary.length - 2, exp = cnt, found = [], 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.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.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.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.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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/firstObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var suite = SuiteModuleBuilder.create(); suite.module('firstObject'); suite.test('returns first item in enumerable', function() { var obj = this.newObject(); equal(get(obj, 'firstObject'), this.toArray(obj)[0]); }); suite.test('returns undefined if enumerable is empty', function() { var obj = this.newObject([]); equal(get(obj, 'firstObject'), undefined); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/forEach", ["ember-runtime/tests/suites/suite","ember-metal/property_get","ember-metal/utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var guidFor = __dependency3__.guidFor; var suite = SuiteModuleBuilder.create(); var global = this; suite.module('forEach'); suite.test('forEach should iterate over list', function() { var obj = this.newObject(), ary = this.toArray(obj), 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 (get(this, 'canTestMutation')) { expect(0); return ; } var obj = this.newObject(), ary = this.toArray(obj), 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(), 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(guidFor(this), guidFor(target), 'should pass target as this if context'); }, target); }); suite.test('callback params', function() { var obj = this.newObject(), ary = this.toArray(obj), loc = 0; obj.forEach(function(item, idx, enumerable) { equal(item, ary[loc], 'item param'); equal(idx, loc, 'idx param'); equal(guidFor(enumerable), guidFor(obj), 'enumerable param'); loc++; }); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/invoke", ["ember-runtime/system/object","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var 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.create({ foo: F }), // NOTE: does not impl foo - invoke should just skip EmberObject.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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/is_any", ["ember-runtime/system/object","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var 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.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.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.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.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.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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/lastObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var suite = SuiteModuleBuilder.create(); suite.module('lastObject'); suite.test('returns last item in enumerable', function() { var obj = this.newObject(), ary = this.toArray(obj); equal(get(obj, 'lastObject'), ary[ary.length-1]); }); suite.test('returns undefined if enumerable is empty', function() { var obj = this.newObject([]); equal(get(obj, 'lastObject'), undefined); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/map", ["ember-runtime/tests/suites/suite","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var EnumerableUtils = __dependency2__["default"]; var get = __dependency3__.get; var guidFor = __dependency4__.guidFor; var suite = SuiteModuleBuilder.create(), global = this; suite.module('map'); function mapFunc(item) { return item ? item.toString() : null; } suite.test('map should iterate over list', function() { var obj = this.newObject(), ary = EnumerableUtils.map(this.toArray(obj), mapFunc), found = []; found = obj.map(mapFunc); deepEqual(found, ary, 'mapped arrays should match'); }); suite.test('map should iterate over list after mutation', function() { if (get(this, 'canTestMutation')) { expect(0); return ; } var obj = this.newObject(), ary = this.toArray(obj).map(mapFunc), 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(), 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(guidFor(this), guidFor(target), 'should pass target as this if context'); }, target); }); suite.test('callback params', function() { var obj = this.newObject(), ary = this.toArray(obj), loc = 0; obj.map(function(item, idx, enumerable) { equal(item, ary[loc], 'item param'); equal(idx, loc, 'idx param'); equal(guidFor(enumerable), guidFor(obj), 'enumerable param'); loc++; }); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/mapBy", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/reduce", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var suite = SuiteModuleBuilder.create(); suite.module('reduce'); suite.test('collectes 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/reject", ["ember-runtime/system/object","ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var SuiteModuleBuilder = __dependency2__.SuiteModuleBuilder; var 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]), 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]), isEven = function(i) { return i % 2 === 0; }, 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.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.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.create({ name: 'obj2', foo: 2}), { name: 'obj3', foo: 2}, EmberObject.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.create({ name: 'obj2', foo: null}), { name: 'obj3', foo: null}, EmberObject.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.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.create({ name: 'obj2', foo: 3}), { name: 'obj3', foo: undefined}, EmberObject.create({ name: 'obj4', foo: undefined}), { name: 'obj5'}, EmberObject.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.create({ name: 'obj2', foo: 3}), { name: 'obj3', foo: undefined}, EmberObject.create({ name: 'obj4', foo: undefined}), { name: 'obj5'}, EmberObject.create({ name: 'obj6'}), { name: 'obj7', foo: null }, EmberObject.create({ name: 'obj8', foo: null }), { name: 'obj9', foo: false }, EmberObject.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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/sortBy", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var suite = SuiteModuleBuilder.create(); suite.module('sortBy'); suite.test('sort by value of property', function() { var obj = this.newObject([{a: 2},{a: 1}]), sorted = obj.sortBy('a'); equal(get(sorted[0], 'a'), 1); equal(get(sorted[1], 'a'), 2); }); suite.test('supports multiple propertyNames', function() { var obj = this.newObject([{a: 1, b: 2},{a: 1, b: 1}]), sorted = obj.sortBy('a', 'b'); equal(get(sorted[0], 'b'), 1); equal(get(sorted[1], 'b'), 2); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/toArray", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/uniq", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/enumerable/without", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array", ["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","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { "use strict"; var ArrayTests = __dependency1__.ArrayTests; var ObserverClass = __dependency1__.ObserverClass; var insertAtTests = __dependency2__["default"]; var popObjectTests = __dependency3__["default"]; var pushObjectTests = __dependency4__["default"]; var pushObjectsTest = __dependency5__["default"]; var removeAtTests = __dependency6__["default"]; var replaceTests = __dependency7__["default"]; var shiftObjectTests = __dependency8__["default"]; var unshiftObjectTests = __dependency9__["default"]; var reverseObjectsTests = __dependency10__["default"]; var MutableArrayTests = ArrayTests.extend(); MutableArrayTests.importModuleTests(insertAtTests); MutableArrayTests.importModuleTests(popObjectTests); MutableArrayTests.importModuleTests(pushObjectTests); MutableArrayTests.importModuleTests(pushObjectsTest); MutableArrayTests.importModuleTests(removeAtTests); MutableArrayTests.importModuleTests(replaceTests); MutableArrayTests.importModuleTests(shiftObjectTests); MutableArrayTests.importModuleTests(unshiftObjectTests); MutableArrayTests.importModuleTests(reverseObjectsTests); __exports__["default"] = MutableArrayTests; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/addObject", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/clear", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/insertAt", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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([]), that = this; raises(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(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(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)), that = this; raises(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(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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/popObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/pushObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/pushObjects", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var suite = SuiteModuleBuilder.create(); suite.module('pushObjects'); suite.test("should raise exception if not Ember.Enumerable is passed to pushObjects", function() { var obj = this.newObject([]); raises(function() { obj.pushObjects( "string" ); }); }); __exports__["default"] = suite; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/removeAt", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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([]); raises(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(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(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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/removeObject", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/replace", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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, replace, 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/reverseObjects", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/setObjects", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/shiftObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/unshiftObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_array/unshiftObjects", ["ember-runtime/tests/suites/suite","exports"], function(__dependency1__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var 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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_enumerable", ["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","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; var EnumerableTests = __dependency1__.EnumerableTests; var ObserverClass = __dependency1__.ObserverClass; var addObjectTests = __dependency2__["default"]; var removeObjectTests = __dependency3__["default"]; var removeObjectsTests = __dependency4__["default"]; var MutableEnumerableTests = EnumerableTests.extend(); MutableEnumerableTests.importModuleTests(addObjectTests); MutableEnumerableTests.importModuleTests(removeObjectTests); MutableEnumerableTests.importModuleTests(removeObjectsTests); __exports__["default"] = MutableEnumerableTests; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_enumerable/addObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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'); get(obj, 'firstObject'); get(obj, 'lastObject'); obj.addObject(item); deepEqual(this.toArray(obj), after, 'post item results'); equal(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_enumerable/removeObject", ["ember-runtime/tests/suites/suite","ember-metal/property_get","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var 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(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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/mutable_enumerable/removeObjects", ["ember-runtime/tests/suites/suite","ember-metal/property_get","ember-metal/core","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var SuiteModuleBuilder = __dependency1__.SuiteModuleBuilder; var get = __dependency2__.get; var Ember = __dependency3__["default"]; var suite = SuiteModuleBuilder.create(); suite.module('removeObjects'); suite.test("should return receiver", function() { var before, obj; before = Ember.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.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(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.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(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.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(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.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(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.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(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.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(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.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(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; }); define("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.'); }); }); define("ember-runtime/tests/suites/suite", ["ember-runtime/system/object","ember-metal/mixin","ember-metal/utils","ember-metal/property_get","ember-metal/enumerable_utils","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; var required = __dependency2__.required; var guidFor = __dependency3__.guidFor; var generateGuid = __dependency3__.generateGuid; var get = __dependency4__.get; var forEach = __dependency5__.forEach; /** @class A Suite can be used to define a reusable set of unit tests that can be applied to any object. Suites are most useful for defining tests that work against a mixin or plugin API. Developers implementing objects that use the mixin or support the API can then run these tests against their own code to verify compliance. To define a suite, you need to define the tests themselves as well as a callback API implementors can use to tie your tests to thier specific class. ## Defining a Callback API To define the callback API, just extend this class and add your properties or methods that must be provided. Use Ember.required() placeholders for any properties that implementors must define themselves. ## Defining Unit Tests To add unit tests, use the suite.module() or suite.test() methods instead of a regular module() or test() method when defining your tests. This will add the tests to the suite. ## Using a Suite To use a Suite to test your own objects, extend the suite subclass and define any required methods. Then call run() on the new subclass. This will create an instance of your class and then defining the unit tests. @extends Ember.Object @private */ var Suite = EmberObject.extend({ /** Define a name for these tests - all modules are prefixed w/ it. @type String */ name: 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, teardown = opts.teardown; this.reopen({ run: function() { this._super(); var title = get(this, 'name')+': '+desc, 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(); var ctx = this; if (!func) test(name); // output warning else 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 guidFor(x); }) : actual; exp = (exp && exp.map) ? exp.map(function(x) { return 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); forEach(builder._tests, function(descAndFunc) { self.test.apply(self, descAndFunc); }); } }); var SuiteModuleBuilder = EmberObject.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__.SuiteModuleBuilder = SuiteModuleBuilder; __exports__.Suite = Suite; __exports__["default"] = Suite; }); define("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.'); }); }); define("ember-runtime/tests/system/application/base_test", ["ember-runtime/system/namespace","ember-runtime/system/application"], function(__dependency1__, __dependency2__) { "use strict"; var Namespace = __dependency1__["default"]; var Application = __dependency2__["default"]; QUnit.module('Ember.Application'); test('Ember.Application should be a subclass of Ember.Namespace', function() { ok(Namespace.detect(Application), 'Ember.Application subclass of Ember.Namespace'); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var computed = __dependency3__.computed; var ArrayProxy = __dependency4__["default"]; var array; QUnit.module("ArrayProxy - arrangedContent", { setup: function() { run(function() { array = ArrayProxy.createWithMixins({ content: Ember.A([1,2,4,5]), arrangedContent: computed(function() { var content = this.get('content'); return content && Ember.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(function() { array.destroy(); }); } }); test("addObject - adds to end of 'content' if not present", function() { run(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(function() { array.addObject(1); }); deepEqual(array.get('content'), [1,2,4,5,3], 'does not add existing number to content'); }); test("addObjects - adds to end of 'content' if not present", function() { run(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'); }); test("compact - returns arrangedContent without nulls and undefined", function() { run(function() { array.set('content', Ember.A([1,3,null,2,undefined])); }); deepEqual(array.compact(), [3,2,1]); }); test("indexOf - returns index of object in arrangedContent", function() { equal(array.indexOf(4), 1, 'returns arranged index'); }); test("insertAt - raises, indeterminate behavior", function() { raises(function() { run(function() { array.insertAt(2,3); }); }); }); test("lastIndexOf - returns last index of object in arrangedContent", function() { run(function() { array.pushObject(4); }); equal(array.lastIndexOf(4), 2, 'returns last arranged index'); }); test("nextObject - returns object at index in arrangedContent", function() { equal(array.nextObject(1), 4, 'returns object at index'); }); 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 test("objectAtContent - returns object at index in arrangedContent", function() { equal(array.objectAtContent(1), 4, 'returns object at index'); }); test("objectsAt - returns objects at indices in arrangedContent", function() { deepEqual(array.objectsAt([0,2,4]), [5,2,undefined], 'returns objects at indices'); }); test("popObject - removes last object in arrangedContent", function() { var popped; run(function() { popped = array.popObject(); }); equal(popped, 1, 'returns last object'); deepEqual(array.get('content'), [2,4,5], 'removes from content'); }); test("pushObject - adds to end of content even if it already exists", function() { run(function() { array.pushObject(1); }); deepEqual(array.get('content'), [1,2,4,5,1], 'adds to end of content'); }); test("pushObjects - adds multiple to end of content even if it already exists", function() { run(function() { array.pushObjects([1,2,4]); }); deepEqual(array.get('content'), [1,2,4,5,1,2,4], 'adds to end of content'); }); test("removeAt - removes from index in arrangedContent", function() { run(function() { array.removeAt(1,2); }); deepEqual(array.get('content'), [1,5]); }); test("removeObject - removes object from content", function() { run(function() { array.removeObject(2); }); deepEqual(array.get('content'), [1,4,5]); }); test("removeObjects - removes objects from content", function() { run(function() { array.removeObjects([2,4,6]); }); deepEqual(array.get('content'), [1,5]); }); test("replace - raises, indeterminate behavior", function() { raises(function() { run(function() { array.replace(1, 2, [3]); }); }); }); test("replaceContent - does a standard array replace on content", function() { run(function() { array.replaceContent(1, 2, [3]); }); deepEqual(array.get('content'), [1,3,5]); }); test("reverseObjects - raises, use Sortable#sortAscending", function() { raises(function() { run(function() { array.reverseObjects(); }); }); }); test("setObjects - replaces entire content", function() { run(function() { array.setObjects([6,7,8]); }); deepEqual(array.get('content'), [6,7,8], 'replaces content'); }); test("shiftObject - removes from start of arrangedContent", function() { var shifted; run(function() { shifted = array.shiftObject(); }); equal(shifted, 5, 'returns first object'); deepEqual(array.get('content'), [1,2,4], 'removes object from content'); }); test("slice - returns a slice of the arrangedContent", function() { deepEqual(array.slice(1,3), [4,2], 'returns sliced arrangedContent'); }); test("toArray - returns copy of arrangedContent", function() { deepEqual(array.toArray(), [5,4,2,1]); }); test("unshiftObject - adds to start of content", function() { run(function() { array.unshiftObject(6); }); deepEqual(array.get('content'), [6,1,2,4,5], 'adds to start of content'); }); test("unshiftObjects - adds to start of content", function() { run(function() { array.unshiftObjects([6,7]); }); deepEqual(array.get('content'), [6,7,1,2,4,5], 'adds to start of content'); }); test("without - returns arrangedContent without object", function() { deepEqual(array.without(2), [5,4,1], 'returns arranged without object'); }); test("lastObject - returns last arranged object", function() { equal(array.get('lastObject'), 1, 'returns last arranged object'); }); 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(function() { array = ArrayProxy.createWithMixins({ content: Ember.A([1,2,4,5]) }); }); }, teardown: function() { run(function() { array.destroy(); }); } }); test("insertAt - inserts object at specified index", function() { run(function() { array.insertAt(2, 3); }); deepEqual(array.get('content'), [1,2,3,4,5]); }); test("replace - does a standard array replace", function() { run(function() { array.replace(1, 2, [3]); }); deepEqual(array.get('content'), [1,3,5]); }); test("reverseObjects - reverses content", function() { run(function() { array.reverseObjects(); }); deepEqual(array.get('content'), [5,4,2,1]); }); QUnit.module("ArrayProxy - arrangedContent with transforms", { setup: function() { run(function() { array = ArrayProxy.createWithMixins({ content: Ember.A([1,2,4,5]), arrangedContent: computed(function() { var content = this.get('content'); return content && Ember.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(function() { array.destroy(); }); } }); test("indexOf - returns index of object in arrangedContent", function() { equal(array.indexOf('4'), 1, 'returns arranged index'); }); test("lastIndexOf - returns last index of object in arrangedContent", function() { run(function() { array.pushObject(4); }); equal(array.lastIndexOf('4'), 2, 'returns last arranged index'); }); test("nextObject - returns object at index in arrangedContent", function() { equal(array.nextObject(1), '4', 'returns object at index'); }); 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 test("objectAtContent - returns object at index in arrangedContent", function() { equal(array.objectAtContent(1), '4', 'returns object at index'); }); test("objectsAt - returns objects at indices in arrangedContent", function() { deepEqual(array.objectsAt([0,2,4]), ['5','2',undefined], 'returns objects at indices'); }); test("popObject - removes last object in arrangedContent", function() { var popped; run(function() { popped = array.popObject(); }); equal(popped, '1', 'returns last object'); deepEqual(array.get('content'), [2,4,5], 'removes from content'); }); test("removeObject - removes object from content", function() { run(function() { array.removeObject('2'); }); deepEqual(array.get('content'), [1,4,5]); }); test("removeObjects - removes objects from content", function() { run(function() { array.removeObjects(['2','4','6']); }); deepEqual(array.get('content'), [1,5]); }); test("shiftObject - removes from start of arrangedContent", function() { var shifted; run(function() { shifted = array.shiftObject(); }); equal(shifted, '5', 'returns first object'); deepEqual(array.get('content'), [1,2,4], 'removes object from content'); }); test("slice - returns a slice of the arrangedContent", function() { deepEqual(array.slice(1,3), ['4','2'], 'returns sliced arrangedContent'); }); test("toArray - returns copy of arrangedContent", function() { deepEqual(array.toArray(), ['5','4','2','1']); }); test("without - returns arrangedContent without object", function() { deepEqual(array.without('2'), ['5','4','1'], 'returns arranged without object'); }); test("lastObject - returns last arranged object", function() { equal(array.get('lastObject'), '1', 'returns last arranged object'); }); test("firstObject - returns first arranged object", function() { equal(array.get('firstObject'), '5', 'returns first arranged object'); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var set = __dependency2__.set; var run = __dependency3__["default"]; var ArrayProxy = __dependency4__["default"]; var ArrayController = __dependency5__["default"]; QUnit.module("ArrayProxy - content change"); test("should update length for null content", function() { var proxy = ArrayProxy.create({ content: Ember.A([1,2,3]) }); equal(proxy.get('length'), 3, "precond - length is 3"); proxy.set('content', null); equal(proxy.get('length'), 0, "length updates"); }); test("The `arrangedContentWillChange` method is invoked before `content` is changed.", function() { var callCount = 0, expectedLength; var proxy = ArrayProxy.extend({ content: Ember.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.A(['a', 'b'])); equal(callCount, 1, "replacing the content array triggers the hook"); }); test("The `arrangedContentDidChange` method is invoked after `content` is changed.", function() { var callCount = 0, expectedLength; var proxy = ArrayProxy.extend({ content: Ember.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.A(['a', 'b'])); equal(callCount, 1, "replacing the content array triggers the hook"); }); test("The ArrayProxy doesn't explode when assigned a destroyed object", function() { var arrayController = ArrayController.create(); var proxy = ArrayProxy.create(); run(function() { arrayController.destroy(); }); set(proxy, 'content', arrayController); ok(true, "No exception was raised"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/array_proxy/content_update_test", ["ember-metal/core","ember-metal/computed","ember-runtime/system/array_proxy"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var computed = __dependency2__.computed; var ArrayProxy = __dependency3__["default"]; QUnit.module("Ember.ArrayProxy - content update"); test("The `contentArrayDidChange` method is invoked after `content` is updated.", function() { var proxy, observerCalled = false; proxy = ArrayProxy.createWithMixins({ content: Ember.A(), arrangedContent: computed('content', function(key, value) { // setup arrangedContent as a different object than content, // which is the default return Ember.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"); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var MutableArrayTests = __dependency2__["default"]; var ArrayProxy = __dependency3__["default"]; var get = __dependency4__.get; MutableArrayTests.extend({ name: 'Ember.ArrayProxy', newObject: function(ary) { var ret = ary ? ary.slice() : this.newFixture(3); return ArrayProxy.create({ content: Ember.A(ret) }); }, mutate: function(obj) { obj.pushObject(get(obj, 'length')+1); }, toArray: function(obj) { return obj.toArray ? obj.toArray() : obj.slice(); } }).run(); }); define("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.'); }); }); define("ember-runtime/tests/system/deferred_test", ["ember-metal/core","ember-metal/run_loop","ember-runtime/system/deferred"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var Deferred = __dependency3__["default"]; QUnit.module("Ember.Deferred all-in-one"); asyncTest("Can resolve a promise", function() { var value = { value: true }; ignoreDeprecation(function() { var promise = Deferred.promise(function(deferred) { setTimeout(function() { run(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.promise(function(deferred) { setTimeout(function() { run(function() { deferred.reject(rejected); }); }); }); promise.then(null, function(rejectedValue) { QUnit.start(); equal(rejectedValue, rejected, "The resolved value should be correct"); }); }); }); }); define("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.'); }); }); define("ember-runtime/tests/system/lazy_load_test", ["ember-metal/core","ember-metal/run_loop","ember-runtime/system/lazy_load"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var onLoad = __dependency3__.onLoad; var runLoadHooks = __dependency3__.runLoadHooks; QUnit.module("Lazy Loading"); test("if a load hook is registered, it is executed when runLoadHooks are exected", function() { var count = 0; run(function() { onLoad("__test_hook__", function(object) { count += object; }); }); run(function() { runLoadHooks("__test_hook__", 1); }); equal(count, 1, "the object was passed into the load hook"); }); test("if runLoadHooks was already run, it executes newly added hooks immediately", function() { var count = 0; run(function() { onLoad("__test_hook__", function(object) { count += object; }); }); run(function() { runLoadHooks("__test_hook__", 1); }); count = 0; run(function() { onLoad("__test_hook__", function(object) { count += object; }); }); equal(count, 1, "the original object was passed into the load hook"); }); 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(function() { 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") { 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(function() { runLoadHooks("__test_hook_for_events__", eventObject); }); }); } }); define("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.'); }); }); define("ember-runtime/tests/system/namespace/base_test", ["ember-metal/core","ember-metal/run_loop","ember-metal/property_get","ember-runtime/system/object","ember-runtime/mixins/array","ember-runtime/system/namespace"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var get = __dependency3__.get; var EmberObject = __dependency4__["default"]; var EmberArray = __dependency5__["default"]; var Namespace = __dependency6__["default"]; var originalLookup, lookup; QUnit.module('Namespace', { setup: function() { originalLookup = Ember.lookup; Ember.BOOTED = false; lookup = Ember.lookup = {}; }, teardown: function() { Ember.BOOTED = false; for(var prop in lookup) { if (lookup[prop]) { run(lookup[prop], 'destroy'); } } Ember.lookup = originalLookup; } }); test('Namespace should be a subclass of EmberObject', function() { ok(EmberObject.detect(Namespace)); }); test("Namespace should be duck typed", function() { ok(get(Namespace.create(), 'isNamespace'), "isNamespace property is true"); }); test('Namespace is found and named', function() { var nsA = lookup.NamespaceA = Namespace.create(); equal(nsA.toString(), "NamespaceA", "namespaces should have a name if they are on lookup"); var nsB = lookup.NamespaceB = Namespace.create(); equal(nsB.toString(), "NamespaceB", "namespaces work if created after the first namespace processing pass"); }); test("Classes under an Namespace are properly named", function() { var nsA = lookup.NamespaceA = Namespace.create(); nsA.Foo = EmberObject.extend(); equal(nsA.Foo.toString(), "NamespaceA.Foo", "Classes pick up their parent namespace"); nsA.Bar = EmberObject.extend(); equal(nsA.Bar.toString(), "NamespaceA.Bar", "New Classes get the naming treatment too"); var nsB = lookup.NamespaceB = Namespace.create(); nsB.Foo = EmberObject.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"); //}); test("Lowercase namespaces are no longer supported", function() { var nsC = lookup.namespaceC = Namespace.create(); equal(nsC.toString(), undefined); }); test("A namespace can be assigned a custom name", function() { var nsA = Namespace.create({ name: "NamespaceA" }); var nsB = lookup.NamespaceB = Namespace.create({ name: "CustomNamespaceB" }); nsA.Foo = EmberObject.extend(); nsB.Foo = EmberObject.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"); }); test("Calling namespace.nameClasses() eagerly names all classes", function() { Ember.BOOTED = true; var namespace = lookup.NS = Namespace.create(); namespace.ClassA = EmberObject.extend(); namespace.ClassB = EmberObject.extend(); Namespace.processAll(); equal(namespace.ClassA.toString(), "NS.ClassA"); equal(namespace.ClassB.toString(), "NS.ClassB"); }); test("A namespace can be looked up by its name", function() { var NS = lookup.NS = Namespace.create(); var UI = lookup.UI = Namespace.create(); var CF = lookup.CF = Namespace.create(); equal(Namespace.byName('NS'), NS); equal(Namespace.byName('UI'), UI); equal(Namespace.byName('CF'), CF); }); test("A nested namespace can be looked up by its name", function() { var UI = lookup.UI = Namespace.create(); UI.Nav = Namespace.create(); equal(Namespace.byName('UI.Nav'), UI.Nav); }); test("Destroying a namespace before caching lookup removes it from the list of namespaces", function(){ var CF = lookup.CF = Namespace.create(); run(CF,'destroy'); equal(Namespace.byName('CF'), undefined, "namespace can not be found after destroyed"); }); test("Destroying a namespace after looking up removes it from the list of namespaces", function(){ var CF = lookup.CF = Namespace.create(); equal(Namespace.byName('CF'), CF, "precondition - namespace can be looked up by name"); run(CF,'destroy'); equal(Namespace.byName('CF'), undefined, "namespace can not be found after destroyed"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/native_array/copyable_suite_test", ["ember-runtime/tests/suites/copyable","ember-metal/utils"], function(__dependency1__, __dependency2__) { "use strict"; var CopyableTests = __dependency1__["default"]; var generateGuid = __dependency2__.generateGuid; CopyableTests.extend({ name: 'NativeArray Copyable', newObject: function() { return Ember.A([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"); 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"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/native_array/suite_test", ["ember-runtime/tests/suites/mutable_array"], function(__dependency1__) { "use strict"; var MutableArrayTests = __dependency1__["default"]; MutableArrayTests.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(); }); define("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.'); }); }); define("ember-runtime/tests/system/object/computed_test", ["ember-metal/computed","ember-metal/property_get","ember-metal/mixin","ember-runtime/tests/props_helper","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var computed = __dependency1__.computed; var emberGet = __dependency2__.get; var observer = __dependency3__.observer; var testWithDefault = __dependency4__.testWithDefault; var testBoth = __dependency4__.testBoth; var EmberObject = __dependency5__["default"]; QUnit.module('EmberObject computed property'); testWithDefault('computed property on instance', function(get, set) { var MyClass = EmberObject.extend({ foo: computed(function() { return 'FOO'; }) }); equal(get(new MyClass(), 'foo'), 'FOO'); }); testWithDefault('computed property on subclass', function(get, set) { var MyClass = EmberObject.extend({ foo: computed(function() { return 'FOO'; }) }); var Subclass = MyClass.extend({ foo: computed(function() { return 'BAR'; }) }); equal(get(new Subclass(), 'foo'), 'BAR'); }); testWithDefault('replacing computed property with regular val', function(get, set) { var MyClass = EmberObject.extend({ foo: computed(function() { return 'FOO'; }) }); var Subclass = MyClass.extend({ foo: 'BAR' }); equal(get(new Subclass(), 'foo'), 'BAR'); }); testWithDefault('complex depndent keys', function(get, set) { var MyClass = EmberObject.extend({ init: function() { this._super(); set(this, 'bar', { baz: 'BIFF' }); }, count: 0, foo: computed(function() { set(this, 'count', get(this, 'count')+1); return emberGet(get(this, 'bar'), 'baz') + ' ' + get(this, 'count'); }).property('bar.baz') }); var Subclass = MyClass.extend({ count: 20 }); var obj1 = new MyClass(), 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'); }); testWithDefault('complex depndent keys changing complex dependent keys', function(get, set) { var MyClass = EmberObject.extend({ init: function() { this._super(); set(this, 'bar', { baz: 'BIFF' }); }, count: 0, foo: computed(function() { set(this, 'count', get(this, 'count')+1); return emberGet(get(this, 'bar'), 'baz') + ' ' + get(this, 'count'); }).property('bar.baz') }); var Subclass = MyClass.extend({ init: function() { this._super(); set(this, 'bar2', { baz: 'BIFF2' }); }, count: 0, foo: computed(function() { set(this, 'count', get(this, 'count')+1); return emberGet(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'); }); test("can retrieve metadata for a computed property", function() { var MyClass = EmberObject.extend({ computedProperty: computed(function() { }).meta({ key: 'keyValue' }) }); equal(emberGet(MyClass.metaForProperty('computedProperty'), 'key'), 'keyValue', "metadata saved on the computed property can be retrieved"); var ClassWithNoMetadata = EmberObject.extend({ computedProperty: 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'."); }); test("can iterate over a list of computed properties for a class", function() { var MyClass = EmberObject.extend({ foo: computed(function() { }), fooDidChange: observer('foo', function() { }), bar: computed(function() { }) }); var SubClass = MyClass.extend({ baz: computed(function() { }) }); SubClass.reopen({ bat: 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"); }); test("list of properties updates when an additional property is added (such cache busting)", function() { var MyClass = EmberObject.extend({ foo: computed(Ember.K), fooDidChange: observer('foo', function() { }), bar: computed(Ember.K) }); var list = []; MyClass.eachComputedProperty(function(name) { list.push(name); }); deepEqual(list.sort(), ['bar', 'foo'].sort(), 'expected two computed properties'); MyClass.reopen({ baz: computed(Ember.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'); }); }); define("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.'); }); }); define("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"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var guidFor = __dependency4__.guidFor; var computed = __dependency5__.computed; var required = __dependency6__.required; var Mixin = __dependency6__.Mixin; var observer = __dependency6__.observer; var run = __dependency7__["default"]; var on = __dependency8__.on; var EmberObject = __dependency9__["default"]; var moduleOptions, originalLookup; moduleOptions = { setup: function(){ originalLookup = Ember.lookup; Ember.lookup = {}; }, teardown: function(){ Ember.lookup = originalLookup; } }; QUnit.module('EmberObject.create', moduleOptions); test("simple properties are set", function() { var o = EmberObject.create({ohai: 'there'}); equal(o.get('ohai'), 'there'); }); test("calls computed property setters", function() { var MyClass = EmberObject.extend({ foo: 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'); }); if (Ember.ENV.MANDATORY_SETTER) { test("sets up mandatory setters for watched simple properties", function() { var MyClass = EmberObject.extend({ foo: null, bar: null, fooDidChange: observer('foo', function() {}) }); var o = MyClass.create({foo: 'bar', bar: 'baz'}); equal(o.get('foo'), 'bar'); // Catch IE8 where Object.getOwnPropertyDescriptor exists but only works on DOM elements try { Object.getOwnPropertyDescriptor({}, 'foo'); } catch(e) { return; } var descriptor = Object.getOwnPropertyDescriptor(o, 'foo'); ok(descriptor.set, 'Mandatory setter was setup'); descriptor = Object.getOwnPropertyDescriptor(o, 'bar'); ok(!descriptor.set, 'Mandatory setter was not setup'); }); } test("allows bindings to be defined", function() { var obj = EmberObject.create({ foo: 'foo', barBinding: 'foo' }); equal(obj.get('bar'), 'foo', 'The binding value is correct'); }); test("calls setUnknownProperty if defined", function() { var setUnknownPropertyCalled = false; var MyClass = EmberObject.extend({ setUnknownProperty: function(key, value) { setUnknownPropertyCalled = true; } }); var o = MyClass.create({foo: 'bar'}); ok(setUnknownPropertyCalled, 'setUnknownProperty was called'); }); test("throws if you try to define a computed property", function() { expectAssertion(function() { EmberObject.create({ foo: computed(function() {}) }); }, 'Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().'); }); test("throws if you try to call _super in a method", function() { expectAssertion(function() { EmberObject.create({ foo: function() { this._super(); } }); }, 'Ember.Object.create no longer supports defining methods that call _super.'); }); test("throws if you try to 'mixin' a definition", function() { var myMixin = Mixin.create({ adder: function(arg1, arg2) { return arg1 + arg2; } }); expectAssertion(function() { var o = EmberObject.create(myMixin); }, "Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead."); }); // This test is for IE8. test("property name is the same as own prototype property", function() { var MyClass = EmberObject.extend({ toString: function() { return 'MyClass'; } }); equal(MyClass.create().toString(), 'MyClass', "should inherit property from the arguments of `EmberObject.create`"); }); test("inherits properties from passed in EmberObject", function() { var baseObj = EmberObject.create({ foo: 'bar' }), secondaryObj = EmberObject.create(baseObj); equal(secondaryObj.foo, baseObj.foo, "Em.O.create inherits properties from EmberObject parameter"); }); test("throws if you try to pass anything a string as a parameter", function(){ var expected = "EmberObject.create only accepts an objects."; throws(function() { var o = EmberObject.create("some-string"); }, expected); }); test("EmberObject.create can take undefined as a parameter", function(){ var o = EmberObject.create(undefined); deepEqual(EmberObject.create(), o); }); test("EmberObject.create can take null as a parameter", function(){ var o = EmberObject.create(null); deepEqual(EmberObject.create(), o); }); QUnit.module('EmberObject.createWithMixins', moduleOptions); test("Creates a new object that contains passed properties", function() { var called = false; var obj = EmberObject.createWithMixins({ prop: 'FOO', method: function() { called=true; } }); equal(get(obj, 'prop'), 'FOO', 'obj.prop'); obj.method(); ok(called, 'method executed'); }); // .......................................................... // WORKING WITH MIXINS // test("Creates a new object that includes mixins and properties", function() { var MixinA = Mixin.create({ mixinA: 'A' }); var obj = EmberObject.createWithMixins(MixinA, { prop: 'FOO' }); equal(get(obj, 'mixinA'), 'A', 'obj.mixinA'); equal(get(obj, 'prop'), 'FOO', 'obj.prop'); }); // .......................................................... // LIFECYCLE // test("Configures _super() on methods with override", function() { var completed = false; var MixinA = Mixin.create({ method: function() {} }); var obj = EmberObject.createWithMixins(MixinA, { method: function() { this._super(); completed = true; } }); obj.method(); ok(completed, 'should have run method without error'); }); test("Calls init if defined", function() { var completed = false; var obj = EmberObject.createWithMixins({ init: function() { this._super(); completed = true; } }); ok(completed, 'should have run init without error'); }); test("Calls all mixin inits if defined", function() { var completed = 0; var Mixin1 = Mixin.create({ init: function() { this._super(); completed++; } }); var Mixin2 = Mixin.create({ init: function() { this._super(); completed++; } }); EmberObject.createWithMixins(Mixin1, Mixin2); equal(completed, 2, 'should have called init for both mixins.'); }); test("Triggers init", function() { var completed = false; var obj = EmberObject.createWithMixins({ markAsCompleted: on("init", function(){ completed = true; }) }); ok(completed, 'should have triggered init which should have run markAsCompleted'); }); test('creating an object with required properties', function() { var ClassA = EmberObject.extend({ foo: required() }); var obj = ClassA.createWithMixins({ foo: 'FOO' }); // should not throw equal(get(obj,'foo'), 'FOO'); }); // .......................................................... // BUGS // test('create should not break observed values', function() { var CountObject = EmberObject.extend({ value: null, _count: 0, reset: function() { this._count = 0; return this; }, valueDidChange: observer('value', function() { this._count++; }) }); var obj = CountObject.createWithMixins({ value: 'foo' }); equal(obj._count, 0, 'should not fire yet'); set(obj, 'value', 'BAR'); equal(obj._count, 1, 'should fire'); }); test('bindings on a class should only sync on instances', function() { Ember.lookup['TestObject'] = EmberObject.createWithMixins({ foo: 'FOO' }); var Class, inst; run(function() { Class = EmberObject.extend({ fooBinding: 'TestObject.foo' }); inst = Class.createWithMixins(); }); equal(get(Class.prototype, 'foo'), undefined, 'should not sync binding'); equal(get(inst, 'foo'), 'FOO', 'should sync binding'); }); test('inherited bindings should only sync on instances', function() { var TestObject; Ember.lookup['TestObject'] = TestObject = EmberObject.createWithMixins({ foo: 'FOO' }); var Class, Subclass, inst; run(function() { Class = EmberObject.extend({ fooBinding: 'TestObject.foo' }); }); run(function() { Subclass = Class.extend(); inst = Subclass.createWithMixins(); }); equal(get(Class.prototype, 'foo'), undefined, 'should not sync binding on Class'); equal(get(Subclass.prototype, 'foo'), undefined, 'should not sync binding on Subclass'); equal(get(inst, 'foo'), 'FOO', 'should sync binding on inst'); run(function() { set(TestObject, 'foo', 'BAR'); }); equal(get(Class.prototype, 'foo'), undefined, 'should not sync binding on Class'); equal(get(Subclass.prototype, 'foo'), undefined, 'should not sync binding on Subclass'); equal(get(inst, 'foo'), 'BAR', 'should sync binding on inst'); }); test("created objects should not share a guid with their superclass", function() { ok(guidFor(EmberObject), "EmberObject has a guid"); var objA = EmberObject.createWithMixins(), objB = EmberObject.createWithMixins(); ok(guidFor(objA) !== guidFor(objB), "two instances do not share a guid"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/destroy_test", ["ember-metal/core","ember-metal/run_loop","ember-metal/platform","ember-metal/mixin","ember-metal/property_set","ember-metal/binding","ember-metal/property_events","ember-metal/utils","ember-runtime/keys","ember-runtime/tests/props_helper","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var platform = __dependency3__.platform; var observer = __dependency4__.observer; var set = __dependency5__.set; var bind = __dependency6__.bind; var beginPropertyChanges = __dependency7__.beginPropertyChanges; var endPropertyChanges = __dependency7__.endPropertyChanges; var META_KEY = __dependency8__.META_KEY; var objectKeys = __dependency9__["default"]; var testBoth = __dependency10__.testBoth; var EmberObject = __dependency11__["default"]; QUnit.module('ember-runtime/system/object/destroy_test'); testBoth("should schedule objects to be destroyed at the end of the run loop", function(get, set) { var obj = EmberObject.create(), meta; run(function() { obj.destroy(); meta = obj[META_KEY]; 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[META_KEY]; ok(!meta, "meta is destroyed after run loop finishes"); ok(get(obj, 'isDestroyed'), "object is destroyed after run loop finishes"); }); test("should raise an exception when modifying watched properties on a destroyed object", function() { if (platform.hasAccessors) { var obj = EmberObject.createWithMixins({ foo: "bar", fooDidChange: observer('foo', function() { }) }); run(function() { obj.destroy(); }); raises(function() { set(obj, 'foo', 'baz'); }, Error, "raises an exception"); } else { expect(0); } }); test("observers should not fire after an object has been destroyed", function() { var count = 0; var obj = EmberObject.createWithMixins({ fooDidChange: observer('foo', function() { count++; }) }); obj.set('foo', 'bar'); equal(count, 1, "observer was fired once"); run(function() { beginPropertyChanges(); obj.set('foo', 'quux'); obj.destroy(); endPropertyChanges(); }); equal(count, 1, "observer was not called after object was destroyed"); }); test("destroyed objects should not see each others changes during teardown but a long lived object should", function () { var shouldChange = 0, shouldNotChange = 0; var objs = {}; var A = EmberObject.extend({ objs: objs, isAlive: true, willDestroy: function () { this.set('isAlive', false); }, bDidChange: observer('objs.b.isAlive', function () { shouldNotChange++; }), cDidChange: observer('objs.c.isAlive', function () { shouldNotChange++; }) }); var B = EmberObject.extend({ objs: objs, isAlive: true, willDestroy: function () { this.set('isAlive', false); }, aDidChange: observer('objs.a.isAlive', function () { shouldNotChange++; }), cDidChange: observer('objs.c.isAlive', function () { shouldNotChange++; }) }); var C = EmberObject.extend({ objs: objs, isAlive: true, willDestroy: function () { this.set('isAlive', false); }, aDidChange: observer('objs.a.isAlive', function () { shouldNotChange++; }), bDidChange: observer('objs.b.isAlive', function () { shouldNotChange++; }) }); var LongLivedObject = EmberObject.extend({ objs: objs, isAliveDidChange: observer('objs.a.isAlive', function () { shouldChange++; }) }); objs.a = new A(); objs.b = new B(); objs.c = new C(); var longLivedObject = new LongLivedObject(); run(function () { var keys = objectKeys(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'); }); test("bindings should be synced when are updated in the willDestroy hook", function() { var bar = EmberObject.create({ value: false, willDestroy: function() { this.set('value', true); } }); var foo = EmberObject.create({ value: null, bar: bar }); run(function() { bind(foo, 'value', 'bar.value'); }); ok(bar.get('value') === false, 'the initial value has been bound'); run(function() { bar.destroy(); }); ok(foo.get('value'), 'foo is synced when the binding is updated in the willDestroy hook'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/detectInstance_test", ["ember-runtime/system/object"], function(__dependency1__) { "use strict"; var EmberObject = __dependency1__["default"]; QUnit.module('system/object/detectInstance'); test('detectInstance detects instances correctly', function() { var A = EmberObject.extend(); var B = A.extend(); var C = A.extend(); var o = EmberObject.create(), a = A.create(), b = B.create(), c = C.create(); ok( EmberObject.detectInstance(o), 'o is an instance of EmberObject' ); ok( EmberObject.detectInstance(a), 'a is an instance of EmberObject' ); ok( EmberObject.detectInstance(b), 'b is an instance of EmberObject' ); ok( EmberObject.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' ); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/detect_test", ["ember-runtime/system/object"], function(__dependency1__) { "use strict"; var EmberObject = __dependency1__["default"]; QUnit.module('system/object/detect'); test('detect detects classes correctly', function() { var A = EmberObject.extend(); var B = A.extend(); var C = A.extend(); ok( EmberObject.detect(EmberObject), 'EmberObject is an EmberObject class' ); ok( EmberObject.detect(A), 'A is an EmberObject class' ); ok( EmberObject.detect(B), 'B is an EmberObject class' ); ok( EmberObject.detect(C), 'C is an EmberObject class' ); ok( !A.detect(EmberObject), '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), '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), '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' ); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/events_test", ["ember-runtime/system/object","ember-runtime/mixins/evented"], function(__dependency1__, __dependency2__) { "use strict"; var EmberObject = __dependency1__["default"]; var Evented = __dependency2__["default"]; QUnit.module("Object events"); test("a listener can be added to an object", function() { var count = 0; var F = function() { count++; }; var obj = EmberObject.createWithMixins(Evented); obj.on('event!', F); obj.trigger('event!'); equal(count, 1, "the event was triggered"); obj.trigger('event!'); equal(count, 2, "the event was triggered"); }); 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.createWithMixins(Evented); 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"); }); test("triggering an event can have arguments", function() { var self, args; var obj = EmberObject.createWithMixins(Evented); obj.on('event!', function() { args = [].slice.call(arguments); self = this; }); obj.trigger('event!', "foo", "bar"); deepEqual(args, [ "foo", "bar" ]); equal(self, obj); }); test("a listener can be added and removed automatically and have arguments", function() { var self, args, count = 0; var obj = EmberObject.createWithMixins(Evented); 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); }); test("binding an event can specify a different target", function() { var self, args; var obj = EmberObject.createWithMixins(Evented); 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); }); 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.createWithMixins(Evented); 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"); }); test("a listener registered with one can be removed with off", function() { var obj = EmberObject.createWithMixins(Evented, { 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'); }); test("adding and removing listeners should be chainable", function() { var obj = EmberObject.createWithMixins(Evented); 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'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/extend_test", ["ember-metal/property_get","ember-runtime/system/object"], function(__dependency1__, __dependency2__) { "use strict"; var get = __dependency1__.get; var EmberObject = __dependency2__["default"]; QUnit.module('EmberObject.extend'); test('Basic extend', function() { var SomeClass = EmberObject.extend({ foo: 'BAR' }); ok(SomeClass.isClass, "A class has isClass of true"); var obj = new SomeClass(); equal(obj.foo, 'BAR'); }); test('Sub-subclass', function() { var SomeClass = EmberObject.extend({ foo: 'BAR' }); var AnotherClass = SomeClass.extend({ bar: 'FOO' }); var obj = new AnotherClass(); equal(obj.foo, 'BAR'); equal(obj.bar, 'FOO'); }); test('Overriding a method several layers deep', function() { var SomeClass = EmberObject.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(); } }); var FinalClass = AnotherClass.extend({ fooCnt: 0, foo: function() { this.fooCnt++; this._super(); } }); 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(); } }); obj.foo(); obj.bar(); equal(obj.fooCnt, 3, 'should invoke final as well'); equal(obj.barCnt, 2, 'should invoke both'); }); test('With concatenatedProperties', function(){ var SomeClass = EmberObject.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 it's own"); deepEqual(yetAnother.get('things'), ['foo', 'baz'], "subclass should have base class' and it's own"); }); test('With concatenatedProperties class properties', function(){ var SomeClass = EmberObject.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(get(some.constructor, 'things'), ['foo'], 'base class should have just its value'); deepEqual(get(another.constructor, 'things'), ['foo', 'bar'], "subclass should have base class' and it's own"); deepEqual(get(yetAnother.constructor, 'things'), ['foo', 'baz'], "subclass should have base class' and it's own"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/observer_test", ["ember-metal/core","ember-metal/mixin","ember-metal/run_loop","ember-runtime/tests/props_helper","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var observer = __dependency2__.observer; var run = __dependency3__["default"]; var testWithDefault = __dependency4__.testWithDefault; var testBoth = __dependency4__.testBoth; var EmberObject = __dependency5__["default"]; QUnit.module('EmberObject observer'); testBoth('observer on class', function(get, set) { var MyClass = EmberObject.extend({ count: 0, foo: 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'); }); testBoth('observer on subclass', function(get, set) { var MyClass = EmberObject.extend({ count: 0, foo: observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var Subclass = MyClass.extend({ foo: 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'); }); testBoth('observer on instance', function(get, set) { var obj = EmberObject.createWithMixins({ count: 0, foo: 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'); }); testBoth('observer on instance overridding class', function(get, set) { var MyClass = EmberObject.extend({ count: 0, foo: observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); var obj = MyClass.createWithMixins({ foo: 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'); }); testBoth('observer should not fire after being destroyed', function(get, set) { var obj = EmberObject.createWithMixins({ count: 0, foo: observer('bar', function() { set(this, 'count', get(this, 'count')+1); }) }); equal(get(obj, 'count'), 0, 'precond - should not invoke observer immediately'); run(function() { obj.destroy(); }); if (Ember.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 // testBoth('chain observer on class', function(get, set) { var MyClass = EmberObject.extend({ count: 0, foo: 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'); }); testBoth('chain observer on class', function(get, set) { var MyClass = EmberObject.extend({ count: 0, foo: 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: 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'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/reopenClass_test", ["ember-metal/property_get","ember-runtime/system/object"], function(__dependency1__, __dependency2__) { "use strict"; var get = __dependency1__.get; var EmberObject = __dependency2__["default"]; QUnit.module('system/object/reopenClass'); test('adds new properties to subclass', function() { var Subclass = EmberObject.extend(); Subclass.reopenClass({ foo: function() { return 'FOO'; }, bar: 'BAR' }); equal(Subclass.foo(), 'FOO', 'Adds method'); equal(get(Subclass, 'bar'), 'BAR', 'Adds property'); }); test('class properties inherited by subclasses', function() { var Subclass = EmberObject.extend(); Subclass.reopenClass({ foo: function() { return 'FOO'; }, bar: 'BAR' }); var SubSub = Subclass.extend(); equal(SubSub.foo(), 'FOO', 'Adds method'); equal(get(SubSub, 'bar'), 'BAR', 'Adds property'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/reopen_test", ["ember-metal/property_get","ember-runtime/system/object"], function(__dependency1__, __dependency2__) { "use strict"; var get = __dependency1__.get; var EmberObject = __dependency2__["default"]; QUnit.module('system/core_object/reopen'); test('adds new properties to subclass instance', function() { var Subclass = EmberObject.extend(); Subclass.reopen({ foo: function() { return 'FOO'; }, bar: 'BAR' }); equal( new Subclass().foo(), 'FOO', 'Adds method'); equal(get(new Subclass(), 'bar'), 'BAR', 'Adds property'); }); test('reopened properties inherited by subclasses', function() { var Subclass = EmberObject.extend(); var SubSub = Subclass.extend(); Subclass.reopen({ foo: function() { return 'FOO'; }, bar: 'BAR' }); equal( new SubSub().foo(), 'FOO', 'Adds method'); equal(get(new SubSub(), 'bar'), 'BAR', 'Adds property'); }); // We plan to allow this in the future test('does not allow reopening already instantiated classes', function() { var Subclass = EmberObject.extend(); Subclass.create(); Subclass.reopen({ trololol: true }); equal(Subclass.create().get('trololol'), true, "reopen works"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/strict-mode-test", ["ember-metal/core","ember-runtime/system/object"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var EmberObject = __dependency2__["default"]; var originalLookup, lookup; QUnit.module('strict mode tests'); test('__superWrapper does not throw errors in strict mode', function() { var Foo = EmberObject.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'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/subclasses_test", ["ember-metal/run_loop","ember-metal/computed","ember-runtime/system/object"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var run = __dependency1__["default"]; var computed = __dependency2__.computed; var EmberObject = __dependency3__["default"]; QUnit.module('system/object/subclasses'); test('chains should copy forward to subclasses when prototype created', function () { var ObjectWithChains, objWithChains, SubWithChains, SubSub, subSub; run(function () { ObjectWithChains = EmberObject.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(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(function () { objWithChains.set('obj.hi', 'hello'); }); equal(subSub.get('greeting'), 'hello world'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object/toString_test", ["ember-metal/core","ember-metal/utils","ember-runtime/system/object","ember-runtime/system/namespace"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var guidFor = __dependency2__.guidFor; var GUID_KEY = __dependency2__.GUID_KEY; var EmberObject = __dependency3__["default"]; var Namespace = __dependency4__["default"]; var originalLookup, lookup; QUnit.module('system/object/toString', { setup: function() { originalLookup = Ember.lookup; lookup = Ember.lookup = {}; }, teardown: function() { Ember.lookup = originalLookup; } }); test("toString() returns the same value if called twice", function() { var Foo = Namespace.create(); Foo.toString = function() { return "Foo"; }; Foo.Bar = EmberObject.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"); }); test("toString on a class returns a useful value when nested in a namespace", function() { var obj; var Foo = Namespace.create(); Foo.toString = function() { return "Foo"; }; Foo.Bar = EmberObject.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(), ""); }); test("toString on a namespace finds the namespace in Ember.lookup", function() { var Foo = lookup.Foo = Namespace.create(); equal(Foo.toString(), "Foo"); }); test("toString on a namespace finds the namespace in Ember.lookup", function() { var Foo = lookup.Foo = Namespace.create(), obj; Foo.Bar = EmberObject.extend(); equal(Foo.Bar.toString(), "Foo.Bar"); obj = Foo.Bar.create(); equal(obj.toString(), ""); }); test('toString includes toStringExtension if defined', function() { var Foo = EmberObject.extend({ toStringExtension: function() { return "fooey"; } }), foo = Foo.create(), Bar = EmberObject.extend({}), bar = Bar.create(); // simulate these classes being defined on a Namespace Foo[GUID_KEY+'_name'] = 'Foo'; Bar[GUID_KEY+'_name'] = 'Bar'; equal(bar.toString(), '', 'does not include toStringExtension part'); equal(foo.toString(), '', 'Includes toStringExtension result'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/object_proxy_test", ["ember-metal/observer","ember-metal/computed","ember-metal/watching","ember-runtime/tests/props_helper","ember-runtime/system/object_proxy"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var addObserver = __dependency1__.addObserver; var removeObserver = __dependency1__.removeObserver; var computed = __dependency2__.computed; var isWatching = __dependency3__.isWatching; var testBoth = __dependency4__.testBoth; var ObjectProxy = __dependency5__["default"]; QUnit.module("ObjectProxy"); testBoth("should not proxy properties passed to create", function (get, set) { var Proxy = ObjectProxy.extend({ cp: 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'); }); testBoth("should proxy properties to content", function(get, set) { var content = { firstName: 'Tom', lastName: 'Dale', unknownProperty: function (key) { return key + ' unknown';} }, proxy = ObjectProxy.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'); }); testBoth("should work with watched properties", function(get, set) { var content1 = {firstName: 'Tom', lastName: 'Dale'}, content2 = {firstName: 'Yehuda', lastName: 'Katz'}, Proxy, proxy, count = 0, last; Proxy = ObjectProxy.extend({ fullName: computed(function () { var firstName = this.get('firstName'), lastName = this.get('lastName'); if (firstName && lastName) { return firstName + ' ' + lastName; } return firstName || lastName; }).property('firstName', 'lastName') }); proxy = Proxy.create(); 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(!isWatching(content1, 'firstName'), 'not watching firstName'); ok(!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'); }); test("set and get should work with paths", function () { var content = {foo: {bar: 'baz'}}, proxy = ObjectProxy.create({content: content}), 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'); }); testBoth("should transition between watched and unwatched strategies", function(get, set) { var content = {foo: 'foo'}, proxy = ObjectProxy.create({content: content}), 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'); 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'); 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'); }); testBoth('setting `undefined` to a proxied content property should override its existing value', function(get, set) { var proxyObject = ObjectProxy.create({ content: { prop: 'emberjs' } }); set(proxyObject, 'prop', undefined); equal(get(proxyObject, 'prop'), undefined, 'sets the `undefined` value to the proxied content'); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var CopyableTests = __dependency1__["default"]; var Set = __dependency2__["default"]; var generateGuid = __dependency3__.generateGuid; var get = __dependency4__.get; CopyableTests.extend({ name: 'Ember.Set Copyable', newObject: function() { var set = new Set(); set.addObject(generateGuid()); return set; }, isEqual: function(a,b) { if (!(a instanceof Set)) return false; if (!(b instanceof Set)) return false; return get(a, 'firstObject') === get(b, 'firstObject'); }, shouldBeFreezable: true }).run(); }); define("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.'); }); }); define("ember-runtime/tests/system/set/enumerable_suite_test", ["ember-runtime/tests/suites/mutable_enumerable","ember-runtime/system/set","ember-metal/property_get"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var MutableEnumerableTests = __dependency1__["default"]; var Set = __dependency2__["default"]; var get = __dependency3__.get; MutableEnumerableTests.extend({ name: 'Ember.Set', newObject: function(ary) { ary = ary ? ary.slice() : this.newFixture(3); var ret = new Set(); ret.addObjects(ary); return ret; }, mutate: function(obj) { obj.addObject(get(obj, 'length')+1); }, toArray: function(obj) { return obj.toArray ? obj.toArray() : obj.slice(); // make a copy. } }).run(); }); define("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.'); }); }); define("ember-runtime/tests/system/set/extra_test", ["ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-metal/observer","ember-runtime/system/set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var EnumerableUtils = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var addObserver = __dependency4__.addObserver; var Set = __dependency5__["default"]; QUnit.module('Set.init'); test('passing an array to new Set() should instantiate w/ items', function() { var ary = [1,2,3]; var aSet = new Set(ary); var count = 0; equal(get(aSet, 'length'), 3, 'should have three items'); aSet.forEach(function(x) { ok(EnumerableUtils.indexOf(ary, x)>=0, 'should find passed item in array'); count++; }); equal(count, 3, 'iterating should have returned three objects'); }); QUnit.module('Set.clear'); test('should clear a set of its content', function() { var aSet = new Set([1,2,3]); var count = 0; equal(get(aSet, 'length'), 3, 'should have three items'); ok(get(aSet, 'firstObject'), 'firstObject should return an object'); ok(get(aSet, 'lastObject'), 'lastObject should return an object'); addObserver(aSet, '[]', function() { count++; }); aSet.clear(); equal(get(aSet, 'length'), 0, 'should have 0 items'); equal(count, 1, 'should have notified of content change'); equal(get(aSet, 'firstObject'), null, 'firstObject should return nothing'); equal(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'); test('calling pop should return an object and remove it', function() { var aSet = new Set([1,2,3]); var count = 0, obj; while(count<10 && (obj = aSet.pop())) { equal(aSet.contains(obj), false, 'set should no longer contain object'); count++; equal(get(aSet, 'length'), 3-count, 'length should be shorter'); } equal(count, 3, 'should only pop 3 objects'); equal(get(aSet, 'length'), 0, 'final length should be zero'); equal(aSet.pop(), null, 'extra pops should do nothing'); }); // .......................................................... // Set.aliases // QUnit.module('Set aliases'); test('method aliases', function() { var aSet = new Set(); 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'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/camelize_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var camelize = __dependency2__.camelize; QUnit.module('EmberStringUtils.camelize'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.camelize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.camelize, 'String.prototype helper disabled'); }); } test("camelize normal string", function() { deepEqual(camelize('my favorite items'), 'myFavoriteItems'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('my favorite items'.camelize(), 'myFavoriteItems'); } }); test("camelize capitalized string", function() { deepEqual(camelize('I Love Ramen'), 'iLoveRamen'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('I Love Ramen'.camelize(), 'iLoveRamen'); } }); test("camelize dasherized string", function() { deepEqual(camelize('css-class-name'), 'cssClassName'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('css-class-name'.camelize(), 'cssClassName'); } }); test("camelize underscored string", function() { deepEqual(camelize('action_name'), 'actionName'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action_name'.camelize(), 'actionName'); } }); test("camelize dot notation string", function() { deepEqual(camelize('action.name'), 'actionName'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action.name'.camelize(), 'actionName'); } }); test("does nothing with camelcased string", function() { deepEqual(camelize('innerHTML'), 'innerHTML'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('innerHTML'.camelize(), 'innerHTML'); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/capitalize_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var capitalize = __dependency2__.capitalize; QUnit.module('EmberStringUtils.capitalize'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.capitalize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.capitalize, 'String.prototype helper disabled'); }); } test("capitalize normal string", function() { deepEqual(capitalize('my favorite items'), 'My favorite items'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('my favorite items'.capitalize(), 'My favorite items'); } }); test("capitalize dasherized string", function() { deepEqual(capitalize('css-class-name'), 'Css-class-name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('css-class-name'.capitalize(), 'Css-class-name'); } }); test("capitalize underscored string", function() { deepEqual(capitalize('action_name'), 'Action_name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action_name'.capitalize(), 'Action_name'); } }); test("capitalize camelcased string", function() { deepEqual(capitalize('innerHTML'), 'InnerHTML'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('innerHTML'.capitalize(), 'InnerHTML'); } }); test("does nothing with capitalized string", function() { deepEqual(capitalize('Capitalized string'), 'Capitalized string'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('Capitalized string'.capitalize(), 'Capitalized string'); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/classify_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var classify = __dependency2__.classify; QUnit.module('EmberStringUtils.classify'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.classify is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.classify, 'String.prototype helper disabled'); }); } test("classify normal string", function() { deepEqual(classify('my favorite items'), 'MyFavoriteItems'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('my favorite items'.classify(), 'MyFavoriteItems'); } }); test("classify dasherized string", function() { deepEqual(classify('css-class-name'), 'CssClassName'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('css-class-name'.classify(), 'CssClassName'); } }); test("classify underscored string", function() { deepEqual(classify('action_name'), 'ActionName'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action_name'.classify(), 'ActionName'); } }); test("does nothing with classified string", function() { deepEqual(classify('InnerHTML'), 'InnerHTML'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('InnerHTML'.classify(), 'InnerHTML'); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/dasherize_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var dasherize = __dependency2__.dasherize; QUnit.module('EmberStringUtils.dasherize'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.dasherize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.dasherize, 'String.prototype helper disabled'); }); } test("dasherize normal string", function() { deepEqual(dasherize('my favorite items'), 'my-favorite-items'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('my favorite items'.dasherize(), 'my-favorite-items'); } }); test("does nothing with dasherized string", function() { deepEqual(dasherize('css-class-name'), 'css-class-name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('css-class-name'.dasherize(), 'css-class-name'); } }); test("dasherize underscored string", function() { deepEqual(dasherize('action_name'), 'action-name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action_name'.dasherize(), 'action-name'); } }); test("dasherize camelcased string", function() { deepEqual(dasherize('innerHTML'), 'inner-html'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('innerHTML'.dasherize(), 'inner-html'); } }); test("dasherize string that is the property name of Object.prototype", function() { deepEqual(dasherize('toString'), 'to-string'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('toString'.dasherize(), 'to-string'); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/decamelize_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var decamelize = __dependency2__.decamelize; QUnit.module('EmberStringUtils.decamelize'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.decamelize is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.decamelize, 'String.prototype helper disabled'); }); } test("does nothing with normal string", function() { deepEqual(decamelize('my favorite items'), 'my favorite items'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('my favorite items'.decamelize(), 'my favorite items'); } }); test("does nothing with dasherized string", function() { deepEqual(decamelize('css-class-name'), 'css-class-name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('css-class-name'.decamelize(), 'css-class-name'); } }); test("does nothing with underscored string", function() { deepEqual(decamelize('action_name'), 'action_name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action_name'.decamelize(), 'action_name'); } }); test("converts a camelized string into all lower case separated by underscores.", function() { deepEqual(decamelize('innerHTML'), 'inner_html'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('innerHTML'.decamelize(), 'inner_html'); } }); test("decamelizes strings with numbers", function() { deepEqual(decamelize('size160Url'), 'size160_url'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('size160Url'.decamelize(), 'size160_url'); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/fmt_string_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var fmt = __dependency2__.fmt; QUnit.module('EmberStringUtils.fmt'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.fmt is not modified without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.fmt, 'String.prototype helper disabled'); }); } test("'Hello %@ %@'.fmt('John', 'Doe') => 'Hello John Doe'", function() { equal(fmt('Hello %@ %@', ['John', 'Doe']), 'Hello John Doe'); if (Ember.EXTEND_PROTOTYPES) { equal('Hello %@ %@'.fmt('John', 'Doe'), 'Hello John Doe'); } }); test("'Hello %@2 %@1'.fmt('John', 'Doe') => 'Hello Doe John'", function() { equal(fmt('Hello %@2 %@1', ['John', 'Doe']), 'Hello Doe John'); if (Ember.EXTEND_PROTOTYPES) { equal('Hello %@2 %@1'.fmt('John', 'Doe'), 'Hello Doe John'); } }); 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(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.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'); } }); test("'data: %@'.fmt({id: 3}) => 'data: {id: 3}'", function() { equal(fmt('data: %@', [{id: 3}]), 'data: {id: 3}'); if (Ember.EXTEND_PROTOTYPES) { equal('data: %@'.fmt({id: 3}), 'data: {id: 3}'); } }); test("works with argument form", function() { equal(fmt('%@', 'John'), 'John'); equal(fmt('%@ %@', ['John'], 'Doe'), '[John] Doe'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/loc_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var loc = __dependency2__.loc; var oldString; QUnit.module('EmberStringUtils.loc', { setup: function() { oldString = Ember.STRINGS; Ember.STRINGS = { '_Hello World': 'Bonjour le monde', '_Hello %@': 'Bonjour %@', '_Hello %@ %@': 'Bonjour %@ %@', '_Hello %@# %@#': 'Bonjour %@2 %@1' }; }, teardown: function() { Ember.STRINGS = oldString; } }); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.loc is not available without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.loc, 'String.prototype helper disabled'); }); } test("'_Hello World'.loc() => 'Bonjour le monde'", function() { equal(loc('_Hello World'), 'Bonjour le monde'); if (Ember.EXTEND_PROTOTYPES) { equal('_Hello World'.loc(), 'Bonjour le monde'); } }); test("'_Hello %@ %@'.loc('John', 'Doe') => 'Bonjour John Doe'", function() { equal(loc('_Hello %@ %@', ['John', 'Doe']), 'Bonjour John Doe'); if (Ember.EXTEND_PROTOTYPES) { equal('_Hello %@ %@'.loc('John', 'Doe'), 'Bonjour John Doe'); } }); test("'_Hello %@# %@#'.loc('John', 'Doe') => 'Bonjour Doe John'", function() { equal(loc('_Hello %@# %@#', ['John', 'Doe']), 'Bonjour Doe John'); if (Ember.EXTEND_PROTOTYPES) { equal('_Hello %@# %@#'.loc('John', 'Doe'), 'Bonjour Doe John'); } }); test("'_Not In Strings'.loc() => '_Not In Strings'", function() { equal(loc('_Not In Strings'), '_Not In Strings'); if (Ember.EXTEND_PROTOTYPES) { equal('_Not In Strings'.loc(), '_Not In Strings'); } }); test("works with argument form", function() { equal(loc('_Hello %@', 'John'), 'Bonjour John'); equal(loc('_Hello %@ %@', ['John'], 'Doe'), 'Bonjour [John] Doe'); }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/underscore_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var underscore = __dependency2__.underscore; QUnit.module('EmberStringUtils.underscore'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.underscore is not available without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.underscore, 'String.prototype helper disabled'); }); } test("with normal string", function() { deepEqual(underscore('my favorite items'), 'my_favorite_items'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('my favorite items'.underscore(), 'my_favorite_items'); } }); test("with dasherized string", function() { deepEqual(underscore('css-class-name'), 'css_class_name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('css-class-name'.underscore(), 'css_class_name'); } }); test("does nothing with underscored string", function() { deepEqual(underscore('action_name'), 'action_name'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('action_name'.underscore(), 'action_name'); } }); test("with camelcased string", function() { deepEqual(underscore('innerHTML'), 'inner_html'); if (Ember.EXTEND_PROTOTYPES) { deepEqual('innerHTML'.underscore(), 'inner_html'); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/string/w_test", ["ember-metal/core","ember-runtime/system/string"], function(__dependency1__, __dependency2__) { "use strict"; var Ember = __dependency1__["default"]; var w = __dependency2__.w; QUnit.module('EmberStringUtils.w'); if (!Ember.EXTEND_PROTOTYPES && !Ember.EXTEND_PROTOTYPES.String) { test("String.prototype.w is not available without EXTEND_PROTOTYPES", function() { ok("undefined" === typeof String.prototype.w, 'String.prototype helper disabled'); }); } test("'one two three'.w() => ['one','two','three']", function() { deepEqual(w('one two three'), ['one','two','three']); if (Ember.EXTEND_PROTOTYPES) { deepEqual('one two three'.w(), ['one','two','three']); } }); test("'one two three'.w() with extra spaces between words => ['one','two','three']", function() { deepEqual(w('one two three'), ['one','two','three']); if (Ember.EXTEND_PROTOTYPES) { deepEqual('one two three'.w(), ['one','two','three']); } }); test("'one two three'.w() with tabs", function() { deepEqual(w('one\ttwo three'), ['one','two','three']); if (Ember.EXTEND_PROTOTYPES) { deepEqual('one\ttwo three'.w(), ['one','two','three']); } }); }); define("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.'); }); }); define("ember-runtime/tests/system/subarray_test", ["ember-metal/enumerable_utils","ember-runtime/system/subarray"], function(__dependency1__, __dependency2__) { "use strict"; var forEach = __dependency1__.forEach; var SubArray = __dependency2__["default"]; var subarray; QUnit.module('SubArray', { setup: function () { subarray = new SubArray(); } }); function operationsString() { var str = ""; forEach(subarray._operations, function (operation) { str += " " + operation.type + ":" + operation.count; }); return str.substring(1); } test("Subarray operations are initially retain:n", function() { subarray = new SubArray(10); equal(operationsString(), "r:10", "subarray operations are initially retain n"); }); 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."); }); 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."); }); 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."); }); 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."); }); 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."); }); 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."); }); test("`addItem` returns the index of the item in the subarray", function() { var indexes = []; 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."); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); }); define("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.'); }); }); define("ember-runtime/tests/system/tracked_array_test", ["ember-metal/enumerable_utils","ember-runtime/system/tracked_array"], function(__dependency1__, __dependency2__) { "use strict"; var forEach = __dependency1__.forEach; var TrackedArray = __dependency2__["default"]; var trackedArray; var RETAIN = TrackedArray.RETAIN; var INSERT = TrackedArray.INSERT; var DELETE = TrackedArray.DELETE; QUnit.module('Ember.TrackedArray'); test("operations for a tracked array of length n are initially retain:n", function() { trackedArray = new TrackedArray([1,2,3,4]); equal("r:4", trackedArray.toString(), "initial mutation is retain n"); }); test("insert zero items is a no-op", function() { trackedArray = new TrackedArray([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"); }); test("inserts can split retains", function() { trackedArray = new TrackedArray([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"); }); test("inserts can expand (split/compose) inserts", function() { trackedArray = new TrackedArray([]); 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"); }); test("inserts left of inserts compose", function() { trackedArray = new TrackedArray([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"); }); test("inserts right of inserts compose", function() { trackedArray = new TrackedArray([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"); }); test("delete zero items is a no-op", function() { trackedArray = new TrackedArray([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"); }); test("deletes compose with several inserts and retains", function() { trackedArray = new TrackedArray([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"); }); test("deletes compose with several inserts and retains and an adjacent delete", function() { trackedArray = new TrackedArray([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"); }); test("deletes compose with several inserts and retains and can reduce the last one", function() { trackedArray = new TrackedArray([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"); }); test("deletes can split retains", function() { trackedArray = new TrackedArray([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"); }); test("deletes can trim retains on the right", function() { trackedArray = new TrackedArray([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"); }); test("deletes can trim retains on the left", function() { trackedArray = new TrackedArray([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"); }); test("deletes can split inserts", function() { trackedArray = new TrackedArray([]); 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"); }); test("deletes can trim inserts on the right", function() { trackedArray = new TrackedArray([]); 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"); }); test("deletes can trim inserts on the left", function() { trackedArray = new TrackedArray([]); 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"); }); test("deletes can trim inserts on the left while composing with a delete on the left", function() { trackedArray = new TrackedArray(['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"); }); 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([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"); }); test("removeItems returns the removed items", function() { trackedArray = new TrackedArray([1,2,3,4]); deepEqual(trackedArray.removeItems(1, 2), [2,3], "`removeItems` returns the removed items"); }); test("apply invokes the callback with each group of items and the mutation's calculated offset", function() { var i = 0; trackedArray = new TrackedArray([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"); }); }); define("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.'); }); }); define("ember-testing.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-testing.js should pass jshint', function() { ok(true, 'ember-testing.js should pass jshint.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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-handlebars","ember-routing"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; var run = __dependency1__["default"]; var jQuery = __dependency2__["default"]; var Test = __dependency3__["default"]; var QUnitAdapter = __dependency4__["default"]; var EmberView = __dependency5__["default"]; // ensure the initializer is setup var EmberApplication = __dependency7__["default"]; var EmberRoute = __dependency8__["default"]; var EmberHandlebars = __dependency9__["default"]; //ES6TODO: we need {{link-to}} and {{outlet}} to exist here //ES6TODO: fixme? var App, find, click, fillIn, currentRoute, visit, originalAdapter, andThen, indexHitCount; QUnit.module("ember-testing Acceptance", { setup: function() { jQuery('').appendTo('head'); jQuery('
    ').appendTo('body'); run(function() { indexHitCount = 0; App = EmberApplication.create({ rootElement: '#ember-testing' }); App.Router.map(function() { this.route('posts'); this.route('comments'); this.route('abort_transition'); }); App.IndexRoute = EmberRoute.extend({ model: function(){ indexHitCount += 1; } }); App.PostsRoute = EmberRoute.extend({ renderTemplate: function() { currentRoute = 'posts'; this._super(); } }); App.PostsView = EmberView.extend({ defaultTemplate: EmberHandlebars.compile("
    {{#link-to 'comments'}}Comments{{/link-to}}
    "), classNames: ['posts-view'] }); App.CommentsRoute = EmberRoute.extend({ renderTemplate: function() { currentRoute = 'comments'; this._super(); } }); App.CommentsView = EmberView.extend({ defaultTemplate: EmberHandlebars.compile("{{input type=text}}") }); App.AbortTransitionRoute = EmberRoute.extend({ beforeModel: function(transition) { transition.abort(); } }); App.setupForTesting(); }); App.injectTestHelpers(); find = window.find; click = window.click; fillIn = window.fillIn; visit = window.visit; andThen = window.andThen; originalAdapter = Test.adapter; }, teardown: function() { App.removeTestHelpers(); jQuery('#ember-testing-container, #ember-testing').remove(); run(App, App.destroy); App = null; Test.adapter = originalAdapter; indexHitCount = 0; } }); 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('.ember-text-field').val(), 'yeah', "chained with fillIn"); return fillIn('.ember-text-field', '#ember-testing-container', "context working"); }).then(function() { equal(jQuery('.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 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('.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"); }); }); 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'); }); }); 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'); }); }); 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'); }); }); test("Aborted transitions are not logged via Ember.Test.adapter#exception", function () { expect(0); Test.adapter = QUnitAdapter.create({ exception: function(error) { ok(false, "aborted transitions are not logged"); } }); visit("/abort_transition"); }); test("Unhandled exceptions are logged via Ember.Test.adapter#exception", function () { expect(2); var asyncHandled; Test.adapter = QUnitAdapter.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"); }); 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'); }); }); test("only enters the index route once when visiting /", function(){ visit('/'); andThen(function(){ equal(indexHitCount, 1, 'should hit index once when visiting /'); }); }); }); define("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.'); }); }); define("ember-testing/tests/adapters/adapter_test", ["ember-metal/core","ember-metal/run_loop","ember-testing/adapters/adapter"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.K var run = __dependency2__["default"]; var Adapter = __dependency3__["default"]; var adapter; QUnit.module("ember-testing Adapter", { setup: function() { adapter = new Adapter(); }, teardown: function() { run(adapter, adapter.destroy); } }); test("asyncStart is a noop", function() { equal(adapter.asyncStart, Ember.K); }); test("asyncEnd is a noop", function() { equal(adapter.asyncEnd, Ember.K); }); test("exception throws", function() { var error = "Hai", thrown; try { adapter.exception(error); } catch (e) { thrown = e; } equal(thrown, error); }); }); define("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.'); }); }); define("ember-testing/tests/adapters/qunit_test", ["ember-metal/core","ember-metal/run_loop","ember-testing/adapters/qunit"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.K var run = __dependency2__["default"]; var QUnitAdapter = __dependency3__["default"]; var adapter; QUnit.module("ember-testing QUnitAdapter", { setup: function() { adapter = new QUnitAdapter(); }, teardown: function() { run(adapter, adapter.destroy); } }); test("asyncStart calls stop", function() { var originalStop = QUnit.stop; try { QUnit.stop = function(){ ok(true, "stop called"); }; adapter.asyncStart(); } finally { QUnit.stop = originalStop; } }); test("asyncEnd calls start", function() { var originalStart = QUnit.start; try { QUnit.start = function(){ ok(true, "start called"); }; adapter.asyncEnd(); } finally { QUnit.start = originalStart; } }); test("exception causes a failing assertion", function() { var error = {err: 'hai'}, 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; } }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var run = __dependency1__["default"]; var Test = __dependency2__["default"]; var Adapter = __dependency3__["default"]; var QUnitAdapter = __dependency4__["default"]; var EmberApplication = __dependency5__["default"]; var App, originalAdapter; QUnit.module("ember-testing Adapters", { setup: function() { originalAdapter = Test.adapter; }, teardown: function() { run(App, App.destroy); App.removeTestHelpers(); App = null; Test.adapter = originalAdapter; } }); test("Setting a test adapter manually", function() { expect(1); var CustomAdapter; CustomAdapter = Adapter.extend({ asyncStart: function() { ok(true, "Correct adapter was used"); } }); run(function() { App = EmberApplication.create(); Test.adapter = CustomAdapter.create(); App.setupForTesting(); }); Test.adapter.asyncStart(); }); test("QUnitAdapter is used by default", function() { expect(1); Test.adapter = null; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); ok(Test.adapter instanceof QUnitAdapter); }); }); define("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.'); }); }); define("ember-testing/tests/helper_registration_test", ["ember-metal/run_loop","ember-testing/test","ember-application/system/application"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var run = __dependency1__["default"]; var Test = __dependency2__["default"]; var EmberApplication = __dependency3__["default"]; var App, appBooted, helperContainer; function registerHelper(){ Test.registerHelper('boot', function(app) { run(app, app.advanceReadiness); appBooted = true; return app.testHelpers.wait(); }); } function unregisterHelper(){ Test.unregisterHelper('boot'); } var originalAdapter = Test.adapter; function setupApp(){ appBooted = false; helperContainer = {}; run(function() { App = EmberApplication.create(); App.setupForTesting(); App.injectTestHelpers(helperContainer); }); } function destroyApp(){ if (App) { run(App, 'destroy'); App = null; } } QUnit.module("Test - registerHelper/unregisterHelper", { teardown: function(){ Test.adapter = originalAdapter; destroyApp(); } }); test("Helper gets registered", function() { expect(2); registerHelper(); setupApp(); ok(App.testHelpers.boot); ok(helperContainer.boot); }); test("Helper is ran when called", function(){ expect(1); registerHelper(); setupApp(); App.testHelpers.boot().then(function() { ok(appBooted); }); }); 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"); }); }); define("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.'); }); }); define("ember-testing/tests/helpers_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","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"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var EmberObject = __dependency5__["default"]; var RSVP = __dependency6__["default"]; var EmberView = __dependency7__["default"]; var jQuery = __dependency8__["default"]; var Test = __dependency9__["default"]; // ensure that the helpers are loaded // ensure the initializer is setup var setupForTesting = __dependency12__["default"]; var EmberRouter = __dependency13__["default"]; var EmberRoute = __dependency14__["default"]; var EmberApplication = __dependency15__["default"]; var App, originalAdapter = Test.adapter; function cleanup(){ // Teardown setupForTesting Test.adapter = originalAdapter; run(function(){ jQuery(document).off('ajaxSend'); jQuery(document).off('ajaxComplete'); }); Test.pendingAjaxRequests = null; // Other cleanup if (App) { run(App, App.destroy); App.removeTestHelpers(); App = null; } Ember.TEMPLATES = {}; } function assertHelpers(application, helperContainer, expected){ if (!helperContainer) { helperContainer = window; } if (expected === undefined) { expected = true; } function checkHelperPresent(helper, expected){ var presentInHelperContainer = !!helperContainer[helper], 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); } function assertNoHelpers(application, helperContainer) { assertHelpers(application, helperContainer, false); } function currentRouteName(app){ var appController = app.__container__.lookup('controller:application'); return get(appController, 'currentRouteName'); } function currentPath(app){ var appController = app.__container__.lookup('controller:application'); return get(appController, 'currentPath'); } function currentURL(app){ var router = app.__container__.lookup('router:main'); return get(router, 'location').getURL(); } QUnit.module("ember-testing Helpers", { setup: function(){ cleanup(); }, teardown: function() { cleanup(); } }); test("Ember.Application#injectTestHelpers/#removeTestHelpers", function() { App = run(EmberApplication, EmberApplication.create); assertNoHelpers(App); App.injectTestHelpers(); assertHelpers(App); App.removeTestHelpers(); assertNoHelpers(App); }); test("Ember.Application#setupForTesting", function() { run(function() { App = EmberApplication.create(); App.setupForTesting(); }); equal(App.__container__.lookup('router:main').location.implementation, 'none'); }); test("Ember.Application.setupForTesting sets the application to `testing`.", function(){ run(function() { App = EmberApplication.create(); App.setupForTesting(); }); equal(App.testing, true, "Application instance is set to testing."); }); test("Ember.Application.setupForTesting leaves the system in a deferred state.", function(){ run(function() { App = EmberApplication.create(); App.setupForTesting(); }); equal(App._readinessDeferrals, 1, "App is in deferred state after setupForTesting."); }); test("App.reset() after Application.setupForTesting leaves the system in a deferred state.", function(){ run(function() { App = EmberApplication.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."); }); test("`visit` advances readiness.", function(){ expect(2); run(function() { App = EmberApplication.create(); App.setupForTesting(); App.injectTestHelpers(); }); 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."); }); }); test("`wait` helper can be passed a resolution value", function() { expect(4); var promise, wait; promise = new RSVP.Promise(function(resolve) { run(null, resolve, 'promise'); }); run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.injectTestHelpers(); run(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'); }); }); test("`click` triggers appropriate events in order", function() { expect(4); var click, wait, events; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.IndexView = EmberView.extend({ classNames: 'index-view', didInsertElement: function() { this.$().on('mousedown focusin mouseup click', function(e) { events.push(e.type); }); }, Checkbox: Ember.Checkbox.extend({ click: function() { events.push('click:' + this.get('checked')); }, change: function() { events.push('change:' + this.get('checked')); } }) }); Ember.TEMPLATES.index = Ember.Handlebars.compile('{{input type="text"}} {{view view.Checkbox}} {{textarea}}'); App.injectTestHelpers(); run(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() { // In IE (< 8), the change event only fires when the value changes before element focused. jQuery('.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'); }); }); test("Ember.Application#setupForTesting attaches ajax listeners", function() { var documentEvents; documentEvents = jQuery._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(function() { setupForTesting(); }); documentEvents = jQuery._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'); }); test("Ember.Application#setupForTesting attaches ajax listeners only once", function() { var documentEvents; documentEvents = jQuery._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(function() { setupForTesting(); }); run(function() { setupForTesting(); }); documentEvents = jQuery._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'); }); test("Ember.Application#injectTestHelpers calls callbacks registered with onInjectHelpers", function(){ var injected = 0; Test.onInjectHelpers(function(){ injected++; }); run(function() { App = EmberApplication.create(); App.setupForTesting(); }); equal(injected, 0, 'onInjectHelpers are not called before injectTestHelpers'); App.injectTestHelpers(); equal(injected, 1, 'onInjectHelpers are called after injectTestHelpers'); }); test("Ember.Application#injectTestHelpers adds helpers to provided object.", function(){ var helpers = {}; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.injectTestHelpers(helpers); assertHelpers(App, helpers); App.removeTestHelpers(); assertNoHelpers(App, helpers); }); test("Ember.Application#removeTestHelpers resets the helperContainer's original values", function(){ var helpers = {visit: 'snazzleflabber'}; run(function() { App = EmberApplication.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"); }); test("`wait` respects registerWaiters", function() { expect(2); var counter=0; function waiter() { return ++counter > 2; } run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.injectTestHelpers(); run(App, App.advanceReadiness); Test.registerWaiter(waiter); App.testHelpers.wait().then(function() { equal(waiter(), true, 'should not resolve until our waiter is ready'); Test.unregisterWaiter(waiter); equal(Test.waiters.length, 0, 'should not leave a waiter registered'); }); }); test("`wait` waits for outstanding timers", function() { expect(1); var wait_done = false; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.injectTestHelpers(); run(App, App.advanceReadiness); run.later(this, function() { wait_done = true; }, 500); App.testHelpers.wait().then(function() { equal(wait_done, true, 'should wait for the timer to be fired.'); }); }); test("`wait` respects registerWaiters with optional context", function() { expect(2); var obj = { counter: 0, ready: function() { return ++this.counter > 2; } }; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.injectTestHelpers(); run(App, App.advanceReadiness); Test.registerWaiter(obj, obj.ready); App.testHelpers.wait().then(function() { equal(obj.ready(), true, 'should not resolve until our waiter is ready'); Test.unregisterWaiter(obj, obj.ready); equal(Test.waiters.length, 0, 'should not leave a waiter registered'); }); }); QUnit.module("ember-testing routing helpers", { setup: function(){ cleanup(); run(function() { App = EmberApplication.create(); App.Router = EmberRouter.extend({ location: 'none' }); App.Router.map(function() { this.resource("posts", function() { this.route("new"); }); }); App.setupForTesting(); }); App.injectTestHelpers(); run(App, 'advanceReadiness'); }, teardown: function(){ cleanup(); } }); 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 '/'."); }); }); 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'."); }); }); 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(){ cleanup(); run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.injectTestHelpers(); }, teardown: function() { cleanup(); } }); test("pendingAjaxRequests is maintained for ajaxSend and ajaxComplete events", function() { equal(Test.pendingAjaxRequests, 0); var xhr = {some: 'xhr'}; jQuery(document).trigger('ajaxSend', xhr); equal(Test.pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests was incremented'); jQuery(document).trigger('ajaxComplete', xhr); equal(Test.pendingAjaxRequests, 0, 'Ember.Test.pendingAjaxRequests was decremented'); }); test("pendingAjaxRequests is ignores ajaxComplete events from past setupForTesting calls", function() { equal(Test.pendingAjaxRequests, 0); var xhr = {some: 'xhr'}; jQuery(document).trigger('ajaxSend', xhr); equal(Test.pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests was incremented'); Ember.run(function(){ setupForTesting(); }); equal(Test.pendingAjaxRequests, 0, 'Ember.Test.pendingAjaxRequests was reset'); var altXhr = {some: 'more xhr'}; jQuery(document).trigger('ajaxSend', altXhr); equal(Test.pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests was incremented'); jQuery(document).trigger('ajaxComplete', xhr); equal(Test.pendingAjaxRequests, 1, 'Ember.Test.pendingAjaxRequests is not impressed with your unexpected complete'); }); test("pendingAjaxRequests is reset by setupForTesting", function() { Test.pendingAjaxRequests = 1; Ember.run(function(){ setupForTesting(); }); equal(Test.pendingAjaxRequests, 0, 'pendingAjaxRequests is reset'); }); test("`triggerEvent accepts an optional options hash and context", function(){ expect(3); var triggerEvent, wait, event; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.IndexView = EmberView.extend({ template: Ember.Handlebars.compile('{{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; }); } }); App.injectTestHelpers(); run(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'); }); }); test("`triggerEvent accepts an optional options hash without context", function(){ expect(3); var triggerEvent, wait, event; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.IndexView = EmberView.extend({ template: Ember.Handlebars.compile('{{input type="text" id="scope" class="input"}}'), didInsertElement: function() { this.$('.input').on('blur change', function(e) { event = e; }); } }); App.injectTestHelpers(); run(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'); }); }); test("`triggerEvent can limit searching for a selector to a scope", function(){ expect(2); var triggerEvent, wait, event; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.IndexView = EmberView.extend({ template: Ember.Handlebars.compile('{{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; }); } }); App.injectTestHelpers(); run(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'); }); }); test("`triggerEvent` can be used to trigger arbitrary events", function() { expect(2); var triggerEvent, wait, event; run(function() { App = EmberApplication.create(); App.setupForTesting(); }); App.IndexView = EmberView.extend({ template: Ember.Handlebars.compile('{{input type="text" id="foo"}}'), didInsertElement: function() { this.$('#foo').on('blur change', function(e) { event = e; }); } }); App.injectTestHelpers(); run(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.module("ember-testing async router", { setup: function(){ cleanup(); run(function() { App = EmberApplication.create(); App.Router = EmberRouter.extend({ location: 'none' }); App.Router.map(function() { this.resource("user", function() { this.route("profile"); this.route("edit"); }); }); App.UserRoute = EmberRoute.extend({ model: function() { return resolveLater(); } }); App.UserProfileRoute = EmberRoute.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(function() { promise = new RSVP.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(function() { resolve(EmberObject.create({firstName: 'Tom'})); }); }, 20); }); }); return promise; } App.setupForTesting(); }); App.injectTestHelpers(); run(App, 'advanceReadiness'); }, teardown: function(){ cleanup(); } }); 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'."); }); }); 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._helpers.visit; originalFindHelper = Test._helpers.find; originalWaitHelper = Test._helpers.wait; jQuery('').appendTo('head'); jQuery('
    ').appendTo('body'); run(function() { App = Ember.Application.create({ rootElement: '#ember-testing' }); App.setupForTesting(); }); }, teardown: function(){ App.removeTestHelpers(); jQuery('#ember-testing-container, #ember-testing').remove(); run(App, App.destroy); App = null; Test._helpers.visit = originalVisitHelper; Test._helpers.find = originalFindHelper; Test._helpers.wait = originalWaitHelper; } }); test("can override visit helper", function(){ expect(1); Test.registerHelper('visit', function(){ ok(true, 'custom visit helper was called'); }); App.injectTestHelpers(); App.testHelpers.visit(); }); test("can override find helper", function(){ expect(1); Test.registerHelper('find', function(){ ok(true, 'custom find helper was called'); return ['not empty array']; }); App.injectTestHelpers(); App.testHelpers.findWithAssert('.who-cares'); }); }); define("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.'); }); }); define("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-handlebars","ember-application"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var ArrayController = __dependency4__["default"]; var jQuery = __dependency5__["default"]; var EmberView = __dependency6__["default"]; var Test = __dependency7__["default"]; var EmberRoute = __dependency8__["default"]; var EmberApplication = __dependency9__["default"]; var EmberHandlebars = __dependency10__["default"]; var App, find, visit, originalAdapter = Test.adapter; QUnit.module("ember-testing Integration", { setup: function() { jQuery('
    ').appendTo('body'); run(function() { App = EmberApplication.create({ rootElement: '#ember-testing' }); App.Router.map(function() { this.resource("people", { path: "/" }); }); App.PeopleRoute = EmberRoute.extend({ model: function() { return App.Person.find(); } }); App.PeopleView = EmberView.extend({ defaultTemplate: EmberHandlebars.compile("{{#each person in controller}}
    {{person.firstName}}
    {{/each}}") }); App.PeopleController = ArrayController.extend({}); App.Person = EmberObject.extend({ firstName: '' }); App.Person.reopenClass({ find: function() { return Ember.A(); } }); App.ApplicationView = EmberView.extend({ defaultTemplate: EmberHandlebars.compile("{{outlet}}") }); App.setupForTesting(); }); run(function() { App.reset(); }); App.injectTestHelpers(); find = window.find; visit = window.visit; }, teardown: function() { App.removeTestHelpers(); jQuery('#ember-testing-container, #ember-testing').remove(); run(App, App.destroy); App = null; Test.adapter = originalAdapter; } }); test("template is bound to empty array of people", function() { App.Person.find = function() { return Ember.A(); }; run(App, 'advanceReadiness'); visit("/").then(function() { var rows = find(".name").length; equal(rows, 0, "successfully stubbed an empty array of people"); }); }); test("template is bound to array of 2 people", function() { App.Person.find = function() { var people = Ember.A(); var first = App.Person.create({firstName: "x"}); var last = App.Person.create({firstName: "y"}); run(people, people.pushObject, first); run(people, people.pushObject, last); return people; }; run(App, 'advanceReadiness'); visit("/").then(function() { var rows = find(".name").length; equal(rows, 2, "successfully stubbed a non empty array of people"); }); }); test("template is again bound to empty array of people", function() { App.Person.find = function() { return Ember.A(); }; run(App, 'advanceReadiness'); visit("/").then(function() { var rows = find(".name").length; equal(rows, 0, "successfully stubbed another empty array of people"); }); }); test("`visit` can be called without advancedReadiness.", function(){ App.Person.find = function() { return Ember.A(); }; visit("/").then(function() { var rows = find(".name").length; equal(rows, 0, "stubbed an empty array of people without calling advancedReadiness."); }); }); }); define("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.'); }); }); define("ember-testing/tests/simple_setup", ["ember-metal/core","ember-metal/run_loop","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var jQuery = __dependency3__["default"]; var App; QUnit.module('Simple Testing Setup', { teardown: function() { if (App) { App.removeTestHelpers(); jQuery('#ember-testing-container, #ember-testing').remove(); run(App, 'destroy'); App = null; } } }); }); define("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.'); }); }); define("ember-views.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-views.js should pass jshint', function() { ok(true, 'ember-views.js should pass jshint.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-views/tests/mixins/view_target_action_support_test", ["ember-metal/core","ember-runtime/system/object","ember-views/views/view","ember-views/mixins/view_target_action_support"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var EmberObject = __dependency2__["default"]; var View = __dependency3__["default"]; var ViewTargetActionSupport = __dependency4__["default"]; QUnit.module("ViewTargetActionSupport"); test("it should return false if no action is specified", function() { expect(1); var view = View.createWithMixins(ViewTargetActionSupport, { controller: EmberObject.create() }); ok(false === view.triggerAction(), "a valid target and action were specified"); }); test("it should support actions specified as strings", function() { expect(2); var view = View.createWithMixins(ViewTargetActionSupport, { controller: EmberObject.create({ anEvent: function() { ok(true, "anEvent method was called"); } }), action: 'anEvent' }); ok(true === view.triggerAction(), "a valid target and action were specified"); }); test("it should invoke the send() method on the controller with the view's context", function() { expect(3); var view = View.createWithMixins(ViewTargetActionSupport, { context: {}, controller: EmberObject.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"); }); }); define("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.'); }); }); define("ember-views/tests/system/event_dispatcher_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__) { "use strict"; var Ember = __dependency1__["default"]; // A, FEATURES, assert, TESTING_DEPRECATION var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var EmberObject = __dependency5__["default"]; var jQuery = __dependency6__["default"]; var View = __dependency7__["default"]; var EventDispatcher = __dependency8__["default"]; var ContainerView = __dependency9__["default"]; var view; var dispatcher; QUnit.module("EventDispatcher", { setup: function() { run(function() { dispatcher = EventDispatcher.create(); dispatcher.setup(); }); }, teardown: function() { run(function() { if (view) { view.destroy(); } dispatcher.destroy(); }); } }); test("should dispatch events to views", function() { var receivedEvent; var parentMouseDownCalled = 0; var childKeyDownCalled = 0; var parentKeyDownCalled = 0; view = ContainerView.createWithMixins({ childViews: ['child'], child: View.extend({ render: function(buffer) { buffer.push('ewot'); }, keyDown: function(evt) { childKeyDownCalled++; return false; } }), render: function(buffer) { buffer.push('some awesome content'); this._super(buffer); }, mouseDown: function(evt) { parentMouseDownCalled++; receivedEvent = evt; }, keyDown: function(evt) { parentKeyDownCalled++; } }); run(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('#wot').trigger('mousedown'); ok(receivedEvent, "event bubbles up to nearest View"); jQuery('#wot').trigger('keydown'); equal(childKeyDownCalled, 1, "calls keyDown on child view"); equal(parentKeyDownCalled, 0, "does not call keyDown on parent if child handles event"); }); test("should not dispatch events to views not inDOM", function() { var receivedEvent; view = View.createWithMixins({ render: function(buffer) { buffer.push('some awesome content'); this._super(buffer); }, mouseDown: function(evt) { receivedEvent = evt; } }); run(function() { view.append(); }); var $element = view.$(); run(function() { view.set('element', null); // Force into preRender }); $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(); }); test("should send change events up view hierarchy if view contains form elements", function() { var receivedEvent; view = View.create({ render: function(buffer) { buffer.push(''); }, change: function(evt) { receivedEvent = evt; } }); run(function() { view.append(); }); jQuery('#is-done').trigger('change'); ok(receivedEvent, "calls change method when a child element is changed"); equal(receivedEvent.target, jQuery('#is-done')[0], "target property is the element that was clicked"); }); test("events should stop propagating if the view is destroyed", function() { var parentViewReceived, receivedEvent; var parentView = ContainerView.create({ change: function(evt) { parentViewReceived = true; } }); view = parentView.createChildView(View, { render: function(buffer) { buffer.push(''); }, change: function(evt) { receivedEvent = true; var self = this; run(function() { get(self, 'parentView').destroy(); }); } }); parentView.pushObject(view); run(function() { parentView.append(); }); ok(jQuery('#is-done').length, "precond - view is in the DOM"); jQuery('#is-done').trigger('change'); ok(!jQuery('#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"); }); test('should not interfere with event propagation of virtualViews', function() { var receivedEvent; var view = View.create({ isVirtual: true, render: function(buffer) { buffer.push('
    '); } }); run(function() { view.append(); }); jQuery(window).bind('click', function(evt) { receivedEvent = evt; }); jQuery('#propagate-test-div').click(); ok(receivedEvent, 'allowed event to propagate'); deepEqual(receivedEvent && receivedEvent.target, jQuery('#propagate-test-div')[0], 'target property is the element that was clicked'); }); test("should dispatch events to nearest event manager", function() { var receivedEvent=0; view = ContainerView.create({ render: function(buffer) { buffer.push(''); }, eventManager: EmberObject.create({ mouseDown: function() { receivedEvent++; } }), mouseDown: function() {} }); run(function() { view.append(); }); jQuery('#is-done').trigger('mousedown'); equal(receivedEvent, 1, "event should go to manager and not view"); }); test("event manager should be able to re-dispatch events to view", function() { var receivedEvent=0; view = ContainerView.createWithMixins({ elementId: 'containerView', eventManager: EmberObject.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.extend({ elementId: 'nestedView', mouseDown: function(evt) { receivedEvent++; } }), mouseDown: function(evt) { receivedEvent++; } }); run(function() { view.append(); }); jQuery('#nestedView').trigger('mousedown'); equal(receivedEvent, 2, "event should go to manager and not view"); }); test("event handlers should be wrapped in a run loop", function() { expect(1); view = View.createWithMixins({ elementId: 'test-view', eventManager: EmberObject.create({ mouseDown: function() { ok(run.currentRunLoop, 'a run loop should have started'); } }) }); run(function() { view.append(); }); jQuery('#test-view').trigger('mousedown'); }); QUnit.module("EventDispatcher#setup", { setup: function() { run(function() { dispatcher = EventDispatcher.create({ rootElement: "#qunit-fixture" }); }); }, teardown: function() { run(function() { if (view) { view.destroy(); } dispatcher.destroy(); }); } }); test("additional events which should be listened on can be passed", function () { expect(1); run(function () { dispatcher.setup({ myevent: "myEvent" }); view = View.create({ elementId: "leView", myEvent: function() { ok(true, "custom event has been triggered"); } }).appendTo( dispatcher.get("rootElement") ); }); jQuery("#leView").trigger("myevent"); }); test("additional events and rootElement can be specified", function () { expect(3); jQuery("#qunit-fixture").append("
    "); run(function () { dispatcher.setup({ myevent: "myEvent" }, ".custom-root"); view = View.create({ elementId: "leView", myEvent: function() { ok(true, "custom event has been triggered"); } }).appendTo( dispatcher.get("rootElement") ); }); ok(jQuery(".custom-root").hasClass("ember-application"), "the custom rootElement is used"); equal(dispatcher.get("rootElement"), ".custom-root", "the rootElement is updated"); jQuery("#leView").trigger("myevent"); }); }); define("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.'); }); }); define("ember-views/tests/system/ext_test", ["ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__) { "use strict"; var run = __dependency1__["default"]; var View = __dependency2__["default"]; QUnit.module("Ember.View additions to run queue"); test("View hierarchy is done rendering to DOM when functions queued in afterRender execute", function() { var lookup1, lookup2; var childView = View.create({ elementId: 'child_view', render: function(buffer) { buffer.push('child'); }, didInsertElement: function() { this.$().addClass('extra-class'); } }); var parentView = View.create({ elementId: 'parent_view', render: function(buffer) { buffer.push('parent'); this.appendChild(childView); }, didInsertElement: function() { lookup1 = this.$('.extra-class'); run.scheduleOnce('afterRender', this, function() { lookup2 = this.$('.extra-class'); }); } }); run(function() { parentView.appendTo('#qunit-fixture'); }); equal(lookup1.length, 0, "doesn't not find child in DOM on didInsertElement"); equal(lookup2.length, 1, "finds child in DOM afterRender"); run(function() { parentView.destroy(); }); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var run = __dependency1__["default"]; var EventDispatcher = __dependency2__["default"]; var jQuery = __dependency3__["default"]; var View = __dependency4__["default"]; 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(function() { dispatcher = EventDispatcher.create(); dispatcher.setup(); }); }, teardown: function() { run(function() { if (view) { view.destroy(); } dispatcher.destroy(); }); } }); if (canDataTransfer) { test("jQuery.event.fix copies over the dataTransfer property", function() { var originalEvent; var receivedEvent; originalEvent = { type: 'drop', dataTransfer: 'success', target: document.body }; receivedEvent = jQuery.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"); }); test("drop handler should receive event with dataTransfer property", function() { var receivedEvent; var dropCalled = 0; view = View.createWithMixins({ render: function(buffer) { buffer.push('please drop stuff on me'); this._super(buffer); }, drop: function(evt) { receivedEvent = evt; dropCalled++; } }); run(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"); }); } }); define("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.'); }); }); define("ember-views/tests/system/render_buffer_test", ["ember-metal/property_set","ember-metal/property_get","ember-views/system/jquery","ember-views/system/render_buffer"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var set = __dependency1__.set; var get = __dependency2__.get; var jQuery = __dependency3__["default"]; var RenderBuffer = __dependency4__["default"]; var trim = jQuery.trim; // ....................................................... // render() // QUnit.module("RenderBuffer"); test("RenderBuffers combine strings", function() { var buffer = new RenderBuffer('div'); buffer.pushOpeningTag(); buffer.push('a'); buffer.push('b'); // IE8 returns `element name as upper case with extra whitespace. equal("
    ab
    ", trim(buffer.string().toLowerCase()), "Multiple pushes should concatenate"); }); test("value of 0 is included in output", function() { var buffer, $el; buffer = new RenderBuffer('input'); buffer.prop('value', 0); buffer.pushOpeningTag(); $el = buffer.element(); strictEqual($el.value, '0', "generated element has value of '0'"); buffer = new RenderBuffer('input'); buffer.prop('value', 0); buffer.push('
    '); buffer.pushOpeningTag(); buffer.push('
    '); $el = jQuery(buffer.innerString()); strictEqual($el.find('input').val(), '0', "raw tag has value of '0'"); }); test("prevents XSS injection via `id`", function() { var buffer = new RenderBuffer('div'); buffer.push(''); // We need the buffer to not be empty so we use the string path buffer.id('hacked" megahax="yes'); buffer.pushOpeningTag(); equal('
    ', buffer.string()); }); test("prevents XSS injection via `attr`", function() { var buffer = new RenderBuffer('div'); buffer.push(''); // We need the buffer to not be empty so we use the string path buffer.attr('id', 'trololol" onmouseover="pwn()'); buffer.attr('class', "hax>
    ', buffer.string()); }); test("prevents XSS injection via `addClass`", function() { var buffer = new RenderBuffer('div'); buffer.push(''); // We need the buffer to not be empty so we use the string path buffer.addClass('megahax" xss="true'); buffer.pushOpeningTag(); // Regular check then check for IE equal('
    ', buffer.string()); }); test("prevents XSS injection via `style`", function() { var buffer = new RenderBuffer('div'); buffer.push(''); // We need the buffer to not be empty so we use the string path buffer.style('color', 'blue;" xss="true" style="color:red'); buffer.pushOpeningTag(); equal('
    ', buffer.string()); }); test("prevents XSS injection via `tagName`", function() { var buffer = new RenderBuffer('cool-div>
    '); // We need the buffer to not be empty so we use the string path buffer.pushOpeningTag(); buffer.begin('span>', buffer.string()); }); test("handles null props - Issue #2019", function() { var buffer = new RenderBuffer('div'); buffer.push(''); // We need the buffer to not be empty so we use the string path buffer.prop('value', null); buffer.pushOpeningTag(); equal('
    ', buffer.string()); }); test("handles browsers like Firefox < 11 that don't support outerHTML Issue #1952", function() { var buffer = new RenderBuffer('div'); buffer.pushOpeningTag(); // Make sure element.outerHTML is falsy to trigger the fallback. var elementStub = '
    '; buffer.element = function() { return elementStub; }; // IE8 returns `element name as upper case with extra whitespace. equal(elementStub, trim(buffer.string().toLowerCase())); }); test("resets classes after pushing the opening tag", function() { var buffer = new RenderBuffer('div'); // IE8 renders single class without quote. To pass this test any environments, add class twice. buffer.addClass('foo1'); buffer.addClass('foo2'); buffer.pushOpeningTag(); buffer.begin('div'); buffer.addClass('bar1'); buffer.addClass('bar2'); buffer.pushOpeningTag(); buffer.pushClosingTag(); buffer.pushClosingTag(); equal(trim(buffer.string()).toLowerCase().replace(/\r\n/g, ''), '
    '); }); test("lets `setClasses` and `addClass` work together", function() { var buffer = new RenderBuffer('div'); buffer.setClasses(['foo', 'bar']); buffer.addClass('baz'); buffer.pushOpeningTag(); buffer.pushClosingTag(); equal(trim(buffer.string().toLowerCase()), '
    '); }); QUnit.module("RenderBuffer - without tagName"); test("It is possible to create a RenderBuffer without a tagName", function() { var buffer = new RenderBuffer(); buffer.push('a'); buffer.push('b'); buffer.push('c'); equal(buffer.string(), "abc", "Buffers without tagNames do not wrap the content in a tag"); }); QUnit.module("RenderBuffer#element"); test("properly handles old IE's zero-scope bug", function() { var buffer = new RenderBuffer('div'); buffer.pushOpeningTag(); buffer.push('foo'); var element = buffer.element(); ok(jQuery(element).html().match(/script/i), "should have script tag"); ok(!jQuery(element).html().match(/­/), "should not have ­"); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.A var set = __dependency2__.set; var get = __dependency3__.get; var run = __dependency4__["default"]; var forEach = __dependency5__.forEach; var Mixin = __dependency6__.Mixin; var fmt = __dependency7__.fmt; var ArrayProxy = __dependency8__["default"]; var ArrayController = __dependency9__["default"]; var jQuery = __dependency10__["default"]; var CollectionView = __dependency11__["default"]; var View = __dependency12__["default"]; var trim = jQuery.trim; var view; var originalLookup; QUnit.module("CollectionView", { setup: function() { CollectionView.CONTAINER_MAP.del = 'em'; originalLookup = Ember.lookup; }, teardown: function() { delete CollectionView.CONTAINER_MAP.del; run(function() { if (view) { view.destroy(); } }); Ember.lookup = originalLookup; } }); test("should render a view for each item in its content array", function() { view = CollectionView.create({ content: Ember.A([1, 2, 3, 4]) }); run(function() { view.append(); }); equal(view.$('div').length, 4); }); test("should render the emptyView if content array is empty (view class)", function() { view = CollectionView.create({ tagName: 'del', content: Ember.A(), emptyView: View.extend({ tagName: 'kbd', render: function(buf) { buf.push("OY SORRY GUVNAH NO NEWS TODAY EH"); } }) }); run(function() { view.append(); }); ok(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, "displays empty view"); }); test("should render the emptyView if content array is empty (view instance)", function() { view = CollectionView.create({ tagName: 'del', content: Ember.A(), emptyView: View.create({ tagName: 'kbd', render: function(buf) { buf.push("OY SORRY GUVNAH NO NEWS TODAY EH"); } }) }); run(function() { view.append(); }); ok(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, "displays empty view"); }); test("should be able to override the tag name of itemViewClass even if tag is in default mapping", function() { view = CollectionView.create({ tagName: 'del', content: Ember.A(['NEWS GUVNAH']), itemViewClass: View.extend({ tagName: 'kbd', render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); ok(view.$().find('kbd:contains("NEWS GUVNAH")').length, "displays the item view with proper tag name"); }); test("should allow custom item views by setting itemViewClass", function() { var passedContents = []; view = CollectionView.create({ content: Ember.A(['foo', 'bar', 'baz']), itemViewClass: View.extend({ render: function(buf) { passedContents.push(get(this, 'content')); buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); deepEqual(passedContents, ['foo', 'bar', 'baz'], "sets the content property on each item view"); forEach(passedContents, function(item) { equal(view.$(':contains("'+item+'")').length, 1); }); }); test("should insert a new item in DOM when an item is added to the content array", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.insertAt(1, 'quux'); }); equal(jQuery.trim(view.$(':nth-child(2)').text()), 'quux'); }); test("should remove an item from DOM when an item is removed from the content array", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.removeAt(1); }); forEach(content, function(item, idx) { equal(view.$(fmt(':nth-child(%@)', [String(idx+1)])).text(), item); }); }); test("it updates the view if an item is replaced", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.removeAt(1); content.insertAt(1, "Kazuki" ); }); forEach(content, function(item, idx) { equal(jQuery.trim(view.$(fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); test("can add and replace in the same runloop", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.pushObject("Tom Dale" ); content.removeAt(0); content.insertAt(0, "Kazuki" ); }); forEach(content, function(item, idx) { equal(jQuery.trim(view.$(fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); test("can add and replace the object before the add in the same runloop", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.pushObject("Tom Dale" ); content.removeAt(1); content.insertAt(1, "Kazuki" ); }); forEach(content, function(item, idx) { equal(jQuery.trim(view.$(fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); test("can add and replace complicatedly", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.pushObject("Tom Dale" ); content.removeAt(1); content.insertAt(1, "Kazuki" ); content.pushObject("Firestone" ); content.pushObject("McMunch" ); }); forEach(content, function(item, idx) { equal(jQuery.trim(view.$(fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update: "+item.name+"!="+view.$(fmt(':nth-child(%@)', [String(idx+1)])).text()); }); }); test("can add and replace complicatedly harder", function() { var content = Ember.A(['foo', 'bar', 'baz']); view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }) }); run(function() { view.append(); }); forEach(content, function(item) { equal(view.$(':contains("'+item+'")').length, 1, "precond - generates pre-existing items"); }); run(function() { content.pushObject("Tom Dale" ); content.removeAt(1); content.insertAt(1, "Kazuki" ); content.pushObject("Firestone" ); content.pushObject("McMunch" ); content.removeAt(2); }); forEach(content, function(item, idx) { equal(jQuery.trim(view.$(fmt(':nth-child(%@)', [String(idx+1)])).text()), item, "postcond - correct array update"); }); }); test("should allow changes to content object before layer is created", function() { view = CollectionView.create({ content: null }); run(function() { set(view, 'content', Ember.A()); set(view, 'content', Ember.A([1, 2, 3])); set(view, 'content', Ember.A([1, 2])); view.append(); }); ok(view.$().children().length); }); test("should fire life cycle events when elements are added and removed", function() { var view, didInsertElement = 0, willDestroyElement = 0, willDestroy = 0, destroy = 0, content = Ember.A([1, 2, 3]); run(function () { view = CollectionView.create({ content: content, itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'content')); }, didInsertElement: function () { didInsertElement++; }, willDestroyElement: function () { willDestroyElement++; }, willDestroy: function () { willDestroy++; this._super(); }, destroy: function() { destroy++; this._super(); } }) }); view.appendTo('#qunit-fixture'); }); equal(didInsertElement, 3); equal(willDestroyElement, 0); equal(willDestroy, 0); equal(destroy, 0); equal(view.$().text(), '123'); run(function () { content.pushObject(4); content.unshiftObject(0); }); equal(didInsertElement, 5); equal(willDestroyElement, 0); equal(willDestroy, 0); equal(destroy, 0); // Remove whitspace added by IE 8 equal(trim(view.$().text()), '01234'); run(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(function () { view.set('content', Ember.A([7,8,9])); }); equal(didInsertElement, 8); equal(willDestroyElement, 5); equal(willDestroy, 5); equal(destroy, 5); // Remove whitspace added by IE 8 equal(trim(view.$().text()), '789'); run(function () { view.destroy(); }); equal(didInsertElement, 8); equal(willDestroyElement, 8); equal(willDestroy, 8); equal(destroy, 8); }); test("should allow changing content property to be null", function() { view = CollectionView.create({ content: Ember.A([1, 2, 3]), emptyView: View.extend({ template: function() { return "(empty)"; } }) }); run(function() { view.append(); }); equal(view.$().children().length, 3, "precond - creates three elements"); run(function() { set(view, 'content', null); }); equal(jQuery.trim(view.$().children().text()), "(empty)", "should display empty view"); }); test("should allow items to access to the CollectionView's current index in the content array", function() { view = CollectionView.create({ content: Ember.A(['zero', 'one', 'two']), itemViewClass: View.extend({ render: function(buf) { buf.push(get(this, 'contentIndex')); } }) }); run(function() { view.append(); }); deepEqual(view.$(':nth-child(1)').text(), "0"); deepEqual(view.$(':nth-child(2)').text(), "1"); deepEqual(view.$(':nth-child(3)').text(), "2"); }); test("should allow declaration of itemViewClass as a string", function() { Ember.lookup = { "Ember": { "View": View } }; view = CollectionView.create({ content: Ember.A([1, 2, 3]), itemViewClass: 'Ember.View' }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('.ember-view').length, 3); }); test("should not render the emptyView if content is emptied and refilled in the same run loop", function() { view = CollectionView.create({ tagName: 'div', content: Ember.A(['NEWS GUVNAH']), emptyView: View.extend({ tagName: 'kbd', render: function(buf) { buf.push("OY SORRY GUVNAH NO NEWS TODAY EH"); } }) }); run(function() { view.append(); }); equal(view.$().find('kbd:contains("OY SORRY GUVNAH")').length, 0); run(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); }); test("a array_proxy that backs an sorted array_controller that backs a collection view functions properly", function() { var array = Ember.A([{ name: "Other Katz" }]); var arrayProxy = ArrayProxy.create({content: array}); var sortedController = ArrayController.create({ content: arrayProxy, sortProperties: ['name'] }); var container = CollectionView.create({ content: sortedController }); run(function() { container.appendTo('#qunit-fixture'); }); run(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(function() { container.destroy(); }); }); 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.create({ destroyElement: function() { if (this.state === 'inDOM') { ok(this.get('element'), this + ' still exists in DOM'); } return this._super(); } }); var ChildView = View.extend(assertProperDestruction, { render: function(buf) { // emulate nested template this.appendChild(View.createWithMixins(assertProperDestruction, { template: function() { return "
    "; } })); } }); var view = CollectionView.create({ content: Ember.A([1]), itemViewClass: ChildView }); run(function() { view.append(); }); equal(jQuery('.inner_element').length, 1, "precond - generates inner element"); run(function() { view.get('content').clear(); }); equal(jQuery('.inner_element').length, 0, "elements removed"); run(function() { view.remove(); }); }); test("should render the emptyView if content array is empty and emptyView is given as string", function() { Ember.lookup = { App: { EmptyView: View.extend({ tagName: 'kbd', render: function(buf) { buf.push("THIS IS AN EMPTY VIEW"); } }) } }; view = CollectionView.create({ tagName: 'del', content: Ember.A(), emptyView: 'App.EmptyView' }); run(function() { view.append(); }); ok(view.$().find('kbd:contains("THIS IS AN EMPTY VIEW")').length, "displays empty view"); }); test("should lookup against the container if itemViewClass is given as a string", function() { var ItemView = View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView.create({ container: container, content: Ember.A([1, 2, 3, 4]), itemViewClass: 'item' }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$('.ember-view').length, 4); function lookupFactory(fullName) { equal(fullName, 'view:item'); return ItemView; } }); test("should lookup only global path against the container if itemViewClass is given as a string", function() { var ItemView = View.extend({ render: function(buf) { buf.push(get(this, 'content')); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView.create({ container: container, content: Ember.A(['hi']), itemViewClass: 'top' }); run(function() { view.appendTo('#qunit-fixture'); }); equal(view.$().text(), 'hi'); function lookupFactory(fullName) { equal(fullName, 'view:top'); return ItemView; } }); 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.extend({ tagName: 'kbd', render: function(buf) { buf.push("THIS IS AN EMPTY VIEW"); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView.create({ container: container, tagName: 'del', content: Ember.A(), emptyView: 'empty' }); run(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; } }); 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.extend({ render: function(buf) { buf.push("EMPTY"); } }); var container = { lookupFactory: lookupFactory }; view = CollectionView.create({ container: container, content: Ember.A(), emptyView: 'top' }); run(function() { view.append(); }); equal(view.$().text(), "EMPTY"); function lookupFactory(fullName) { equal(fullName, 'view:top'); return EmptyView; } }); }); define("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.'); }); }); define("ember-views/tests/views/component_test", ["ember-metal/property_set","ember-metal/run_loop","ember-runtime/system/object","ember-views/views/view","ember-views/views/component"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var set = __dependency1__.set; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var Component = __dependency5__["default"]; var a_slice = Array.prototype.slice; var component, controller, actionCounts, sendCount, actionArguments; QUnit.module("Ember.Component", { setup: function(){ component = Component.create(); }, teardown: function() { run(function() { if(component) { component.destroy(); } if(controller) { controller.destroy(); } }); } }); test("The context of an Ember.Component is itself", function() { strictEqual(component, component.get('context'), "A components's context is itself"); }); test("The controller (target of `action`) of an Ember.Component is itself", function() { strictEqual(component, component.get('controller'), "A components's controller is itself"); }); 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.extend({ templateName: 'blah-blah' }).create(); equal(component.get('layoutName'), 'blah-blah', "The layoutName now contains the templateName specified."); }); 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.extend({ template: 'blah-blah' }).create(); equal(component.get('layout'), 'blah-blah', "The layoutName now contains the templateName specified."); }); test("A template specified to a component is deprecated", function(){ expectDeprecation(function(){ component = Component.extend({ template: 'blah-blah' }).create(); }, 'Do not specify template on a Component, use layout instead.'); }); test("A templateName specified to a component is deprecated", function(){ expectDeprecation(function(){ component = Component.extend({ templateName: 'blah-blah' }).create(); }, 'Do not specify templateName on a Component, use layoutName instead.'); }); test("Specifying both templateName and layoutName to a component is NOT deprecated", function(){ expectNoDeprecation(); component = Component.extend({ templateName: 'blah-blah', layoutName: 'hum-drum' }).create(); }); test("Specifying a templateName on a component with a layoutName specified in a superclass is NOT deprecated", function(){ expectNoDeprecation(); var Parent = Component.extend({ layoutName: 'hum-drum' }); component = Parent.extend({ templateName: 'blah-blah' }).create(); }); QUnit.module("Ember.Component - Actions", { setup: function() { actionCounts = {}; sendCount = 0; actionArguments = null; controller = EmberObject.create({ send: function(actionName) { sendCount++; actionCounts[actionName] = actionCounts[actionName] || 0; actionCounts[actionName]++; actionArguments = a_slice.call(arguments, 1); } }); component = Component.create({ _parentView: EmberView.create({ controller: controller }) }); }, teardown: function() { run(function() { component.destroy(); controller.destroy(); }); } }); test("Calling sendAction on a component without an action defined does nothing", function() { component.sendAction(); equal(sendCount, 0, "addItem action was not invoked"); }); test("Calling sendAction on a component with an action defined calls send on the controller", function() { set(component, 'action', "addItem"); component.sendAction(); equal(sendCount, 1, "send was called once"); equal(actionCounts['addItem'], 1, "addItem event was sent once"); }); test("Calling sendAction with a named action uses the component's property as the action name", function() { set(component, 'playing', "didStartPlaying"); 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"); }); test("Calling sendAction when the action name is not a string raises an exception", function() { set(component, 'action', {}); set(component, 'playing', {}); expectAssertion(function() { component.sendAction(); }); expectAssertion(function() { component.sendAction('playing'); }); }); test("Calling sendAction on a component with a context", function() { set(component, 'playing', "didStartPlaying"); var testContext = {song: 'She Broke My Ember'}; component.sendAction('playing', testContext); deepEqual(actionArguments, [testContext], "context was sent with the action"); }); test("Calling sendAction on a component with multiple parameters", function() { set(component, 'playing', "didStartPlaying"); var firstContext = {song: 'She Broke My Ember'}, secondContext = {song: 'My Achey Breaky Ember'}; component.sendAction('playing', firstContext, secondContext); deepEqual(actionArguments, [firstContext, secondContext], "arguments were sent to the action"); }); }); define("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.'); }); }); define("ember-views/tests/views/container_view_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/computed","ember-runtime/controllers/controller","ember-views/system/jquery","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var computed = __dependency4__.computed; var Controller = __dependency5__["default"]; var jQuery = __dependency6__["default"]; var View = __dependency7__["default"]; var ContainerView = __dependency8__["default"]; var trim = jQuery.trim, container, view, otherContainer; QUnit.module("ember-views/views/container_view_test", { teardown: function() { run(function() { container.destroy(); if (view) { view.destroy(); } if (otherContainer) { otherContainer.destroy(); } }); } }); test("should be able to insert views after the DOM representation is created", function() { container = ContainerView.create({ classNameBindings: ['name'], name: 'foo', container: {} }); run(function() { container.appendTo('#qunit-fixture'); }); view = View.create({ template: function() { return "This is my moment"; } }); run(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(jQuery.trim(container.$().text()), "This is my moment"); run(function() { container.destroy(); }); }); test("should be able to observe properties that contain child views", function() { run(function() { var Container = ContainerView.extend({ childViews: ['displayView'], displayIsDisplayed: computed.alias('displayView.isDisplayed'), displayView: View.extend({ isDisplayed: true }) }); container = Container.create(); container.appendTo('#qunit-fixture'); }); equal(container.get('displayIsDisplayed'), true, "can bind to child view"); run(function () { container.set('displayView.isDisplayed', false); }); equal(container.get('displayIsDisplayed'), false, "can bind to child view"); }); test("childViews inherit their parents iocContainer, and retain the original container even when moved", function() { container = ContainerView.create({ container: {} }); otherContainer = ContainerView.create({ container: {} }); view = View.create(); container.pushObject(view); equal(view.get('parentView'), container, "sets the parent view after the childView is appended"); equal(get(view, 'container'), container.container, "inherits its parentViews iocContainer"); container.removeObject(view); equal(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(get(view, 'container'), container.container, "still inherits its original parentViews iocContainer"); }); test("should set the parentView property on views that are added to the child views array", function() { container = ContainerView.create(); var ViewKlass = View.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(function() { container.removeObject(view); }); equal(get(view, 'parentView'), null, "sets parentView to null when a view is removed"); run(function() { container.appendTo('#qunit-fixture'); }); run(function() { container.pushObject(view); }); equal(get(view, 'parentView'), container, "sets the parent view after the childView is appended"); var secondView = ViewKlass.create(), thirdView = ViewKlass.create(), fourthView = ViewKlass.create(); run(function() { container.pushObject(secondView); container.replace(1, 0, [thirdView, fourthView]); }); equal(get(secondView, 'parentView'), container, "sets the parent view of the second view"); equal(get(thirdView, 'parentView'), container, "sets the parent view of the third view"); equal(get(fourthView, 'parentView'), container, "sets the parent view of the fourth view"); run(function() { container.replace(2, 2); }); equal(get(view, 'parentView'), container, "doesn't change non-removed view"); equal(get(thirdView, 'parentView'), container, "doesn't change non-removed view"); equal(get(secondView, 'parentView'), null, "clears the parent view of the third view"); equal(get(fourthView, 'parentView'), null, "clears the parent view of the fourth view"); run(function() { secondView.destroy(); thirdView.destroy(); fourthView.destroy(); }); }); test("should trigger parentViewDidChange when parentView is changed", function() { container = ContainerView.create(); var secondContainer = ContainerView.create(); var parentViewChanged = 0; var ViewKlass = View.extend({ parentViewDidChange: function() { parentViewChanged++; } }); view = ViewKlass.create(); container.pushObject(view); container.removeChild(view); secondContainer.pushObject(view); equal(parentViewChanged, 3); run(function() { secondContainer.destroy(); }); }); test("should be able to push initial views onto the ContainerView and have it behave", function() { var Container = ContainerView.extend({ init: function () { this._super(); this.pushObject(View.create({ name: 'A', template: function () { return 'A'; } })); this.pushObject(View.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(container, 'appendTo', '#qunit-fixture'); equal(container.$().text(), 'AB'); run(function () { container.pushObject(View.create({ name: 'C', template: function () { return 'C'; } })); }); equal(container.lengthSquared(), 9); deepEqual(container.mapViewNames(), ['A','B','C']); equal(container.$().text(), 'ABC'); run(container, 'destroy'); }); test("views that are removed from a ContainerView should have their child views cleared", function() { container = ContainerView.create(); view = View.createWithMixins({ remove: function() { this._super(); }, template: function(context, options) { options.data.view.appendChild(View); } }); container.pushObject(view); run(function() { container.appendTo('#qunit-fixture'); }); equal(get(view, 'childViews.length'), 1, "precond - renders one child view"); run(function() { container.removeObject(view); }); equal(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"); }); test("if a ContainerView starts with an empy currentView, nothing is displayed", function() { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), '', "has a empty contents"); equal(get(container, 'childViews.length'), 0, "should not have any child views"); }); test("if a ContainerView starts with a currentView, it is rendered as a child view", function() { var controller = Controller.create(); container = ContainerView.create({ controller: controller }); var context = null; var templateData = null; var mainView = View.create({ template: function(ctx, opts) { context = ctx; templateData = opts.data; return "This is the main view."; } }); set(container, 'currentView', mainView); run(function() { container.appendTo('#qunit-fixture'); }); equal(jQuery.trim(container.$().text()), "This is the main view.", "should render its child"); equal(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(templateData.keywords.controller, controller, 'templateData is setup'); equal(templateData.keywords.view, mainView, 'templateData is setup'); }); test("if a ContainerView is created with a currentView, it is rendered as a child view", function() { var context = null; var templateData = null; var mainView = View.create({ template: function(ctx, opts) { context = ctx; templateData = opts.data; return "This is the main view."; } }); var controller = Controller.create(); container = ContainerView.create({ currentView: mainView, controller: controller }); run(function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(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(templateData.keywords.controller, controller, 'templateData is setup'); equal(templateData.keywords.view, mainView, 'templateData is setup'); }); test("if a ContainerView starts with no currentView and then one is set, the ContainerView is updated", function() { var context = null; var templateData = null; var mainView = View.create({ template: function(ctx, opts) { context = ctx; templateData = opts.data; return "This is the main view."; } }); var controller = Controller.create(); container = ContainerView.create({ controller: controller }); run(function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), '', "has a empty contents"); equal(get(container, 'childViews.length'), 0, "should not have any child views"); run(function() { set(container, 'currentView', mainView); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(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(templateData.keywords.controller, controller, 'templateData is setup'); equal(templateData.keywords.view, mainView, 'templateData is setup'); }); test("if a ContainerView starts with a currentView and then is set to null, the ContainerView is updated", function() { var context = null; var templateData = null; var mainView = View.create({ template: function(ctx, opts) { context = ctx; templateData = opts.data; return "This is the main view."; } }); var controller = Controller.create(); container = ContainerView.create({ controller: controller }); container.set('currentView', mainView); run(function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(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(templateData.keywords.controller, controller, 'templateData is setup'); equal(templateData.keywords.view, mainView, 'templateData is setup'); run(function() { set(container, 'currentView', null); }); equal(container.$().text(), '', "has a empty contents"); equal(get(container, 'childViews.length'), 0, "should not have any child views"); }); 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 templateData = null; var mainView = View.create({ template: function(ctx, opts) { context = ctx; templateData = opts.data; return "This is the main view."; } }); var controller = Controller.create(); container = ContainerView.create({ controller: controller }); container.set('currentView', mainView); run(function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(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(templateData.keywords.controller, controller, 'templateData is setup'); equal(templateData.keywords.view, mainView, 'templateData is setup'); run(function() { set(container, 'currentView', null); }); equal(mainView.isDestroyed, true, 'should destroy the previous currentView.'); equal(container.$().text(), '', "has a empty contents"); equal(get(container, 'childViews.length'), 0, "should not have any child views"); }); 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.create(); var mainView = View.create({ template: function() { return "This is the main view."; } }); var secondaryView = View.create({ template: function() { return "This is the secondary view."; } }); var tertiaryView = View.create({ template: function() { return "This is the tertiary view."; } }); container.set('currentView', mainView); run(function() { container.appendTo('#qunit-fixture'); }); equal(container.$().text(), "This is the main view.", "should render its child"); equal(get(container, 'length'), 1, "should have one child view"); equal(container.objectAt(0), mainView, "should have the currentView as the only child view"); run(function() { set(container, 'currentView', secondaryView); }); equal(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(jQuery.trim(container.$().text()), "This is the secondary view.", "should render its child"); run(function() { set(container, 'currentView', tertiaryView); }); equal(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(jQuery.trim(container.$().text()), "This is the tertiary view.", "should render its child"); }); test("should be able to modify childViews many times during an run loop", function () { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var one = View.create({ template: function() { return 'one'; } }); var two = View.create({ template: function() { return 'two'; } }); var three = View.create({ template: function() { return 'three'; } }); run(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'); }); test("should be able to modify childViews then remove the ContainerView in same run loop", function () { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var count = 0; var child = View.create({ template: function () { count++; return 'child'; } }); run(function() { container.pushObject(child); container.remove(); }); equal(count, 0, 'did not render child'); }); test("should be able to modify childViews then destroy the ContainerView in same run loop", function () { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var count = 0; var child = View.create({ template: function () { count++; return 'child'; } }); run(function() { container.pushObject(child); container.destroy(); }); equal(count, 0, 'did not render child'); }); test("should be able to modify childViews then rerender the ContainerView in same run loop", function () { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var count = 0; var child = View.create({ template: function () { count++; return 'child'; } }); run(function() { container.pushObject(child); container.rerender(); }); equal(count, 1, 'rendered child only once'); }); test("should be able to modify childViews then rerender then modify again the ContainerView in same run loop", function () { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var Child = View.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(function() { container.pushObject(one); container.pushObject(two); }); equal(one.count, 1, 'rendered child only once'); equal(two.count, 1, 'rendered child only once'); // Remove whitespace added by IE 8 equal(trim(container.$().text()), 'onetwo'); }); test("should be able to modify childViews then rerender again the ContainerView in same run loop and then modify again", function () { container = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var Child = View.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(function() { container.pushObject(one); container.rerender(); }); equal(one.count, 1, 'rendered child only once'); equal(container.$().text(), 'one'); run(function () { container.pushObject(two); }); equal(one.count, 1, 'rendered child only once'); equal(two.count, 1, 'rendered child only once'); // IE 8 adds a line break but this shouldn't affect validity equal(trim(container.$().text()), 'onetwo'); }); test("should invalidate `element` on itself and childViews when being rendered by ensureChildrenAreInDOM", function () { var root = ContainerView.create(); view = View.create({ template: function() {} }); container = ContainerView.create({ childViews: ['child'], child: view }); run(function() { root.appendTo('#qunit-fixture'); }); run(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(function() { root.destroy(); }); }); test("Child view can only be added to one container at a time", function () { expect(2); container = ContainerView.create(); var secondContainer = ContainerView.create(); run(function() { container.appendTo('#qunit-fixture'); }); var view = View.create(); run(function() { container.set('currentView', view); }); expectAssertion(function() { run(function() { secondContainer.set('currentView', view); }); }); expectAssertion(function() { run(function() { secondContainer.pushObject(view); }); }); run(function() { secondContainer.destroy(); }); }); 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, root = ContainerView.create({}); container = ContainerView.create({ didInsertElement: function() { var view = ContainerView.create({ didInsertElement: function() { counter++; } }); this.pushObject(view); } }); run(function() { root.appendTo('#qunit-fixture'); }); run(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(function() { root.destroy(); }); }); test("ContainerView is observable [DEPRECATED]", function(){ container = ContainerView.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.create()); ok(observerFired, 'View pushed, observer fired'); }); }); define("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.'); }); }); define("ember-views/tests/views/instrumentation_test", ["ember-metal/instrumentation","ember-metal/run_loop","ember-metal/utils","ember-metal/computed","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var subscribe = __dependency1__.subscribe; var instrumentationReset = __dependency1__.reset; var run = __dependency2__["default"]; var guidFor = __dependency3__.guidFor; var computed = __dependency4__.computed; var EmberView = __dependency5__["default"]; var view, beforeCalls, afterCalls; function confirmPayload(payload, view) { var objectId = guidFor(view); equal(payload.object, view.toString(), 'payload object equals view.toString()'); equal(payload.containerKey, view._debugContainerKey, 'payload contains the containerKey'); equal(payload.view, view, 'payload contains the view itself'); } QUnit.module("EmberView#instrumentation", { setup: function () { beforeCalls = []; afterCalls = []; subscribe("render", { before: function(name, timestamp, payload) { beforeCalls.push(payload); }, after: function(name, timestamp, payload) { afterCalls.push(payload); } }); view = EmberView.create({ _debugContainerKey: 'suchryzsd', instrumentDisplay: 'asdfasdfmewj' }); }, teardown: function() { if (view) { run(view, 'destroy'); } instrumentationReset(); } }); test("generates the proper instrumentation details when called directly", function() { var payload = {}; view.instrumentDetails(payload); confirmPayload(payload, view); }); test("should add ember-view to views", function() { run(view, 'createElement'); confirmPayload(beforeCalls[0], view); }); }); define("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.'); }); }); define("ember-views/tests/views/view/actions_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/mixin","ember-runtime/controllers/controller","ember-runtime/system/object","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var Mixin = __dependency5__.Mixin; var Controller = __dependency6__["default"]; var EmberObject = __dependency7__["default"]; var View = __dependency8__["default"]; var view; QUnit.module("View action handling", { teardown: function() { run(function() { if (view) { view.destroy(); } }); } }); test("Action can be handled by a function on actions object", function() { expect(1); view = View.extend({ actions: { poke: function() { ok(true, 'poked'); } } }).create(); view.send("poke"); }); if (!Ember.FEATURES.isEnabled('ember-routing-drop-deprecated-action-style')) { test("Action can be handled by a function on the view (DEPRECATED)", function() { expect(2); expectDeprecation(/Action handlers implemented directly on views are deprecated/); view = View.extend({ poke: function() { ok(true, 'poked'); } }).create(); view.send("poke"); }); } test("A handled action can be bubbled to the target for continued processing", function() { expect(2); view = View.extend({ actions: { poke: function() { ok(true, 'poked 1'); return true; } }, target: Controller.extend({ actions: { poke: function() { ok(true, 'poked 2'); } } }).create() }).create(); view.send("poke"); }); test("Action can be handled by a superclass' actions object", function() { expect(4); var SuperView = View.extend({ actions: { foo: function() { ok(true, 'foo'); }, bar: function(msg) { equal(msg, "HELLO"); } } }); var BarViewMixin = 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"); }); test("Actions cannot be provided at create time", function() { expectAssertion(function() { view = View.create({ actions: { foo: function() { ok(true, 'foo'); } } }); }); // but should be OK on an object that doesn't mix in Ember.ActionHandler var obj = EmberObject.create({ actions: ['foo'] }); }); }); define("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.'); }); }); define("ember-views/tests/views/view/append_to_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-views/system/jquery","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var jQuery = __dependency5__["default"]; var EmberView = __dependency6__["default"]; var ContainerView = __dependency7__["default"]; var View, view, willDestroyCalled, childView; QUnit.module("EmberView - append() and appendTo()", { setup: function() { View = EmberView.extend({}); }, teardown: function() { run(function() { if (!view.isDestroyed) { view.destroy(); } }); } }); test("should be added to the specified element when calling appendTo()", function() { jQuery("#qunit-fixture").html(''); view = View.create(); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.appendTo('#menu'); }); var viewElem = jQuery('#menu').children(); ok(viewElem.length > 0, "creates and appends the view's element"); }); test("should be added to the document body when calling append()", function() { view = View.create({ render: function(buffer) { buffer.push("foo bar baz"); } }); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.append(); }); var viewElem = jQuery(document.body).find(':contains("foo bar baz")'); ok(viewElem.length > 0, "creates and appends the view's element"); }); test("raises an assert when a target does not exist in the DOM", function() { view = View.create(); expectAssertion(function() { run(function() { view.appendTo('does-not-exist-in-dom'); }); }); }); 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.create({ willInsertElement: function() { willInsertElementCalledInChild = true; } })); } }); view = ViewWithCallback.create(); run(function() { view.append(); }); ok(willInsertElementCalled, "willInsertElement called"); ok(willInsertElementCalledInChild, "willInsertElement called in child"); ok(didInsertElementCalled, "didInsertElement called"); }); test("remove removes an element from the DOM", function() { willDestroyCalled = 0; view = View.create({ willDestroyElement: function() { willDestroyCalled++; } }); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.append(); }); ok(jQuery("#" + get(view, 'elementId')).length === 1, "precond - element was inserted"); run(function() { view.remove(); }); ok(jQuery("#" + get(view, 'elementId')).length === 0, "remove removes an element from the DOM"); ok(EmberView.views[get(view, 'elementId')] === undefined, "remove does not remove the view from the view hash"); ok(!get(view, 'element'), "remove nulls out the element"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once"); }); test("destroy more forcibly removes the view", function() { willDestroyCalled = 0; view = View.create({ willDestroyElement: function() { willDestroyCalled++; } }); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.append(); }); ok(jQuery("#" + get(view, 'elementId')).length === 1, "precond - element was inserted"); run(function() { view.destroy(); }); ok(jQuery("#" + get(view, 'elementId')).length === 0, "destroy removes an element from the DOM"); ok(EmberView.views[get(view, 'elementId')] === undefined, "destroy removes a view from the global views hash"); equal(get(view, 'isDestroyed'), true, "the view is marked as destroyed"); ok(!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() { View = ContainerView.extend({ childViews: ['child'], child: EmberView.extend({ elementId: 'child' }) }); }, teardown: function() { run(function() { if (!view.isDestroyed) { view.destroy(); } }); } }); test("should be added to the specified element when calling appendTo()", function() { jQuery("#qunit-fixture").html(''); view = View.create(); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.appendTo('#menu'); }); var viewElem = jQuery('#menu #child'); ok(viewElem.length > 0, "creates and appends the view's element"); }); test("should be added to the document body when calling append()", function() { jQuery("#qunit-fixture").html(''); view = View.create(); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.append(); }); var viewElem = jQuery('#child'); ok(viewElem.length > 0, "creates and appends the view's element"); }); QUnit.module("EmberView - removing views in a view hierarchy", { setup: function() { willDestroyCalled = 0; view = ContainerView.create({ childViews: ['child'], child: EmberView.create({ willDestroyElement: function() { willDestroyCalled++; } }) }); childView = get(view, 'child'); }, teardown: function() { run(function() { if (!view.isDestroyed) { view.destroy(); } }); } }); test("remove removes child elements from the DOM", function() { ok(!get(childView, 'element'), "precond - should not have an element"); run(function() { view.append(); }); ok(jQuery("#" + get(childView, 'elementId')).length === 1, "precond - element was inserted"); // remove parent view run(function() { view.remove(); }); ok(jQuery("#" + get(childView, 'elementId')).length === 0, "remove removes child elements the DOM"); ok(EmberView.views[get(childView, 'elementId')] === undefined, "remove does not remove child views from the view hash"); ok(!get(childView, 'element'), "remove nulls out child elements"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once"); }); test("destroy more forcibly removes child views", function() { ok(!get(childView, 'element'), "precond - should not have an element"); run(function() { view.append(); }); ok(jQuery("#" + get(childView, 'elementId')).length === 1, "precond - child element was inserted"); willDestroyCalled = 0; run(function() { view.destroy(); }); ok(jQuery("#" + get(childView, 'elementId')).length === 0, "destroy removes child elements from the DOM"); ok(EmberView.views[get(childView, 'elementId')] === undefined, "destroy removes a child views from the global views hash"); equal(get(childView, 'isDestroyed'), true, "child views are marked as destroyed"); ok(!get(childView, 'element'), "child views no longer have an element"); equal(willDestroyCalled, 1, "the willDestroyElement hook was called once on children"); }); test("destroy removes a child view from its parent", function() { ok(!get(childView, 'element'), "precond - should not have an element"); run(function() { view.append(); }); ok(jQuery("#" + get(childView, 'elementId')).length === 1, "precond - child element was inserted"); run(function() { childView.destroy(); }); ok(get(view, 'childViews.length') === 0, "Destroyed child views should be removed from their parent"); }); }); define("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.'); }); }); define("ember-views/tests/views/view/attribute_bindings_test", ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-metal/observer","ember-metal/property_events","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var run = __dependency3__["default"]; var observersFor = __dependency4__.observersFor; var changeProperties = __dependency5__.changeProperties; var EmberView = __dependency6__["default"]; var originalLookup = Ember.lookup, lookup, view; var appendView = function() { run(function() { view.appendTo('#qunit-fixture'); }); }; QUnit.module("EmberView - Attribute Bindings", { setup: function() { Ember.lookup = lookup = {}; }, teardown: function() { if (view) { run(function() { view.destroy(); }); view = null; } Ember.lookup = originalLookup; } }); test("should render attribute bindings", function() { view = EmberView.create({ classNameBindings: ['priority', 'isUrgent', 'isClassified:classified', 'canIgnore'], attributeBindings: ['type', 'isDisabled:disabled', 'exploded', 'destroyed', 'exists', 'nothing', 'notDefined', 'notNumber', 'explosions'], type: 'submit', isDisabled: true, exploded: false, destroyed: false, exists: true, nothing: null, notDefined: undefined, notNumber: NaN }); run(function() { view.createElement(); }); equal(view.$().attr('type'), 'submit', "updates type attribute"); ok(view.$().prop('disabled'), "supports customizing attribute name for Boolean values"); ok(!view.$().prop('exploded'), "removes exploded attribute when false"); ok(!view.$().prop('destroyed'), "removes destroyed attribute when false"); ok(view.$().prop('exists'), "adds exists attribute when true"); ok(!view.$().attr('nothing'), "removes nothing attribute when null"); ok(!view.$().attr('notDefined'), "removes notDefined attribute when undefined"); ok(!view.$().attr('notNumber'), "removes notNumber attribute when NaN"); }); test("should update attribute bindings", function() { view = EmberView.create({ classNameBindings: ['priority', 'isUrgent', 'isClassified:classified', 'canIgnore'], attributeBindings: ['type', 'isDisabled:disabled', 'exploded', 'destroyed', 'exists', 'nothing', 'notDefined', 'notNumber', 'explosions'], type: 'reset', isDisabled: true, exploded: true, destroyed: true, exists: false, nothing: true, notDefined: true, notNumber: true, explosions: 15 }); run(function() { view.createElement(); }); equal(view.$().attr('type'), 'reset', "adds type attribute"); ok(view.$().prop('disabled'), "adds disabled attribute when true"); ok(view.$().prop('exploded'), "adds exploded attribute when true"); ok(view.$().prop('destroyed'), "adds destroyed attribute when true"); ok(!view.$().prop('exists'), "does not add exists attribute when false"); ok(view.$().prop('nothing'), "adds nothing attribute when true"); ok(view.$().prop('notDefined'), "adds notDefined attribute when true"); ok(view.$().prop('notNumber'), "adds notNumber attribute when true"); equal(view.$().attr('explosions'), "15", "adds integer attributes"); run(function() { view.set('type', 'submit'); view.set('isDisabled', false); view.set('exploded', false); view.set('destroyed', false); view.set('exists', true); view.set('nothing', null); view.set('notDefined', undefined); view.set('notNumber', NaN); }); equal(view.$().attr('type'), 'submit', "updates type attribute"); ok(!view.$().prop('disabled'), "removes disabled attribute when false"); ok(!view.$().prop('exploded'), "removes exploded attribute when false"); ok(!view.$().prop('destroyed'), "removes destroyed attribute when false"); ok(view.$().prop('exists'), "adds exists attribute when true"); ok(!view.$().attr('nothing'), "removes nothing attribute when null"); ok(!view.$().attr('notDefined'), "removes notDefined attribute when undefined"); ok(!view.$().attr('notNumber'), "removes notNumber attribute when NaN"); }); // 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. test("should allow binding to String objects", function() { view = EmberView.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(function() { view.createElement(); }); equal(view.$().attr('foo'), 'bar', "should convert String object to bare string"); run(function() { view.set('foo', false); }); ok(!view.$().attr('foo'), "removes foo attribute when false"); }); test("should teardown observers on rerender", function() { view = EmberView.create({ attributeBindings: ['foo'], classNameBindings: ['foo'], foo: 'bar' }); appendView(); equal(observersFor(view, 'foo').length, 2); run(function() { view.rerender(); }); equal(observersFor(view, 'foo').length, 2); }); test("handles attribute bindings for properties", function() { view = EmberView.create({ attributeBindings: ['checked'], checked: null }); appendView(); equal(!!view.$().prop('checked'), false, 'precond - is not checked'); run(function() { view.set('checked', true); }); equal(view.$().prop('checked'), true, 'changes to checked'); run(function() { view.set('checked', false); }); equal(!!view.$().prop('checked'), false, 'changes to unchecked'); }); test("handles `undefined` value for properties", function() { view = EmberView.create({ attributeBindings: ['value'], value: "test" }); appendView(); equal(view.$().prop('value'), "test", "value is defined"); run(function() { view.set('value', undefined); }); equal(!!view.$().prop('value'), false, "value is not defined"); }); test("handles null value for attributes on text fields", function() { view = EmberView.create({ tagName: 'input', attributeBindings: ['value'] }); appendView(); view.$().attr('value', 'test'); equal(view.$().attr('value'), "test", "value is defined"); run(function() { view.set('value', null); }); equal(!!view.$().prop('value'), false, "value is not defined"); }); test("handles a 0 value attribute on text fields", function() { view = EmberView.create({ tagName: 'input', attributeBindings: ['value'] }); appendView(); view.$().attr('value', 'test'); equal(view.$().attr('value'), "test", "value is defined"); run(function() { view.set('value', 0); }); strictEqual(view.$().prop('value'), "0", "value should be 0"); }); test("attributeBindings should not fail if view has been removed", function() { run(function() { view = EmberView.create({ attributeBindings: ['checked'], checked: true }); }); run(function() { view.createElement(); }); var error; try { run(function() { changeProperties(function() { view.set('checked', false); view.remove(); }); }); } catch(e) { error = e; } ok(!error, error); }); test("attributeBindings should not fail if view has been destroyed", function() { run(function() { view = EmberView.create({ attributeBindings: ['checked'], checked: true }); }); run(function() { view.createElement(); }); var error; try { run(function() { changeProperties(function() { view.set('checked', false); view.destroy(); }); }); } catch(e) { error = e; } ok(!error, error); }); }); define("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.'); }); }); define("ember-views/tests/views/view/child_views_test", ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var run = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var parentView, childView, childViews; QUnit.module('tests/views/view/child_views_tests.js', { setup: function() { parentView = EmberView.create({ render: function(buffer) { buffer.push('Em'); this.appendChild(childView); } }); childView = EmberView.create({ template: function() { return 'ber'; } }); }, teardown: function() { run(function() { parentView.destroy(); childView.destroy(); }); childViews = null; } }); // no parent element, buffer, no element // parent element // no parent element, no buffer, no element test("should render an inserted child view when the child is inserted before a DOM element is created", function() { run(function() { parentView.append(); }); equal(parentView.$().text(), 'Ember', 'renders the child view after the parent view'); }); test("should not duplicate childViews when rerendering in buffer", function() { var Inner = EmberView.extend({ template: function() { return ''; } }); var Inner2 = EmberView.extend({ template: function() { return ''; } }); var Middle = EmberView.extend({ render: function(buffer) { this.appendChild(Inner); this.appendChild(Inner2); } }); var outer = EmberView.create({ render: function(buffer) { this.middle = this.appendChild(Middle); } }); run(function() { outer.renderToBuffer(); }); equal(outer.get('middle.childViews.length'), 2, 'precond middle has 2 child views rendered to buffer'); raises(function() { run(function() { outer.middle.rerender(); }); }, /Something you did caused a view to re-render after it rendered but before it was inserted into the DOM./); equal(outer.get('middle.childViews.length'), 2, 'middle has 2 child views rendered to buffer'); run(function() { outer.destroy(); }); }); }); define("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.'); }); }); define("ember-views/tests/views/view/class_name_bindings_test", ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/property_events","ember-metal/watching","ember-runtime/system/object","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var set = __dependency3__.set; var run = __dependency4__["default"]; var changeProperties = __dependency5__.changeProperties; var isWatching = __dependency6__.isWatching; var EmberObject = __dependency7__["default"]; var EmberView = __dependency8__["default"]; var view; QUnit.module("EmberView - Class Name Bindings", { teardown: function() { run(function() { view.destroy(); }); } }); test("should apply bound class names to the element", function() { view = EmberView.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(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"); }); test("should add, remove, or change class names if changed after element is created", function() { view = EmberView.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.create({ count: 'five-messages', resent: false }) }); run(function() { view.createElement(); set(view, 'priority', 'orange'); set(view, 'isUrgent', false); set(view, 'canIgnore', true); set(view, 'isEnabled', false); set(view, 'messages.count', 'six-messages'); 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"); }); test(":: class name syntax works with an empty true class", function() { view = EmberView.create({ isEnabled: false, classNameBindings: ['isEnabled::not-enabled'] }); run(function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view not-enabled', "false class is rendered when property is false"); run(function() { view.set('isEnabled', true); }); equal(view.$().attr('class'), 'ember-view', "no class is added when property is true and the class is empty"); }); test("classNames should not be duplicated on rerender", function() { run(function() { view = EmberView.create({ classNameBindings: ['priority'], priority: 'high' }); }); run(function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view high'); run(function() { view.rerender(); }); equal(view.$().attr('class'), 'ember-view high'); }); test("classNameBindings should work when the binding property is updated and the view has been removed of the DOM", function() { run(function() { view = EmberView.create({ classNameBindings: ['priority'], priority: 'high' }); }); run(function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view high'); run(function() { view.remove(); }); view.set('priority', 'low'); run(function() { view.append(); }); equal(view.$().attr('class'), 'ember-view low'); }); test("classNames removed by a classNameBindings observer should not re-appear on rerender", function() { view = EmberView.create({ classNameBindings: ['isUrgent'], isUrgent: true }); run(function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view is-urgent'); run(function() { view.set('isUrgent', false); }); equal(view.$().attr('class'), 'ember-view'); run(function() { view.rerender(); }); equal(view.$().attr('class'), 'ember-view'); }); test("classNameBindings lifecycle test", function() { run(function() { view = EmberView.create({ classNameBindings: ['priority'], priority: 'high' }); }); equal(isWatching(view, 'priority'), false); run(function() { view.createElement(); }); equal(view.$().attr('class'), 'ember-view high'); equal(isWatching(view, 'priority'), true); run(function() { view.remove(); view.set('priority', 'low'); }); equal(isWatching(view, 'priority'), false); }); test("classNameBindings should not fail if view has been removed", function() { run(function() { view = EmberView.create({ classNameBindings: ['priority'], priority: 'high' }); }); run(function() { view.createElement(); }); var error; try { run(function() { changeProperties(function() { view.set('priority', 'low'); view.remove(); }); }); } catch(e) { error = e; } ok(!error, error); }); test("classNameBindings should not fail if view has been destroyed", function() { run(function() { view = EmberView.create({ classNameBindings: ['priority'], priority: 'high' }); }); run(function() { view.createElement(); }); var error; try { run(function() { changeProperties(function() { view.set('priority', 'low'); view.destroy(); }); }); } catch(e) { error = e; } ok(!error, error); }); test("Providing a binding with a space in it asserts", function() { view = EmberView.create({ classNameBindings: 'i:think:i am:so:clever' }); expectAssertion(function() { view.createElement(); }, /classNameBindings must not have spaces in them/i); }); }); define("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.'); }); }); define("ember-views/tests/views/view/class_string_for_value_test", ["ember-views/views/view"], function(__dependency1__) { "use strict"; var View = __dependency1__["default"]; QUnit.module("View - _classStringForValue"); var cSFV = View._classStringForValue; test("returns dasherized version of last path part if value is true", function() { equal(cSFV("propertyName", true), "property-name", "class is dasherized"); equal(cSFV("content.propertyName", true), "property-name", "class is dasherized"); }); test("returns className if value is true and className is specified", function() { equal(cSFV("propertyName", true, "truthyClass"), "truthyClass", "returns className if given"); equal(cSFV("content.propertyName", true, "truthyClass"), "truthyClass", "returns className if given"); }); test("returns falsyClassName if value is false and falsyClassName is specified", function() { equal(cSFV("propertyName", false, "truthyClass", "falsyClass"), "falsyClass", "returns falsyClassName if given"); equal(cSFV("content.propertyName", false, "truthyClass", "falsyClass"), "falsyClass", "returns falsyClassName if given"); }); test("returns null if value is false and falsyClassName is not specified", function() { equal(cSFV("propertyName", false, "truthyClass"), null, "returns null if falsyClassName is not specified"); equal(cSFV("content.propertyName", false, "truthyClass"), null, "returns null if falsyClassName is not specified"); }); test("returns null if value is false", function() { equal(cSFV("propertyName", false), null, "returns null if value is false"); equal(cSFV("content.propertyName", false), null, "returns null if value is false"); }); test("returns null if value is true and className is not specified and falsyClassName is specified", function() { equal(cSFV("propertyName", true, undefined, "falsyClassName"), null, "returns null if value is true"); equal(cSFV("content.propertyName", true, undefined, "falsyClassName"), null, "returns null if value is true"); }); test("returns the value if the value is truthy", function() { equal(cSFV("propertyName", "myString"), "myString", "returns value if the value is truthy"); equal(cSFV("content.propertyName", "myString"), "myString", "returns value if the value is truthy"); equal(cSFV("propertyName", "123"), 123, "returns value if the value is truthy"); equal(cSFV("content.propertyName", 123), 123, "returns value if the value is truthy"); }); test("treat empty array as falsy value and return null", function() { equal(cSFV("propertyName", [], "truthyClass"), null, "returns null if value is false"); equal(cSFV("content.propertyName", [], "truthyClass"), null, "returns null if value is false"); }); test("treat non-empty array as truthy value and return the className if specified", function() { equal(cSFV("propertyName", ['emberjs'], "truthyClass"), "truthyClass", "returns className if given"); equal(cSFV("content.propertyName", ['emberjs'], "truthyClass"), "truthyClass", "returns className if given"); }); }); define("ember-views/tests/views/view/class_string_for_value_test.jshint", [], function() { "use strict"; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/class_string_for_value_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/class_string_for_value_test.js should pass jshint.'); }); }); define("ember-views/tests/views/view/context_test", ["ember-metal/core","ember-metal/run_loop","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var ContainerView = __dependency4__["default"]; QUnit.module("EmberView - context property"); 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.create(); var middleView = ContainerView.create(); var outerView = App.outerView = ContainerView.create({ controller: a }); run(function() { outerView.appendTo('#qunit-fixture'); }); run(function () { outerView.set('currentView', middleView); }); run(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(function() { innerView.destroy(); middleView.destroy(); outerView.destroy(); }); }); }); define("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.'); }); }); define("ember-views/tests/views/view/controller_test", ["ember-metal/core","ember-metal/run_loop","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var ContainerView = __dependency3__["default"]; QUnit.module("Ember.View - controller property"); test("controller property should be inherited from nearest ancestor with controller", function() { var grandparent = ContainerView.create(); var parent = ContainerView.create(); var child = ContainerView.create(); var grandchild = ContainerView.create(); var grandparentController = {}; var parentController = {}; run(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(function() { child.pushObject(grandchild); }); strictEqual(grandchild.get('controller'), parentController); var newController = {}; run(function() { parent.set('controller', newController); }); strictEqual(parent.get('controller'), newController); strictEqual(child.get('controller'), newController); strictEqual(grandchild.get('controller'), newController); run(function() { grandparent.destroy(); parent.destroy(); child.destroy(); grandchild.destroy(); }); }); }); define("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.'); }); }); define("ember-views/tests/views/view/create_child_view_test", ["ember-metal/property_get","ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var view, myViewClass, newView, container; QUnit.module("EmberView#createChildView", { setup: function() { container = { }; view = EmberView.create({ container: container }); myViewClass = EmberView.extend({ isMyView: true, foo: 'bar' }); }, teardown: function() { run(function() { view.destroy(); if(newView) { newView.destroy(); } }); } }); 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(get(newView, 'isMyView'), 'newView is instance of myView'); equal(get(newView, 'foo'), 'baz', 'view did get custom attributes'); ok(!attrs.parentView, "the original attributes hash was not mutated"); }); test("should set newView.parentView to receiver", function() { newView = view.createChildView(myViewClass) ; equal(newView.container, container, 'expects to share container with parent'); equal(get(newView, 'parentView'), view, 'newView.parentView == view'); }); 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(get(view, 'someChildView'), newView); }); 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(get(newView, 'foo'), 'baz', 'view did get custom attributes'); deepEqual(newView, myView); }); test("should create from string via container lookup", function() { var ChildViewClass = EmberView.extend(), 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'); }); 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'); }); }); }); define("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.'); }); }); define("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"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var ContainerView = __dependency4__["default"]; var view; QUnit.module("Ember.View#createElement", { teardown: function() { run(function() { view.destroy(); }); } }); test("returns the receiver", function() { var ret; view = EmberView.create(); run(function() { ret = view.createElement(); }); equal(ret, view, 'returns receiver'); }); test("calls render and turns resultant string into element", function() { view = EmberView.create({ tagName: 'span', render: function(buffer) { buffer.push("foo"); } }); equal(get(view, 'element'), null, 'precondition - has no element'); run(function() { view.createElement(); }); var elem = 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'); }); test("generated element include HTML from child views as well", function() { view = ContainerView.create({ childViews: [ EmberView.create({ elementId: "foo" })] }); run(function() { view.createElement(); }); ok(view.$('#foo').length, 'has element with child elementId'); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var ContainerView = __dependency4__["default"]; var view; QUnit.module("EmberView#destroyElement", { teardown: function() { run(function() { view.destroy(); }); } }); test("if it has no element, does nothing", function() { var callCount = 0; view = EmberView.create({ willDestroyElement: function() { callCount++; } }); ok(!get(view, 'element'), 'precond - does NOT have element'); run(function() { view.destroyElement(); }); equal(callCount, 0, 'did not invoke callback'); }); test("if it has a element, calls willDestroyElement on receiver and child views then deletes the element", function() { var parentCount = 0, childCount = 0; view = ContainerView.create({ willDestroyElement: function() { parentCount++; }, childViews: [ContainerView.extend({ // no willDestroyElement here... make sure no errors are thrown childViews: [EmberView.extend({ willDestroyElement: function() { childCount++; } })] })] }); run(function() { view.createElement(); }); ok(get(view, 'element'), 'precond - view has element'); run(function() { view.destroyElement(); }); equal(parentCount, 1, 'invoked destroy element on the parent'); equal(childCount, 1, 'invoked destroy element on the child'); ok(!get(view, 'element'), 'view no longer has element'); ok(!get(get(view, 'childViews').objectAt(0), 'element'), 'child no longer has an element'); }); test("returns receiver", function() { var ret; view = EmberView.create(); run(function() { view.createElement(); ret = view.destroyElement(); }); equal(ret, view, 'returns receiver'); }); test("removes element from parentNode if in DOM", function() { view = EmberView.create(); run(function() { view.append(); }); var parent = view.$().parent(); ok(get(view, 'element'), 'precond - has element'); run(function() { view.destroyElement(); }); equal(view.$(), undefined, 'view has no selector'); ok(!parent.find('#'+view.get('elementId')).length, 'element no longer in parent node'); }); }); define("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.'); }); }); define("ember-views/tests/views/view/destroy_test", ["ember-metal/property_get","ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; QUnit.module("Ember.View#destroy"); test("should teardown viewName on parentView when childView is destroyed", function() { var viewName = "someChildView", parentView = EmberView.create(), childView = parentView.createChildView(EmberView, {viewName: viewName}); equal(get(parentView, viewName), childView, "Precond - child view was registered on parent"); run(function() { childView.destroy(); }); equal(get(parentView, viewName), null, "viewName reference was removed on parent"); run(function() { parentView.destroy(); }); }); }); define("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.'); }); }); define("ember-views/tests/views/view/element_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-views/system/jquery","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var jQuery = __dependency4__["default"]; var EmberView = __dependency5__["default"]; var ContainerView = __dependency6__["default"]; var parentView, child, parentDom, childDom, view; QUnit.module("Ember.View#element", { teardown: function() { run(function() { if (parentView) { parentView.destroy(); } view.destroy(); }); } }); test("returns null if the view has no element and no parent view", function() { view = EmberView.create() ; equal(get(view, 'parentView'), null, 'precond - has no parentView'); equal(get(view, 'element'), null, 'has no element'); }); test("returns null if the view has no element and parent view has no element", function() { parentView = ContainerView.create({ childViews: [ EmberView.extend() ] }); view = get(parentView, 'childViews').objectAt(0); equal(get(view, 'parentView'), parentView, 'precond - has parent view'); equal(get(parentView, 'element'), null, 'parentView has no element'); equal(get(view, 'element'), null, ' has no element'); }); test("returns element if you set the value", function() { view = EmberView.create(); equal(get(view, 'element'), null, 'precond- has no element'); var dom = document.createElement('div'); set(view, 'element', dom); equal(get(view, 'element'), dom, 'now has set element'); }); QUnit.module("Ember.View#element - autodiscovery", { setup: function() { parentView = ContainerView.create({ childViews: [ EmberView.extend({ elementId: 'child-view' }) ] }); child = get(parentView, 'childViews').objectAt(0); // setup parent/child dom parentDom = jQuery("
    ")[0]; // set parent element... set(parentView, 'element', parentDom); }, teardown: function() { run(function() { parentView.destroy(); if (view) { view.destroy(); } }); parentView = child = parentDom = childDom = null ; } }); test("discovers element if has no element but parent view does have element", function() { equal(get(parentView, 'element'), parentDom, 'precond - parent has element'); ok(parentDom.firstChild, 'precond - parentDom has first child'); equal(child.$().attr('id'), 'child-view', 'view discovered child'); }); test("should not allow the elementId to be changed after inserted", function() { view = EmberView.create({ elementId: 'one' }); run(function() { view.appendTo('#qunit-fixture'); }); raises(function() { view.set('elementId', 'two'); }, "raises elementId changed exception"); equal(view.get('elementId'), 'one', 'elementId is still "one"'); }); }); define("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.'); }); }); define("ember-views/tests/views/view/evented_test", ["ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/object","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var view; QUnit.module("EmberView evented helpers", { teardown: function() { run(function() { view.destroy(); }); } }); test("fire should call method sharing event name if it exists on the view", function() { var eventFired = false; view = EmberView.create({ fireMyEvent: function() { this.trigger('myEvent'); }, myEvent: function() { eventFired = true; } }); run(function() { view.fireMyEvent(); }); equal(eventFired, true, "fired the view method sharing the event name"); }); test("fire does not require a view method with the same name", function() { var eventFired = false; view = EmberView.create({ fireMyEvent: function() { this.trigger('myEvent'); } }); var listenObject = EmberObject.create({ onMyEvent: function() { eventFired = true; } }); view.on('myEvent', listenObject, 'onMyEvent'); run(function() { view.fireMyEvent(); }); equal(eventFired, true, "fired the event without a view method sharing its name"); run(function() { listenObject.destroy(); }); }); }); define("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.'); }); }); define("ember-views/tests/views/view/init_test", ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-metal/computed","ember-runtime/system/object","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var run = __dependency3__["default"]; var computed = __dependency4__.computed; var EmberObject = __dependency5__["default"]; var EmberView = __dependency6__["default"]; var originalLookup = Ember.lookup, lookup, view; QUnit.module("EmberView.create", { setup: function() { Ember.lookup = lookup = {}; }, teardown: function() { run(function() { view.destroy(); }); Ember.lookup = originalLookup; } }); test("registers view in the global views hash using layerId for event targeted", function() { view = EmberView.create(); run(function() { view.appendTo('#qunit-fixture'); }); equal(EmberView.views[get(view, 'elementId')], view, 'registers view'); }); QUnit.module("EmberView.createWithMixins"); test("should warn if a non-array is used for classNames", function() { expectAssertion(function() { EmberView.createWithMixins({ elementId: 'test', classNames: computed(function() { return ['className']; })["volatile"]() }); }, /Only arrays are allowed/i); }); test("should warn if a non-array is used for classNamesBindings", function() { expectAssertion(function() { EmberView.createWithMixins({ elementId: 'test', classNameBindings: computed(function() { return ['className']; })["volatile"]() }); }, /Only arrays are allowed/i); }); }); define("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.'); }); }); define("ember-views/tests/views/view/is_visible_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-runtime/system/object","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var EmberView = __dependency5__["default"]; var ContainerView = __dependency6__["default"]; var View, view, parentBecameVisible, childBecameVisible, grandchildBecameVisible; var parentBecameHidden, childBecameHidden, grandchildBecameHidden; QUnit.module("EmberView#isVisible", { setup: function() { parentBecameVisible=0; childBecameVisible=0; grandchildBecameVisible=0; parentBecameHidden=0; childBecameHidden=0; grandchildBecameHidden=0; View = ContainerView.extend({ childViews: ['child'], becameVisible: function() { parentBecameVisible++; }, becameHidden: function() { parentBecameHidden++; }, child: ContainerView.extend({ childViews: ['grandchild'], becameVisible: function() { childBecameVisible++; }, becameHidden: function() { childBecameHidden++; }, grandchild: EmberView.extend({ template: function() { return "seems weird bro"; }, becameVisible: function() { grandchildBecameVisible++; }, becameHidden: function() { grandchildBecameHidden++; } }) }) }); }, teardown: function() { if (view) { run(function() { view.destroy(); }); } } }); test("should hide views when isVisible is false", function() { view = EmberView.create({ isVisible: false }); run(function() { view.append(); }); ok(view.$().is(':hidden'), "the view is hidden"); run(function(){ set(view, 'isVisible', true); }); ok(view.$().is(':visible'), "the view is visible"); run(function() { view.remove(); }); }); test("should hide element if isVisible is false before element is created", function() { view = EmberView.create({ isVisible: false }); ok(!get(view, 'isVisible'), "precond - view is not visible"); set(view, 'template', function() { return "foo"; }); run(function() { view.append(); }); ok(view.$().is(':hidden'), "should be hidden"); run(function() { view.remove(); }); run(function(){ set(view, 'isVisible', true); }); run(function() { view.append(); }); ok(view.$().is(':visible'), "view should be visible"); run(function() { view.remove(); }); }); test("view should be notified after isVisible is set to false and the element has been hidden", function() { run(function() { view = View.create({ isVisible: false }); view.append(); }); ok(view.$().is(':hidden'), "precond - view is hidden when appended"); run(function() { view.set('isVisible', true); }); ok(view.$().is(':visible'), "precond - view is now visible"); equal(parentBecameVisible, 1); equal(childBecameVisible, 1); equal(grandchildBecameVisible, 1); }); 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); var grandchildView = childView.get('childViews').objectAt(0); run(function() { view.append(); }); ok(view.$().is(':visible'), "precond - view is visible when appended"); run(function() { childView.set('isVisible', false); }); ok(childView.$().is(':hidden'), "precond - view is now hidden"); equal(childBecameHidden, 1); equal(grandchildBecameHidden, 1); }); test("view should be notified after isVisible is set to true and the element has been shown", function() { view = View.create({ isVisible: false }); var childView = view.get('childViews').objectAt(0); var grandchildView = childView.get('childViews').objectAt(0); run(function() { view.append(); }); ok(view.$().is(':hidden'), "precond - view is hidden when appended"); run(function() { view.set('isVisible', true); }); ok(view.$().is(':visible'), "precond - view is now visible"); equal(parentBecameVisible, 1); equal(childBecameVisible, 1); equal(grandchildBecameVisible, 1); }); 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); var grandchildView = childView.get('childViews').objectAt(0); run(function() { view.append(); }); ok(view.$().is(':visible'), "precond - view is visible when appended"); run(function() { childView.set('isVisible', false); }); run(function() { view.set('isVisible', false); }); childBecameVisible = 0; grandchildBecameVisible = 0; run(function() { childView.set('isVisible', true); }); equal(childBecameVisible, 0, "the child did not become visible"); equal(grandchildBecameVisible, 0, "the grandchild did not become visible"); }); 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); var grandchildView = childView.get('childViews').objectAt(0); run(function() { view.append(); }); ok(view.$().is(':hidden'), "precond - view is hidden when appended"); run(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(function() { view.set('isVisible', true); }); equal(parentBecameVisible, 1); equal(childBecameVisible, 1); equal(grandchildBecameVisible, 1); }); }); define("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.'); }); }); define("ember-views/tests/views/view/jquery_test", ["ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/object","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var view ; QUnit.module("EmberView#$", { setup: function() { view = EmberView.extend({ render: function(context, firstTime) { context.push(''); } }).create(); run(function() { view.append(); }); }, teardown: function() { run(function() { view.destroy(); }); } }); test("returns undefined if no element", function() { var view = EmberView.create(); ok(!get(view, 'element'), 'precond - should have no element'); equal(view.$(), undefined, 'should return undefined'); equal(view.$('span'), undefined, 'should undefined if filter passed'); run(function() { view.destroy(); }); }); test("returns jQuery object selecting element if provided", function() { ok(get(view, 'element'), 'precond - should have element'); var jquery = view.$(); equal(jquery.length, 1, 'view.$() should have one element'); equal(jquery[0], get(view, 'element'), 'element should be element'); }); test("returns jQuery object selecting element inside element if provided", function() { ok(get(view, 'element'), 'precond - should have element'); var jquery = view.$('span'); equal(jquery.length, 1, 'view.$() should have one element'); equal(jquery[0].parentNode, get(view, 'element'), 'element should be in element'); }); test("returns empty jQuery object if filter passed that does not match item in parent", function() { ok(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'); }); }); define("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.'); }); }); define("ember-views/tests/views/view/layout_test", ["container","ember-metal/property_get","ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var Container = __dependency1__["default"]; var get = __dependency2__.get; var run = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var container, view; QUnit.module("EmberView - Layout Functionality", { setup: function() { container = new Container(); container.optionsForType('template', { instantiate: false }); }, teardown: function() { run(function() { view.destroy(); }); } }); test("should call the function of the associated layout", function() { var templateCalled = 0, layoutCalled = 0; container.register('template:template', function() { templateCalled++; }); container.register('template:layout', function() { layoutCalled++; }); view = EmberView.create({ container: container, layoutName: 'layout', templateName: 'template' }); run(function() { view.createElement(); }); equal(templateCalled, 0, "template is not called when layout is present"); equal(layoutCalled, 1, "layout is called when layout is present"); }); test("should call the function of the associated template with itself as the context", function() { container.register('template:testTemplate', function(dataSource) { return "

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

    "; }); view = EmberView.create({ container: container, layoutName: 'testTemplate', context: { personName: "Tom DAAAALE" } }); run(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"); }); test("should fall back to defaultTemplate if neither template nor templateName are provided", function() { var View; View = EmberView.extend({ defaultLayout: function(dataSource) { return "

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

    "; } }); view = View.create({ context: { personName: "Tom DAAAALE" } }); run(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"); }); test("should not use defaultLayout if layout is provided", function() { var View; View = EmberView.extend({ layout: function() { return "foo"; }, defaultLayout: function(dataSource) { return "

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

    "; } }); view = View.create(); run(function() { view.createElement(); }); equal("foo", view.$().text(), "default layout was not printed"); }); test("the template property is available to the layout template", function() { view = EmberView.create({ template: function(context, options) { options.data.buffer.push(" derp"); }, layout: function(context, options) { options.data.buffer.push("Herp"); get(options.data.view, 'template')(context, options); } }); run(function() { view.createElement(); }); equal("Herp derp", view.$().text(), "the layout has access to the template"); }); }); define("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.'); }); }); define("ember-views/tests/views/view/nearest_of_type_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/mixin","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var EmberMixin = __dependency4__.Mixin; var View = __dependency5__["default"]; var parentView, view; QUnit.module("View#nearest*", { teardown: function() { run(function() { if (parentView) { parentView.destroy(); } if (view) { view.destroy(); } }); } }); (function() { var Mixin = EmberMixin.create({}), Parent = View.extend(Mixin, { render: function(buffer) { this.appendChild( View.create() ); } }); test("nearestOfType should find the closest view by view class", function() { var child; run(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"); }); test("nearestOfType should find the closest view by mixin", function() { var child; run(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"); }); test("nearestWithProperty should search immediate parent", function() { var childView; view = View.create({ myProp: true, render: function(buffer) { this.appendChild(View.create()); } }); run(function() { view.appendTo('#qunit-fixture'); }); childView = view.get('childViews')[0]; equal(childView.nearestWithProperty('myProp'), view); }); }()); }); define("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.'); }); }); define("ember-views/tests/views/view/parse_property_path_test", ["ember-views/views/view"], function(__dependency1__) { "use strict"; var EmberView = __dependency1__["default"]; QUnit.module("EmberView - _parsePropertyPath"); test("it works with a simple property path", function() { var parsed = EmberView._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"); }); test("it works with a more complex property path", function() { var parsed = EmberView._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"); }); test("className is extracted", function() { var parsed = EmberView._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"); }); test("falsyClassName is extracted", function() { var parsed = EmberView._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"); }); test("it works with an empty true class", function() { var parsed = EmberView._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"); }); }); define("ember-views/tests/views/view/parse_property_path_test.jshint", [], function() { "use strict"; module('JSHint - ember-views/tests/views/view'); test('ember-views/tests/views/view/parse_property_path_test.js should pass jshint', function() { ok(true, 'ember-views/tests/views/view/parse_property_path_test.js should pass jshint.'); }); }); define("ember-views/tests/views/view/remove_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/enumerable_utils","ember-views/system/jquery","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var get = __dependency1__.get; var set = __dependency2__.set; var run = __dependency3__["default"]; var indexOf = __dependency4__.indexOf; var jQuery = __dependency5__["default"]; var View = __dependency6__["default"]; var ContainerView = __dependency7__["default"]; // ....................................................... // removeChild() // var parentView, child; QUnit.module("View#removeChild", { setup: function() { parentView = ContainerView.create({ childViews: [View] }); child = get(parentView, 'childViews').objectAt(0); }, teardown: function() { run(function() { parentView.destroy(); child.destroy(); }); } }); test("returns receiver", function() { equal(parentView.removeChild(child), parentView, 'receiver'); }); test("removes child from parent.childViews array", function() { ok(indexOf(get(parentView, 'childViews'), child)>=0, 'precond - has child in childViews array before remove'); parentView.removeChild(child); ok(indexOf(get(parentView, 'childViews'), child)<0, 'removed child'); }); test("sets parentView property to null", function() { ok(get(child, 'parentView'), 'precond - has parentView'); parentView.removeChild(child); ok(!get(child, 'parentView'), 'parentView is now null'); }); // ....................................................... // removeAllChildren() // var view, childViews; QUnit.module("View#removeAllChildren", { setup: function() { view = ContainerView.create({ childViews: [View, View, View] }); childViews = view.get('childViews'); }, teardown: function() { run(function() { childViews.forEach(function(v) { v.destroy(); }); view.destroy(); }); } }); test("removes all child views", function() { equal(get(view, 'childViews.length'), 3, 'precond - has child views'); view.removeAllChildren(); equal(get(view, 'childViews.length'), 0, 'removed all children'); }); test("returns receiver", function() { equal(view.removeAllChildren(), view, 'receiver'); }); // ....................................................... // removeFromParent() // QUnit.module("View#removeFromParent", { teardown: function() { run(function() { if (parentView) { parentView.destroy(); } if (child) { child.destroy(); } if (view) { view.destroy(); } }); } }); test("removes view from parent view", function() { parentView = ContainerView.create({ childViews: [View] }); child = get(parentView, 'childViews').objectAt(0); ok(get(child, 'parentView'), 'precond - has parentView'); run(function() { parentView.createElement(); }); ok(parentView.$('div').length, "precond - has a child DOM element"); run(function() { child.removeFromParent(); }); ok(!get(child, 'parentView'), 'no longer has parentView'); ok(indexOf(get(parentView, 'childViews'), child)<0, 'no longer in parent childViews'); equal(parentView.$('div').length, 0, "removes DOM element from parent"); }); test("returns receiver", function() { parentView = ContainerView.create({ childViews: [View] }); child = get(parentView, 'childViews').objectAt(0); var removed = run(function() { return child.removeFromParent(); }); equal(removed, child, 'receiver'); }); test("does nothing if not in parentView", function() { var callCount = 0; child = View.create(); // monkey patch for testing... ok(!get(child, 'parentView'), 'precond - has no parent'); child.removeFromParent(); run(function() { child.destroy(); }); }); test("the DOM element is gone after doing append and remove in two separate runloops", function() { view = View.create(); run(function() { view.append(); }); run(function() { view.remove(); }); var viewElem = jQuery('#'+get(view, 'elementId')); ok(viewElem.length === 0, "view's element doesn't exist in DOM"); }); test("the DOM element is gone after doing append and remove in a single runloop", function() { view = View.create(); run(function() { view.append(); view.remove(); }); var viewElem = jQuery('#'+get(view, 'elementId')); ok(viewElem.length === 0, "view's element doesn't exist in DOM"); }); }); define("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.'); }); }); define("ember-views/tests/views/view/render_test", ["ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/object","ember-views/system/jquery","ember-views/views/view","ember-views/views/container_view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var jQuery = __dependency4__["default"]; var EmberView = __dependency5__["default"]; var ContainerView = __dependency6__["default"]; var view; // ....................................................... // render() // QUnit.module("EmberView#render", { teardown: function() { run(function() { view.destroy(); }); } }); test("default implementation does not render child views", function() { var rendered = 0, updated = 0, parentRendered = 0, parentUpdated = 0 ; view = ContainerView.createWithMixins({ childViews: ["child"], render: function(buffer) { parentRendered++; this._super(buffer); }, child: EmberView.createWithMixins({ render: function(buffer) { rendered++; this._super(buffer); } }) }); run(function() { view.createElement(); }); equal(rendered, 1, 'rendered the child once'); equal(parentRendered, 1); equal(view.$('div').length, 1); }); test("should invoke renderChildViews if layer is destroyed then re-rendered", function() { var rendered = 0, parentRendered = 0, parentUpdated = 0 ; view = ContainerView.createWithMixins({ childViews: ["child"], render: function(buffer) { parentRendered++; this._super(buffer); }, child: EmberView.createWithMixins({ render: function(buffer) { rendered++; this._super(buffer); } }) }); run(function() { view.append(); }); equal(rendered, 1, 'rendered the child once'); equal(parentRendered, 1); equal(view.$('div').length, 1); run(function() { view.rerender(); }); equal(rendered, 2, 'rendered the child twice'); equal(parentRendered, 2); equal(view.$('div').length, 1); run(function() { view.destroy(); }); }); test("should render child views with a different tagName", function() { var rendered = 0, parentRendered = 0, parentUpdated = 0 ; view = ContainerView.create({ childViews: ["child"], child: EmberView.create({ tagName: 'aside' }) }); run(function() { view.createElement(); }); equal(view.$('aside').length, 1); }); test("should add ember-view to views", function() { view = EmberView.create(); run(function() { view.createElement(); }); ok(view.$().hasClass('ember-view'), "the view has ember-view"); }); test("should allow hX tags as tagName", function() { view = ContainerView.create({ childViews: ["child"], child: EmberView.create({ tagName: 'h3' }) }); run(function() { view.createElement(); }); ok(view.$('h3').length, "does not render the h3 tag correctly"); }); test("should not add role attribute unless one is specified", function() { view = EmberView.create(); run(function() { view.createElement(); }); ok(view.$().attr('role') === undefined, "does not have a role attribute"); }); test("should re-render if the context is changed", function() { view = EmberView.create({ elementId: 'template-context-test', context: { foo: "bar" }, render: function(buffer) { var value = get(get(this, 'context'), 'foo'); buffer.push(value); } }); run(function() { view.appendTo('#qunit-fixture'); }); equal(jQuery('#qunit-fixture #template-context-test').text(), "bar", "precond - renders the view with the initial value"); run(function() { view.set('context', { foo: "bang baz" }); }); equal(jQuery('#qunit-fixture #template-context-test').text(), "bang baz", "re-renders the view with the updated context"); }); }); define("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.'); }); }); define("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(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var get = __dependency1__.get; var run = __dependency2__["default"]; var jQuery = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var ContainerView = __dependency5__["default"]; var View, view, willDestroyCalled, childView; QUnit.module("EmberView - replaceIn()", { setup: function() { View = EmberView.extend({}); }, teardown: function() { run(function() { view.destroy(); }); } }); test("should be added to the specified element when calling replaceIn()", function() { jQuery("#qunit-fixture").html(''); view = View.create(); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.replaceIn('#menu'); }); var viewElem = jQuery('#menu').children(); ok(viewElem.length > 0, "creates and replaces the view's element"); }); test("raises an assert when a target does not exist in the DOM", function() { view = View.create(); expectAssertion(function() { run(function() { view.replaceIn('made-up-target'); }); }); }); test("should remove previous elements when calling replaceIn()", function() { jQuery("#qunit-fixture").html(''); var viewElem = jQuery('#menu').children(); view = View.create(); ok(viewElem.length === 1, "should have one element"); run(function() { view.replaceIn('#menu'); }); ok(viewElem.length === 1, "should have one element"); }); test("should move the view to the inDOM state after replacing", function() { jQuery("#qunit-fixture").html(''); view = View.create(); run(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() { View = ContainerView.extend({ childViews: ['child'], child: EmberView.extend({ elementId: 'child' }) }); }, teardown: function() { run(function() { view.destroy(); }); } }); test("should be added to the specified element when calling replaceIn()", function() { jQuery("#qunit-fixture").html(''); view = View.create(); ok(!get(view, 'element'), "precond - should not have an element"); run(function() { view.replaceIn('#menu'); }); var viewElem = jQuery('#menu #child'); ok(viewElem.length > 0, "creates and replaces the view's element"); }); }); define("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.'); }); }); define("ember-views/tests/views/view/state_deprecation_test", ["ember-metal/platform","ember-metal/run_loop","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var platform = __dependency1__.platform; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var view; QUnit.module("views/view/state_deprecation", { teardown: function() { if (view) { run(view, 'destroy'); } } }); if (platform.hasPropertyAccessors) { test("view.state should be an alias of view._state with a deprecation", function() { expect(2); view = EmberView.create(); expectDeprecation(function() { equal(view._state, view.state, '_state and state are aliased'); }, 'Usage of `state` is deprecated, use `_state` instead.'); }); test("view.states should be an alias of view._states with a deprecation", function() { expect(2); view = EmberView.create(); expectDeprecation(function() { equal(view._states, view.states, '_states and states are aliased'); }, 'Usage of `states` is deprecated, use `_states` instead.'); }); } test("no deprecation is printed if view.state or view._state is not looked up", function() { expect(1); expectNoDeprecation(); EmberView.create(); }); }); define("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.'); }); }); define("ember-views/tests/views/view/template_test", ["container","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/object","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Container = __dependency1__["default"]; var get = __dependency2__.get; var run = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var EmberView = __dependency5__["default"]; var container, view; QUnit.module("EmberView - Template Functionality", { setup: function() { container = new Container(); container.optionsForType('template', { instantiate: false }); }, teardown: function() { run(function() { if (view) { view.destroy(); } }); } }); test("should call the function of the associated template", function() { container.register('template:testTemplate', function() { return "

    template was called

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

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

    "; }); view = EmberView.create({ container: container, templateName: 'testTemplate', context: { personName: "Tom DAAAALE" } }); run(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"); }); test("should fall back to defaultTemplate if neither template nor templateName are provided", function() { var View; View = EmberView.extend({ defaultTemplate: function(dataSource) { return "

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

    "; } }); view = View.create({ context: { personName: "Tom DAAAALE" } }); run(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"); }); test("should not use defaultTemplate if template is provided", function() { var View; View = EmberView.extend({ template: function() { return "foo"; }, defaultTemplate: function(dataSource) { return "

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

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

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

    "; } }); view = View.create(); run(function() { view.createElement(); }); equal("foo", view.$().text(), "default template was not printed"); }); test("should render an empty element if no template is specified", function() { view = EmberView.create(); run(function() { view.createElement(); }); equal(view.$().html(), '', "view div should be empty"); }); test("should provide a controller to the template if a controller is specified on the view", function() { expect(7); var Controller1 = EmberObject.extend({ toString: function() { return "Controller1"; } }); var Controller2 = EmberObject.extend({ toString: function() { return "Controller2"; } }); var controller1 = Controller1.create(), controller2 = Controller2.create(), optionsDataKeywordsControllerForView, optionsDataKeywordsControllerForChildView, contextForView, contextForControllerlessView; view = EmberView.create({ controller: controller1, template: function(buffer, options) { optionsDataKeywordsControllerForView = options.data.keywords.controller; } }); run(function() { view.appendTo('#qunit-fixture'); }); strictEqual(optionsDataKeywordsControllerForView, controller1, "passes the controller in the data"); run(function() { view.destroy(); }); var parentView = EmberView.create({ controller: controller1, template: function(buffer, options) { options.data.view.appendChild(EmberView.create({ controller: controller2, templateData: options.data, template: function(context, options) { contextForView = context; optionsDataKeywordsControllerForChildView = options.data.keywords.controller; } })); optionsDataKeywordsControllerForView = options.data.keywords.controller; } }); run(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(function() { parentView.destroy(); }); var parentViewWithControllerlessChild = EmberView.create({ controller: controller1, template: function(buffer, options) { options.data.view.appendChild(EmberView.create({ templateData: options.data, template: function(context, options) { contextForControllerlessView = context; optionsDataKeywordsControllerForChildView = options.data.keywords.controller; } })); optionsDataKeywordsControllerForView = options.data.keywords.controller; } }); run(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(function() { parentView.destroy(); parentViewWithControllerlessChild.destroy(); }); }); test("should throw an assertion if no container has been set", function() { expect(1); var View; View = EmberView.extend({ templateName: 'foobar', }); raises(function() { view = View.create(); run(function() { view.createElement(); }); }, /Container was not found when looking up a views template./); }); }); define("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.'); }); }); define("ember-views/tests/views/view/transition_to_deprecation_test", ["ember-metal/platform","ember-views/views/view","ember-metal/run_loop"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var platform = __dependency1__.platform; var EmberView = __dependency2__["default"]; var run = __dependency3__["default"]; var view; QUnit.module('views/view/transition_to_deprecation', { setup: function() { view = EmberView.create(); }, teardown: function() { run(view, 'destroy'); } }); test('deprecates when calling transitionTo', function() { expect(1); view = EmberView.create(); expectDeprecation(function() { view.transitionTo('preRender'); }, ''); }); test("doesn't deprecafte when calling _transitionTo", function() { expect(1); view = EmberView.create(); view._transitionTo('preRender'); ok(true); }); }); define("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.'); }); }); define("ember-views/tests/views/view/view_lifecycle_test", ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/object","ember-views/system/jquery","ember-views/views/view"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var Ember = __dependency1__["default"]; var get = __dependency2__.get; var run = __dependency3__["default"]; var EmberObject = __dependency4__["default"]; var jQuery = __dependency5__["default"]; var EmberView = __dependency6__["default"]; var originalLookup = Ember.lookup, lookup, view; QUnit.module("views/view/view_lifecycle_test - pre-render", { setup: function() { Ember.lookup = lookup = {}; }, teardown: function() { if (view) { run(function() { view.destroy(); }); } Ember.lookup = originalLookup; } }); function tmpl(str) { return function(context, options) { options.data.buffer.push(str); }; } test("should create and append a DOM element after bindings have synced", function() { var ViewTest; lookup.ViewTest = ViewTest = {}; run(function() { ViewTest.fakeController = EmberObject.create({ fakeThing: 'controllerPropertyValue' }); view = EmberView.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"); }); test("should throw an exception if trying to append a child before rendering has begun", function() { run(function() { view = EmberView.create(); }); raises(function() { view.appendChild(EmberView, {}); }, null, "throws an error when calling appendChild()"); }); test("should not affect rendering if rerender is called before initial render happens", function() { run(function() { view = EmberView.create({ template: tmpl("Rerender me!") }); view.rerender(); view.append(); }); equal(view.$().text(), "Rerender me!", "renders correctly if rerender is called first"); }); test("should not affect rendering if destroyElement is called before initial render happens", function() { run(function() { view = EmberView.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(function() { view.destroy(); }); } } }); test("appendChild should work inside a template", function() { run(function() { view = EmberView.create({ template: function(context, options) { var buffer = options.data.buffer; buffer.push("

    Hi!

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

    Hi

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

    Virtual

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

    Bye!

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

    Hi

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

    Virtual

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

    Bye!

    "); } }); run(function() { jQuery("#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(function() { childView.removeFromParent(); }); equal(virtualView.get('childViews.length'), 0, "has no childView"); equal(rootView.get('childViews.length'), 0, "has no childView"); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("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.'); }); }); define("ember-views/views/view_collection.jshint", [], function() { "use strict"; module('JSHint - ember-views/views'); test('ember-views/views/view_collection.js should pass jshint', function() { ok(true, 'ember-views/views/view_collection.js should pass jshint.'); }); }); define("ember.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember.js should pass jshint', function() { ok(true, 'ember.js should pass jshint.'); }); }); define("ember/tests/application_lifecycle", ["ember"], function(__dependency1__) { "use strict"; var App, container, router; QUnit.module("Application Lifecycle", { setup: function() { Ember.run(function() { App = Ember.Application.create({ rootElement: '#qunit-fixture' }); 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; }); }); } 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); } }); var router = 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); }); 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); } }); var router = 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); }); }); define("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.'); }); }); define("ember/tests/component_registration_test", ["ember"], function(__dependency1__) { "use strict"; var App, container; var compile = Ember.Handlebars.compile; var originalHelpers; function prepare(){ Ember.TEMPLATES["components/expand-it"] = compile("

    hello {{yield}}

    "); Ember.TEMPLATES.application = compile("Hello world {{#expand-it}}world{{/expand-it}}"); originalHelpers = Ember.A(Ember.keys(Ember.Handlebars.helpers)); } function cleanup(){ Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; cleanupHandlebarsHelpers(); }); } function cleanupHandlebarsHelpers(){ var currentHelpers = Ember.A(Ember.keys(Ember.Handlebars.helpers)); currentHelpers.forEach(function(name){ if (!originalHelpers.contains(name)) { delete Ember.Handlebars.helpers[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' }); container = App.__container__; if (callback) { callback(); } }); var router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); Ember.run(function() { router.handleURL('/'); }); } 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"); }); test("If a component is registered, it is used", function() { boot(function() { container.register('component:expand-it', Ember.Component.extend({ classNames: 'testing123' })); }); equal(Ember.$('div.testing123', '#qunit-fixture').text(), "hello world", "The component is composed correctly"); }); test("Late-registered components can be rendered with custom `template` property (DEPRECATED)", function() { Ember.TEMPLATES.application = compile("
    there goes {{my-hero}}
    "); expectDeprecation(/Do not specify template on a Component/); boot(function() { container.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(!Ember.Handlebars.helpers['my-hero'], "Component wasn't saved to global Handlebars.helpers hash"); }); test("Late-registered components can be rendered with template registered on the container", function() { Ember.TEMPLATES.application = compile("
    hello world {{sally-rutherford}}-{{#sally-rutherford}}!!!{{/sally-rutherford}}
    "); boot(function() { container.register('template:components/sally-rutherford', compile("funkytowny{{yield}}")); container.register('component:sally-rutherford', Ember.Component); }); equal(Ember.$('#wrapper').text(), "hello world funkytowny-funkytowny!!!", "The component is composed correctly"); ok(!Ember.Handlebars.helpers['sally-rutherford'], "Component wasn't saved to global Handlebars.helpers hash"); }); test("Late-registered components can be rendered with ONLY the template registered on the container", function() { Ember.TEMPLATES.application = compile("
    hello world {{borf-snorlax}}-{{#borf-snorlax}}!!!{{/borf-snorlax}}
    "); boot(function() { container.register('template:components/borf-snorlax', compile("goodfreakingTIMES{{yield}}")); }); equal(Ember.$('#wrapper').text(), "hello world goodfreakingTIMES-goodfreakingTIMES!!!", "The component is composed correctly"); ok(!Ember.Handlebars.helpers['borf-snorlax'], "Component wasn't saved to global Handlebars.helpers hash"); }); test("Component-like invocations are treated as bound paths if neither template nor component are registered on the container", function() { Ember.TEMPLATES.application = compile("
    {{user-name}} hello {{api-key}} world
    "); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'user-name': 'machty' })); }); equal(Ember.$('#wrapper').text(), "machty hello world", "The component is composed correctly"); }); test("Component lookups should take place on components' subcontainers", function() { expect(1); Ember.TEMPLATES.application = compile("
    {{#sally-rutherford}}{{mach-ty}}{{/sally-rutherford}}
    "); boot(function() { container.register('component:sally-rutherford', Ember.Component.extend({ init: function() { this._super(); this.container = new Ember.Container(this.container); this.container.register('component:mach-ty', Ember.Component.extend({ didInsertElement: function() { ok(true, "mach-ty was rendered"); } })); } })); }); }); test("Assigning templateName to a component should setup the template as a layout (DEPRECATED)", function(){ expect(2); Ember.TEMPLATES.application = compile("
    {{#my-component}}{{text}}{{/my-component}}
    "); Ember.TEMPLATES['foo-bar-baz'] = compile("{{text}}-{{yield}}"); expectDeprecation(/Do not specify templateName on a Component/); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); container.register('component:my-component', Ember.Component.extend({ text: 'inner', templateName: 'foo-bar-baz' })); }); equal(Ember.$('#wrapper').text(), "inner-outer", "The component is composed correctly"); }); test("Assigning templateName and layoutName should use the templates specified", function(){ expect(1); Ember.TEMPLATES.application = compile("
    {{my-component}}
    "); Ember.TEMPLATES['foo'] = compile("{{text}}"); Ember.TEMPLATES['bar'] = compile("{{text}}-{{yield}}"); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); container.register('component:my-component', Ember.Component.extend({ text: 'inner', layoutName: 'bar', templateName: 'foo' })); }); equal(Ember.$('#wrapper').text(), "inner-outer", "The component is composed correctly"); }); QUnit.module("Application Lifecycle - Component Context", { setup: prepare, teardown: cleanup }); test("Components with a block should have the proper content when a template is provided", function(){ Ember.TEMPLATES.application = compile("
    {{#my-component}}{{text}}{{/my-component}}
    "); Ember.TEMPLATES['components/my-component'] = compile("{{text}}-{{yield}}"); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); container.register('component:my-component', Ember.Component.extend({ text: 'inner' })); }); equal(Ember.$('#wrapper').text(), "inner-outer", "The component is composed correctly"); }); test("Components with a block should yield the proper content without a template provided", function(){ Ember.TEMPLATES.application = compile("
    {{#my-component}}{{text}}{{/my-component}}
    "); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); container.register('component:my-component', Ember.Component.extend({ text: 'inner' })); }); equal(Ember.$('#wrapper').text(), "outer", "The component is composed correctly"); }); test("Components without a block should have the proper content when a template is provided", function(){ Ember.TEMPLATES.application = compile("
    {{my-component}}
    "); Ember.TEMPLATES['components/my-component'] = compile("{{text}}"); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); container.register('component:my-component', Ember.Component.extend({ text: 'inner' })); }); equal(Ember.$('#wrapper').text(), "inner", "The component is composed correctly"); }); test("Components without a block should have the proper content", function(){ Ember.TEMPLATES.application = compile("
    {{my-component}}
    "); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer' })); container.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"); }); test("properties of a component without a template should not collide with internal structures", function(){ Ember.TEMPLATES.application = compile("
    {{my-component data=foo}}
    "); boot(function() { container.register('controller:application', Ember.Controller.extend({ 'text': 'outer', 'foo': 'Some text inserted by jQuery' })); container.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"); }); test("Components trigger actions in the parents context when called from within a block", function(){ Ember.TEMPLATES.application = compile("
    {{#my-component}}Fizzbuzz{{/my-component}}
    "); boot(function() { container.register('controller:application', Ember.Controller.extend({ actions: { fizzbuzz: function(){ ok(true, 'action triggered on parent'); } } })); container.register('component:my-component', Ember.Component.extend()); }); Ember.run(function(){ Ember.$('#fizzbuzz', "#wrapper").click(); }); }); test("Components trigger actions in the components context when called from within its template", function(){ Ember.TEMPLATES.application = compile("
    {{#my-component}}{{text}}{{/my-component}}
    "); Ember.TEMPLATES['components/my-component'] = compile("Fizzbuzz"); boot(function() { container.register('controller:application', Ember.Controller.extend({ actions: { fizzbuzz: function(){ ok(false, 'action triggered on the wrong context'); } } })); container.register('component:my-component', Ember.Component.extend({ actions: { fizzbuzz: function(){ ok(true, 'action triggered on component'); } } })); }); Ember.$('#fizzbuzz', "#wrapper").click(); }); }); define("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.'); }); }); define("ember/tests/global-api-test", ["ember"], function(__dependency1__) { "use strict"; QUnit.module("Global API Tests"); function confirmExport(property) { test('confirm ' + property + ' is exported', function() { ok(Ember.get(window, property) + ' is exported propertly'); }); } confirmExport('Ember.DefaultResolver'); confirmExport('Ember.generateController'); }); define("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.'); }); }); define("ember/tests/helpers/helper_registration_test", ["ember"], function(__dependency1__) { "use strict"; var App, container; var compile = Ember.Handlebars.compile; 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' }); container = App.__container__; if (callback) { callback(); } }); var router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); Ember.run(function() { router.handleURL('/'); }); }; test("Unbound dashed helpers registered on the container can be late-invoked", function() { Ember.TEMPLATES.application = compile("
    {{x-borf}} {{x-borf YES}}
    "); boot(function() { container.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(!Ember.Handlebars.helpers['x-borf'], "Container-registered helper doesn't wind up on global helpers hash"); }); test("Bound helpers registered on the container can be late-invoked", function() { Ember.TEMPLATES.application = compile("
    {{x-reverse}} {{x-reverse foo}}
    "); boot(function() { container.register('controller:application', Ember.Controller.extend({ foo: "alex" })); container.register('helper:x-reverse', Ember.Handlebars.makeBoundHelper(reverseHelper)); }); equal(Ember.$('#wrapper').text(), "-- xela", "The bound helper was invoked from the container"); ok(!Ember.Handlebars.helpers['x-reverse'], "Container-registered helper doesn't wind up on global helpers hash"); }); test("Undashed helpers registered on the container can not (presently) be invoked", function() { var realHelperMissing = Ember.Handlebars.helpers.helperMissing; Ember.Handlebars.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() { container.register('helper:omg', function() { return "OMG"; }); container.register('helper:yorp', Ember.Handlebars.makeBoundHelper(function() { return "YORP"; })); }); equal(Ember.$('#wrapper').text(), "|NOHALPER||NOHALPER", "The undashed helper was invoked from the container"); Ember.Handlebars.helpers.helperMissing = realHelperMissing; }); }); define("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.'); }); }); define("ember/tests/helpers/link_to_test", ["ember"], function(__dependency1__) { "use strict"; var Router, App, AppView, templates, router, eventDispatcher, container; var get = Ember.get, set = Ember.set, map = Ember.ArrayPolyfills.map; function bootApplication() { router = container.lookup('router:main'); Ember.run(App, 'advanceReadiness'); } // IE includes the host name function normalizeUrl(url) { return url.replace(/https?:\/\/[^\/]+/,''); } function compile(template) { return Ember.Handlebars.compile(template); } 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; 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 = Ember.Handlebars.compile("{{outlet}}"); Ember.TEMPLATES.index = Ember.Handlebars.compile("

    Home

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

    About

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

    Item

    {{name}}

    {{#link-to 'index' id='home-link'}}Home{{/link-to}}"); AppView = Ember.View.extend({ templateName: 'app' }); container.register('view:app', AppView); container.register('router:main', Router); }); }, teardown: sharedTeardown }); 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"); }); test("The {{link-to}} helper supports URL replacement", function() { Ember.TEMPLATES.index = Ember.Handlebars.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'); }); test("the {{link-to}} helper doesn't add an href when the tagName isn't 'a'", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("the {{link-to}} applies a 'disabled' class when disabled", function () { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("the {{link-to}} doesn't apply a 'disabled' class if disabledWhen is not provided", function () { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("the {{link-to}} helper supports a custom disabledClass", function () { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("the {{link-to}} helper does not respond to clicks when disabled", function () { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("The {{link-to}} helper supports a custom activeClass", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); 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'); }); test("The {{link-to}} helper supports custom, nested, currentWhen", function() { Router.map(function(match) { this.resource("index", { path: "/" }, function() { this.route("about"); }); this.route("item"); }); Ember.TEMPLATES.index = Ember.Handlebars.compile("

    Home

    {{outlet}}"); Ember.TEMPLATES['index/about'] = Ember.Handlebars.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 currentWhen is a parent route"); }); test("The {{link-to}} helper does not disregard currentWhen 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 = Ember.Handlebars.compile("

    Home

    {{outlet}}"); Ember.TEMPLATES['index/about'] = Ember.Handlebars.compile("{{#link-to 'items' 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 when currentWhen is given for explicitly for a resource"); }); test("The {{link-to}} helper defaults to bubbling", function() { Ember.TEMPLATES.about = Ember.Handlebars.compile("
    {{#link-to 'about.contact' id='about-contact'}}About{{/link-to}}
    {{outlet}}"); Ember.TEMPLATES['about/contact'] = Ember.Handlebars.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"); }); test("The {{link-to}} helper supports bubbles=false", function() { Ember.TEMPLATES.about = Ember.Handlebars.compile("
    {{#link-to 'about.contact' id='about-contact' bubbles=false}}About{{/link-to}}
    {{outlet}}"); Ember.TEMPLATES['about/contact'] = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.compile("

    List

      {{#each controller}}
    • {{#link-to 'item' this}}{{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"); }); test("The {{link-to}} helper binds some anchor html tag common attributes", function() { Ember.TEMPLATES.index = Ember.Handlebars.compile("

    Home

    {{#link-to 'index' id='self-link' title='title-attr' rel='rel-attr'}}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"); }); 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"); }); 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'); }); test("The {{link-to}} helper unwraps controllers", function() { expect(5); 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('

    {{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'); }); 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 = Ember.Handlebars.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"); }); test("Quoteless route param performs property lookup", function() { Ember.TEMPLATES.index = Ember.Handlebars.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'), view = Ember.View.views['index-view']; Ember.run(function() { controller.set('foo', 'about'); view.set('foo', 'about'); }); assertEquality('/about'); }); test("link-to with null/undefined dynamic parameters are put in a loading state", function() { expect(19); var oldWarn = Ember.Logger.warn, warnCalled = false; Ember.Logger.warn = function() { warnCalled = true; }; Ember.TEMPLATES.index = Ember.Handlebars.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'), $staticLink = Ember.$('#static-link', '#qunit-fixture'), 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; }); 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'}), 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'); }); test("The {{link-to}} helper's bound parameter functionality works as expected in conjunction with an ObjectProxy/Controller", function() { Router.map(function() { this.route('post', { path: '/posts/:post_id' }); }); var post = Ember.Object.create({id: '1'}), 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); var linkView = Ember.View.views['self-link']; equal(normalizeUrl($link.attr('href')), '/posts/2', 'self link updated to post 2'); }); test("{{linkTo}} is aliased", function() { var originalWarn = Ember.warn; Ember.warn = function(msg) { equal(msg, "The 'linkTo' view helper is deprecated in favor of 'link-to'", 'Warning called'); }; Ember.TEMPLATES.index = Ember.Handlebars.compile("

    Home

    {{#linkTo 'about' id='about-link' replace=true}}About{{/linkTo}}"); Router.map(function() { this.route("about"); }); bootApplication(); 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'); Ember.warn = originalWarn; }); 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"); }); 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}}') }; bootApplication(); 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"]); }); 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 = Ember.Handlebars.compile("

    Home

    {{link-to 'Contact us' 'contact' id='contact-link'}}{{#link-to 'index' id='self-link'}}Self{{/link-to}}"); Ember.TEMPLATES.contact = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.compile("

    Home

    {{link-to contactName 'contact' id='contact-link'}}{{#link-to 'index' id='self-link'}}Self{{/link-to}}"); Ember.TEMPLATES.contact = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.compile("

    Home

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

    Item

    {{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"); }); test("The non-block form {{link-to}} performs property lookup", function() { Ember.TEMPLATES.index = Ember.Handlebars.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'), view = Ember.View.views['index-view']; Ember.run(function() { controller.set('foo', 'about'); view.set('foo', 'about'); }); assertEquality('/about'); }); test("The non-block form {{link-to}} protects against XSS", function() { Ember.TEMPLATES.application = Ember.Handlebars.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); }); 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"); }); test("the {{link-to}} helper does not call preventDefault if `preventDefault=false` is passed as an option", function(){ Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("the {{link-to}} helper does not throw an error if its route has exited", function(){ expect(0); Ember.TEMPLATES.application = Ember.Handlebars.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(); }); }); test("{{link-to}} active property respects changing parent route context", function() { Ember.TEMPLATES.application = Ember.Handlebars.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'); }); 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 = Ember.Handlebars.compile("{{#link-to 'index' id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/", "link has right href"); }); 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 = Ember.Handlebars.compile("{{#link-to 'index' (query-params) id='the-link'}}Index{{/link-to}}"); bootApplication(); equal(Ember.$('#the-link').attr('href'), "/", "link has right href"); }); test("{{link-to}} populates href with supplied query param values", function() { App.IndexController = Ember.Controller.extend({ queryParams: ['foo'], foo: '123' }); Ember.TEMPLATES.index = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.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' }); container.register('router:main', Router); }); }, teardown: sharedTeardown }); test("doesn't update controller QP properties on current route when invoked", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("doesn't update controller QP properties on current route when invoked (empty query-params obj)", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("link-to with no params throws", function() { Ember.TEMPLATES.index = Ember.Handlebars.compile("{{#link-to id='the-link'}}Index{{/link-to}}"); expectAssertion(function() { bootApplication(); }, /one or more/); }); test("doesn't update controller QP properties on current route when invoked (empty query-params obj, inferred route)", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("updates controller QP properties on current route when invoked", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("updates controller QP properties on current route when invoked (inferred route)", function() { Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("updates controller QP properties on other route after transitioning to that route", function() { Router.map(function() { this.route('about'); }); Ember.TEMPLATES.index = Ember.Handlebars.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"); }); test("supplied QP properties can be bound", function() { var indexController = container.lookup('controller:index'); Ember.TEMPLATES.index = Ember.Handlebars.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'); }); test("supplied QP properties can be bound (booleans)", function() { var indexController = container.lookup('controller:index'); Ember.TEMPLATES.index = Ember.Handlebars.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 }); }); test("href updates when unsupplied controller QP props change", function() { var indexController = container.lookup('controller:index'); Ember.TEMPLATES.index = Ember.Handlebars.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'); }); test("The {{link-to}} applies activeClass when query params are not changed", function() { Ember.TEMPLATES.index = Ember.Handlebars.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 = Ember.Handlebars.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'] = Ember.Handlebars.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'); }); test("The {{link-to}} applies active class when query-param is number", function() { Ember.TEMPLATES.index = Ember.Handlebars.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'); }); test("The {{link-to}} applies active class when query-param is array", function() { Ember.TEMPLATES.index = Ember.Handlebars.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'); }); 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 = Ember.Handlebars.compile( "{{#link-to 'parent' id='parent-link'}}Parent{{/link-to}} " + "{{#link-to 'parent.child' id='parent-child-link'}}Child{{/link-to}} " + "{{outlet}}" ); App.ParentChildController = Ember.ObjectController.extend({ queryParams: ['foo'], foo: 'bar' }); bootApplication(); shouldNotBeActive('#parent-link'); shouldNotBeActive('#parent-child-link'); Ember.run(router, 'handleURL', '/parent/child?foo=dog'); shouldBeActive('#parent-link'); }); test("The {{link-to}} helper disregards query-params in activeness computation when currentWhen specified", function() { App.Router.map(function() { this.route('parent'); }); Ember.TEMPLATES.application = Ember.Handlebars.compile( "{{#link-to 'parent' (query-params page=1) currentWhen='parent' id='app-link'}}Parent{{/link-to}} {{outlet}}"); Ember.TEMPLATES.parent = Ember.Handlebars.compile( "{{#link-to 'parent' (query-params page=1) currentWhen='parent' id='parent-link'}}Parent{{/link-to}} {{outlet}}"); App.ParentController = Ember.ObjectController.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 = Ember.Handlebars.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; QUnit.module("The {{link-to}} helper: eager URL updating", { setup: function() { Ember.run(function() { sharedSetup(); container.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 = Ember.Handlebars.compile("{{outlet}}{{link-to 'Index' 'index' id='index-link'}}{{link-to 'About' 'about' id='about-link'}}"); }); }, teardown: function() { sharedTeardown(); aboutDefer = null; } }); test("invoking a link-to with a slow promise eager updates url", function() { basicEagerURLUpdateTest(false); }); 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); } }); container.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'); }); test("non `a` tags also eagerly update URL", function() { basicEagerURLUpdateTest(true); }); 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'); }); 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'); }); }); define("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.'); }); }); define("ember/tests/homepage_example_test", ["ember"], function(__dependency1__) { "use strict"; var App, $fixture; function setupExample() { // setup templates Ember.TEMPLATES.application = Ember.Handlebars.compile("{{outlet}}"); Ember.TEMPLATES.index = Ember.Handlebars.compile("

    People

      {{#each model}}
    • Hello, {{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 = {}; } }); 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!'); }); }); define("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.'); }); }); define("ember/tests/location_test", ["ember","ember-runtime/copy"], function(__dependency1__, __dependency2__) { "use strict"; var copy = __dependency2__["default"]; var App, AutoTestLocation; QUnit.module('AutoLocation', { setup: function() { AutoTestLocation = copy(Ember.AutoLocation); AutoTestLocation._getSupportsHistory = function () { return true; }; AutoTestLocation._location = { href: 'http://test.com/', pathname: '/rootdir/subdir', hash: '', search: '', replace: function () { ok(false, 'location.replace should not be called'); } }; Ember.run(function() { App = Ember.Application.create({ name: 'App', rootElement: '#qunit-fixture' }); App.Router.reopen({ location: 'none', rootURL: '/rootdir/' }); App.__container__.register('location:auto', AutoTestLocation ); App.deferReadiness(); }); }, teardown: function() { Ember.run(function() { App.destroy(); App = null; Ember.TEMPLATES = {}; }); } }); test('has the rootURL from the main router', function() { Ember.run(App, 'advanceReadiness'); var location = App.__container__.lookup('location:auto'); equal(Ember.get(location, 'rootURL'), '/rootdir/'); }); }); define("ember/tests/location_test.jshint", [], function() { "use strict"; module('JSHint - ember/tests'); test('ember/tests/location_test.js should pass jshint', function() { ok(true, 'ember/tests/location_test.js should pass jshint.'); }); }); define("ember/tests/routing/basic_test", ["ember","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var forEach = __dependency2__.forEach; var get = __dependency3__.get; var set = __dependency4__.set; var Router, App, AppView, templates, router, container, originalLoggerError; var compile = Ember.Handlebars.compile; 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({ }); container = App.__container__; Ember.TEMPLATES.application = compile("{{outlet}}"); Ember.TEMPLATES.home = compile("

    Hours

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

    Megatroll

    {{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; }); } }); 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"); }); 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 = get(this, 'currentPath'); }) }); bootApplication(); equal(currentPath, 'home'); equal(Ember.$('h3:contains(Hours)', '#qunit-fixture').length, 1, "The home template was rendered"); }); 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 = get(this, 'currentPath'); }) }); App.CamelotController = Ember.Controller.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = 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"); }); 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'); }); 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"); }); 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({ home: "Comes from homepage" }); bootApplication(); equal(Ember.$('h3:contains(Megatroll) + p:contains(Comes from homepage)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); 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({ home: "Comes from Foo" }); App.HomepageController = Ember.Controller.extend({ home: "Comes from homepage" }); bootApplication(); equal(Ember.$('h3:contains(Megatroll) + p:contains(Comes from homepage)', '#qunit-fixture').length, 1, "The homepage template was rendered"); }); 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({ 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"); }); test("The Homepage with explicit template name in renderTemplate and controller", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeController = Ember.Controller.extend({ 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"); }); test("Model passed via renderTemplate model is set as controller's model", function(){ Ember.TEMPLATES['bio'] = compile("

    {{name}}

    "); App.BioController = Ember.ObjectController.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"); }); 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"); }); 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"); }); test('render does not replace templateName if user provided', function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.the_real_home_template = Ember.Handlebars.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"); }); test('render does not replace template if user provided', function () { Router.map(function () { this.route("home", { path: "/" }); }); App.HomeView = Ember.View.extend({ template: Ember.Handlebars.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"); }); test('render uses templateName from route', function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.the_real_home_template = Ember.Handlebars.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"); }); test('defining templateName allows other templates to be rendered', function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.alert = Ember.Handlebars.compile( "
    Invader!
    " ); Ember.TEMPLATES.the_real_home_template = Ember.Handlebars.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"); }); 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: Ember.Handlebars.compile("

    This should not be rendered

    {{home}}

    ") }); App.HomepageController = Ember.ObjectController.extend({ model: { home: 'Tinytroll' } }); App.HomepageView = Ember.View.extend({ layout: Ember.Handlebars.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"); }); test("The Homepage with a `setupController` hook", function() { Router.map(function() { this.route("home", { path: "/" }); }); App.HomeRoute = Ember.Route.extend({ setupController: function(controller) { set(controller, 'hours', Ember.A([ "Monday through Friday: 9am to 5pm", "Saturday: Noon to Midnight", "Sunday: Noon to 6pm" ])); } }); Ember.TEMPLATES.home = Ember.Handlebars.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"); }); 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 } }); container.register('controller:home', Ember.Controller.extend()); bootApplication(); deepEqual(container.lookup('route:home').controller, container.lookup('controller:home'), "route controller is the home controller"); }); test("The route controller can be specified via controllerName", function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.home = Ember.Handlebars.compile( "

    {{myValue}}

    " ); App.HomeRoute = Ember.Route.extend({ controllerName: 'myController' }); container.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"); }); test("The route controller specified via controllerName is used in render", function() { Router.map(function() { this.route("home", { path: "/" }); }); Ember.TEMPLATES.alternative_home = Ember.Handlebars.compile( "

    alternative home: {{myValue}}

    " ); App.HomeRoute = Ember.Route.extend({ controllerName: 'myController', renderTemplate: function() { this.render("alternative_home"); } }); container.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"); }); 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 = Ember.Handlebars.compile( "

    home: {{myValue}}

    " ); App.HomeRoute = Ember.Route.extend({ controllerName: 'myController' }); container.register('controller:home', Ember.Controller.extend({ myValue: "home" })); container.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"); }); 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) { set(this.controllerFor('home'), 'hours', Ember.A([ "Monday through Friday: 9am to 5pm", "Saturday: Noon to Midnight", "Sunday: Noon to 6pm" ])); } }); Ember.TEMPLATES.home = Ember.Handlebars.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"); }); 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 = Ember.Handlebars.compile( "
      {{#each}}
    • {{this}}
    • {{/each}}
    " ); bootApplication(); equal(Ember.$('ul li', '#qunit-fixture').eq(2).text(), "Sunday: Noon to 6pm", "The template was rendered with the context intact"); }); 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); set(this.controllerFor('home'), 'hours', model); } }); Ember.TEMPLATES.home = Ember.Handlebars.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"); }); 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) { set(controller, 'model', model); } }); Ember.TEMPLATES.special = Ember.Handlebars.compile( "

    {{model.menuItemId}}

    " ); bootApplication(); container.register('controller:special', Ember.Controller.extend()); handleURL("/specials/1"); equal(Ember.$('p', '#qunit-fixture').text(), "1", "The model was used to render the template"); }); 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) { set(controller, 'model', model); } }); Ember.TEMPLATES.special = Ember.Handlebars.compile( "

    {{model.id}}

    " ); bootApplication(); container.register('controller:special', Ember.Controller.extend()); handleURL("/specials/1"); equal(Ember.$('p', '#qunit-fixture').text(), "1", "The model was used to render the template"); }); 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) { set(controller, 'model', model); } }); Ember.TEMPLATES.special = Ember.Handlebars.compile( "

    {{model.id}}

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

    LOADING!

    " ); bootApplication(); container.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"); }); 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) { set(controller, 'model', model); } }); Ember.TEMPLATES.special = Ember.Handlebars.compile( "

    {{model.id}}

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

    LOADING!

    " ); bootApplication(); container.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'); }); */ 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); }); } test("ApplicationRoute's default error handler can be overridden", function() { testOverridableErrorHandler('actions'); }); test("ApplicationRoute's default error handler can be overridden (with DEPRECATED `events`)", 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) { set(controller, 'model', model); } }); Ember.TEMPLATES.home = Ember.Handlebars.compile( "

    Home

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

    {{model.id}}

    " ); bootApplication(); container.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 = get(this, 'currentPath'); }) }); var menuItem, resolve; 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) { set(controller, 'model', model); } }); Ember.TEMPLATES['root/index'] = Ember.Handlebars.compile( "

    Home

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

    {{model.id}}

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

    LOADING!

    " ); var rootSetup = 0, rootRender = 0, rootModel = 0, rootSerialize = 0; bootApplication(); container.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 = Ember.Handlebars.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(); } } }); container.register('controller:home', controller); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.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 = Ember.Handlebars.compile( "{{name}}" ); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.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'] = Ember.Handlebars.compile( "{{name}}" ); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.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 = Ember.Handlebars.compile( "{{name}}" ); expectDeprecation(/Action handlers contained in an `events` object are deprecated/); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.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'] = Ember.Handlebars.compile( "{{name}}" ); expectDeprecation(/Action handlers contained in an `events` object are deprecated/); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); 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"); }); if (Ember.FEATURES.isEnabled('ember-routing-drop-deprecated-action-style')) { 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 = Ember.Handlebars.compile( "{{name}}" ); var controller = Ember.Controller.extend({ showStuff: function(context) { stateIsNotCalled = false; ok (stateIsNotCalled, "an event on the state is not triggered"); } }); container.register('controller:home', controller); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); } else { asyncTest("Events are triggered on the controller if a matching action name is implemented as a method (DEPRECATED)", 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; }, events: { showStuff: function(obj) { stateIsNotCalled = false; ok (stateIsNotCalled, "an event on the state is not triggered"); } } }); Ember.TEMPLATES.home = Ember.Handlebars.compile( "{{name}}" ); var controller = Ember.Controller.extend({ 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(); } }); container.register('controller:home', controller); expectDeprecation(/Action handlers contained in an `events` object are deprecated/); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.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" }, 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'] = Ember.Handlebars.compile( "{{model1.name}}" ); bootApplication(); var actionId = Ember.$("#qunit-fixture a").data("ember-action"); var action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; var event = new Ember.$.Event("click"); action.handler(event); }); 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++; 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"); }); 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"); }); }); forEach(['foo', 'bar', '/foo'], function(destination) { Ember.run(router, 'transitionTo', destination); }); }); test("using replaceWith calls location.replaceURL if available", function() { var setCount = 0, replaceCount = 0; Router.reopen({ location: Ember.NoneLocation.createWithMixins({ setURL: function(path) { setCount++; set(this, 'path', path); }, replaceURL: function(path) { replaceCount++; 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"); }); 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++; 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"); }); 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 = {}, post2 = {}, post3 = {}, currentPost; var share1 = {}, share2 = {}, share3 = {}, currentShare; 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"); }); test("Resource does not inherit 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 = {}, post2 = {}, post3 = {}, 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'); notEqual(post, parent_model); } }); bootApplication(); currentPost = post1; handleURL("/posts/1/comments"); currentPost = post2; handleURL("/posts/2/comments"); currentPost = post3; handleURL("/posts/3/comments"); }); 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 = {}, post2 = {}, post3 = {}, 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"); }); test("A redirection hook is provided", function() { Router.map(function() { this.route("choose", { path: "/" }); this.route("home"); }); var chooseFollowed = 0, 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'); }); 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"); }); 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'); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); test("Parent route context change", function() { var editCount = 0, 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'); }); test("Router accounts for rootURL on page load when using history location", function() { var rootURL = window.location.pathname + '/app', postsTemplateRendered = false, 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 }); }, replaceState: function(path) { setHistory(this, path); }, pushState: function(path) { setHistory(this, path); } }); container.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"); }); test("The rootURL is passed properly to the location implementation", function() { expect(1); var rootURL = "/blahzorz", HistoryTestLocation; HistoryTestLocation = Ember.HistoryLocation.extend({ rootURL: 'this is not the URL you are looking for', initState: function() { equal(this.get('rootURL'), rootURL); } }); container.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(); }); 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"); }); 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]); }); 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'); }); 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'); }); 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); }); 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"); }); 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 = Ember.Handlebars.compile( "

    {{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"); }); 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 = Ember.Handlebars.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"); }); 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.ObjectController.extend({ currentPathDidChange: Ember.observer('currentPath', function() { currentPath = get(this, 'currentPath'); }) }); bootApplication(); equal(currentPath, 'index', 'currentPath is index'); equal('currentPath' in model, false, 'should have defined currentPath on controller'); }); 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 = Ember.Handlebars.compile("

    INDEX

    "); Ember.TEMPLATES.loading = Ember.Handlebars.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."); }); 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"); }); 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"); }); 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"); }); 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'); }); }); 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); }); 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'); }); 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(); }); 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"); }); 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'); }); 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); }); 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'); }); 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."); }); 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); }); 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(); }); 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"); }); test("rejecting the model hooks promise with a non-error prints the `message` property", function() { var rejectedMessage = 'OMG!! SOOOOOO BAD!!!!', 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(); }); 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(); }); test("rejecting the model hooks promise with a string shows a good error", function() { var originalLoggerError = Ember.Logger.error, 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; }); 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'); }); 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'); }); 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."); }); 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']]); }); }); define("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.'); }); }); define("ember/tests/routing/query_params_test", ["ember","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/platform"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var forEach = __dependency2__.forEach; var map = __dependency2__.map; var computed = __dependency3__.computed; var platform = __dependency4__.platform; var Router, App, AppView, templates, router, container; var get = Ember.get; var set = Ember.set; var compile = Ember.Handlebars.compile; function withoutMeta(object) { if (platform.defineProperty.isSimulated) { var newObject = Ember.$.extend(true, {}, object); delete newObject['__ember_meta__']; return newObject; } else { return object; } } 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); }); }); } var startingURL = '', 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(); container = App.__container__; container.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(); } }); 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"); }); 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'); }); 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"); }); 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(); }); 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(function() { return ['c']; }), c: true }); bootApplication(); var indexController = container.lookup('controller:index'); expectedReplaceURL = "not gonna happen"; Ember.run(indexController, 'set', 'a', 1); }); 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'), ""); }); 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, aboutShouldBeCreated = false, 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(); 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(); 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(); 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'); }); 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"); }); 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(); }); 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(); }); 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'), ""); }); 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"); }); 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); }); 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); }); 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'); }); 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'); }); 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' }); }); test("can opt into full transition by setting refreshModel in route queryParams when transitioning from child to parent", function() { Ember.TEMPLATES.parent = Ember.Handlebars.compile('{{outlet}}'); Ember.TEMPLATES['parent/child'] = Ember.Handlebars.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); var parentController = container.lookup('controller:parent'); Ember.run(Ember.$('#parent-link'), 'click'); equal(parentModelCount, 2); }); 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 refress return { replace: true }; } }) }); bootApplication(); equal(router.get('location.path'), ""); var appController = container.lookup('controller:application'); expectedReplaceURL = "/?alex=wallace"; setAndFlush(appController, 'alex', 'wallace'); }); 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"); }); 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']))); }); 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'); }); 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"); }); 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)"); }); 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)"); }); 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', ''); }); 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); }); 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'), ""); }); 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"); }); 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"); }); 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"]); }); 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]); }); 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'), ""); }); 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.ObjectController.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'); }); 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(); }); test("opting into replace does not affect transitions between routes", function() { expect(5); Ember.TEMPLATES.application = Ember.Handlebars.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'); }); 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); }); 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(); }); 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.ObjectController.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"); } }); 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'); }); 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'); }); 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'); }); 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'); }); 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'); }); 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"); }); 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(); } }); 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'); }); 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' }`"); }); 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); }); }); define("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.'); }); }); define("ember/tests/routing/substates_test", ["ember"], function(__dependency1__) { "use strict"; var Router, App, AppView, templates, router, container, counter; var get = Ember.get, set = Ember.set, compile = Ember.Handlebars.compile; 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'); } 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("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: '' }); } }); test("Slow promise from a child route of application enters nested loading state", function() { var broModel = {}, 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"); }); test("Slow promises waterfall on startup", function() { expect(7); var grandmaDeferred = Ember.RSVP.defer(), 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); 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"); }); test("Loading actions bubble to root, but don't enter substates above pivot", function() { expect(6); delete templates.loading; var sallyDeferred = Ember.RSVP.defer(), smellsDeferred = Ember.RSVP.defer(); var shouldBubbleToApplication = true; 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"); }); test("Default error event moves into nested route", function() { expect(5); templates['grandma'] = "GRANDMA {{outlet}}"; templates['grandma/error'] = "ERROR: {{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"); }); }); define("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.'); }); }); define("ember/tests/states_removal_test", ["ember"], function(__dependency1__) { "use strict"; /*globals EmberDev */ QUnit.module("ember-states removal"); test("errors occur when attempting to use Ember.StateManager or Ember.State", function() { if (EmberDev && EmberDev.runningProdBuild){ ok(true, 'Ember.State & Ember.StateManager are not added to production builds'); return; } raises(function() { Ember.StateManager.extend(); }, /has been moved into a plugin/); raises(function() { Ember.State.extend(); }, /has been moved into a plugin/); raises(function() { Ember.StateManager.create(); }, /has been moved into a plugin/); raises(function() { Ember.State.create(); }, /has been moved into a plugin/); }); }); define("ember/tests/states_removal_test.jshint", [], function() { "use strict"; module('JSHint - ember/tests'); test('ember/tests/states_removal_test.js should pass jshint', function() { ok(true, 'ember/tests/states_removal_test.js should pass jshint.'); }); }); })();