/*! * @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.9.0-beta.4 */ (function() { var enifed, requireModule, eriuqer, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; if (typeof Ember === 'undefined') { Ember = {}; }; if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; enifed = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; requirejs = eriuqer = 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]; var deps = mod.deps; var callback = mod.callback; var reified = []; var 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) { return new this.prototype.constructor(options); } function reopenClass(options) { setProperties(this, options); } function extend(options) { var Child = function(options) { Klass.call(this, options); }; var Parent = this; Child.prototype = new Parent(); Child.prototype.constructor = Child; setProperties(Child, Klass); setProperties(Child.prototype, options); Child.create = create; Child.extend = extend; Child.reopen = extend; Child.reopenClass = reopenClass; return Child; } }; __exports__.factory = factory; __exports__.setProperties = setProperties; }); enifed("container/tests/container_helper.jshint", [], function() { "use strict"; module('JSHint - container/tests'); test('container/tests/container_helper.js should pass jshint', function() { ok(true, 'container/tests/container_helper.js should pass jshint.'); }); }); enifed("container/tests/container_test", ["container/tests/container_helper","container"], function(__dependency1__, __dependency2__) { "use strict"; var factory = __dependency1__.factory; 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(); 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 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(); var 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 a given factory", function(){ var container = new Container(); var PostView = factory(); container.resolver = function(fullName) { if (fullName === 'view:post') { return PostView; } }; container.options('view:post', {instantiate: true, singleton: false}); var postView1 = container.lookup('view:post'); var postView2 = container.lookup('view:post'); ok(postView1 instanceof PostView, "The correct factory was provided"); ok(postView2 instanceof PostView, "The correct factory was provided"); ok(postView1 !== postView2, "The two lookups are different"); }); 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(); var FirstApple = factory('first'); var 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(); var FirstApple = factory('first'); var 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(); var FirstApple = factory('first'); var 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(); var Apple = factory(); var Worm = factory(); container.register('apple:main', Apple); container.register('worm:main', Worm); 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(); var Apple = factory(); var Worm = factory(); container.register('apple:main', Apple); container.register('worm:main', Worm); 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')"); }); test("factory resolves are cached", function() { var container = new Container(); var PostController = factory(); var resolveWasCalled = []; container.resolve = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); container.lookupFactory('controller:post'); deepEqual(resolveWasCalled, ['controller:post']); container.lookupFactory('controller:post'); deepEqual(resolveWasCalled, ['controller:post']); }); test("factory for non extendables (MODEL) resolves are cached", function() { var container = new Container(); var PostController = factory(); var resolveWasCalled = []; container.resolve = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); container.lookupFactory('model:post'); deepEqual(resolveWasCalled, ['model:post']); container.lookupFactory('model:post'); deepEqual(resolveWasCalled, ['model:post']); }); test("factory for non extendables resolves are cached", function() { var container = new Container(); var PostController = {}; var resolveWasCalled = []; container.resolve = function(fullName) { resolveWasCalled.push(fullName); return PostController; }; deepEqual(resolveWasCalled, []); container.lookupFactory('foo:post'); deepEqual(resolveWasCalled, ['foo:post']); container.lookupFactory('foo:post'); deepEqual(resolveWasCalled, ['foo:post']); }); }); enifed("container/tests/container_test.jshint", [], function() { "use strict"; module('JSHint - container/tests'); test('container/tests/container_test.js should pass jshint', function() { ok(true, 'container/tests/container_test.js should pass jshint.'); }); }); enifed("container/tests/sub_container_test", ["container/tests/container_helper","container"], function(__dependency1__, __dependency2__) { "use strict"; var factory = __dependency1__.factory; 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); }); }); enifed("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.'); }); }); enifed("ember-application.jshint", [], function() { "use strict"; module('JSHint - .'); test('ember-application.js should pass jshint', function() { ok(true, 'ember-application.js should pass jshint.'); }); }); enifed("ember-application/ext/controller.jshint", [], function() { "use strict"; module('JSHint - ember-application/ext'); test('ember-application/ext/controller.js should pass jshint', function() { ok(true, 'ember-application/ext/controller.js should pass jshint.'); }); }); enifed("ember-application/system/application.jshint", [], function() { "use strict"; module('JSHint - ember-application/system'); test('ember-application/system/application.js should pass jshint', function() { ok(true, 'ember-application/system/application.js should pass jshint.'); }); }); enifed("ember-application/system/resolver.jshint", [], function() { "use strict"; module('JSHint - ember-application/system'); test('ember-application/system/resolver.js should pass jshint', function() { ok(true, 'ember-application/system/resolver.js should pass jshint.'); }); }); enifed("ember-application/tests/system/application_test", ["ember-metal/core","ember-metal/run_loop","ember-application/system/application","ember-application/system/resolver","ember-routing/system/router","ember-views/views/view","ember-runtime/controllers/controller","ember-routing/location/none_location","ember-handlebars","ember-runtime/system/object","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; /*globals EmberDev */ var Ember = __dependency1__["default"]; var run = __dependency2__["default"]; var Application = __dependency3__["default"]; var DefaultResolver = __dependency4__["default"]; var Router = __dependency5__["default"]; var View = __dependency6__["default"]; var Controller = __dependency7__["default"]; var NoneLocation = __dependency8__["default"]; var EmberHandlebars = __dependency9__["default"]; var EmberObject = __dependency10__["default"]; var jQuery = __dependency11__["default"]; var trim = jQuery.trim; var 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 = {}; var 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./); }); test("registers controls onto to container", function() { run(function() { app = Application.create({ rootElement: '#qunit-fixture' }); }); ok(app.__container__.lookup('view:select'), "Select control is registered into views"); }); }); enifed("ember-application/tests/system/application_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/application_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/application_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/controller_test", ["ember-runtime/controllers/controller","ember-application/ext/controller","ember-runtime/system/container","ember-runtime/system/native_array","ember-runtime/controllers/array_controller","ember-metal/computed"], function(__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'); var 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'); 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"); }); }); enifed("ember-application/tests/system/controller_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/controller_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/controller_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/dependency_injection/custom_resolver_test", ["ember-views/system/jquery","ember-metal/run_loop","ember-application/system/application","ember-application/system/resolver"], function(__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"); }); }); enifed("ember-application/tests/system/dependency_injection/custom_resolver_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/custom_resolver_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/custom_resolver_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/dependency_injection/default_resolver_test", ["ember-metal/core","ember-metal/run_loop","ember-metal/logger","ember-runtime/controllers/controller","ember-runtime/system/object","ember-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, 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'); }); }); enifed("ember-application/tests/system/dependency_injection/default_resolver_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/default_resolver_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/default_resolver_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/dependency_injection/normalization_test", ["ember-metal/run_loop","ember-metal/array","ember-application/system/application"], function(__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)); }); }); }); enifed("ember-application/tests/system/dependency_injection/normalization_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/normalization_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/normalization_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/dependency_injection/to_string_test", ["ember-metal/core","ember-metal/run_loop","ember-application/system/application","ember-runtime/system/object","ember-application/system/resolver","ember-metal/utils"], function(__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'); }); }); enifed("ember-application/tests/system/dependency_injection/to_string_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system/dependency_injection'); test('ember-application/tests/system/dependency_injection/to_string_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection/to_string_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/dependency_injection_test", ["ember-metal/run_loop","ember-runtime/system/object","ember-application/system/application"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var run = __dependency1__["default"]; var EmberObject = __dependency2__["default"]; var Application = __dependency3__["default"]; var EmberApplication = Application; var originalLookup = Ember.lookup; var locator, 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'); var person = locator.lookup('model:person'); var fruit = locator.lookup('fruit:favorite'); equal(user.get('fruit'), fruit); equal(person.get('fruit'), fruit); ok(application.Email.detectInstance(user.get('communication'))); }); }); enifed("ember-application/tests/system/dependency_injection_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/dependency_injection_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/dependency_injection_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/initializers_test", ["ember-metal/run_loop","ember-application/system/application","ember-metal/array","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var run = __dependency1__["default"]; var Application = __dependency2__["default"]; var indexOf = __dependency3__.indexOf; var jQuery = __dependency4__["default"]; var app; QUnit.module("Ember.Application initializers", { setup: function() { }, teardown: function() { if (app) { run(function() { app.destroy(); }); } } }); test("initializers require proper 'name' and 'initialize' properties", function() { var MyApplication = Application.extend(); expectAssertion(function() { run(function() { MyApplication.initializer({name:'initializer'}); }); }); expectAssertion(function() { run(function() { MyApplication.initializer({initialize:Ember.K}); }); }); }); 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 = []; var a = { name: "a", before: "b", initialize: function(container) { order.push('a'); } }; var b = { name: "b", initialize: function(container) { order.push('b'); } }; var c = { name: "c", after: "b", initialize: function(container) { order.push('c'); } }; var afterB = { name: "after b", after: "b", initialize: function(container) { order.push("after b"); } }; var 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; var 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() { 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() { 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; var 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() { 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() { 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) {} }); }); }); enifed("ember-application/tests/system/initializers_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/initializers_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/initializers_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/logging_test", ["ember-metal/run_loop","ember-application/system/application","ember-views/views/view","ember-runtime/controllers/controller","ember-routing/system/route","ember-runtime/ext/rsvp","ember-metal/keys","ember-routing"], function(__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'); }); }); }); enifed("ember-application/tests/system/logging_test.jshint", [], function() { "use strict"; module('JSHint - ember-application/tests/system'); test('ember-application/tests/system/logging_test.js should pass jshint', function() { ok(true, 'ember-application/tests/system/logging_test.js should pass jshint.'); }); }); enifed("ember-application/tests/system/readiness_test", ["ember-metal/run_loop","ember-application/system/application"], function(__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; i 0) { QUnit.deepEqual(templateNames, [], "Ember.TEMPLATES should be empty"); this.env.Ember.TEMPLATES = {}; } } }, restore: function(){} }; __exports__["default"] = RemainingTemplateAssert; }); enifed("ember-dev/test-helper/remaining-view", ["exports"], function(__exports__) { "use strict"; /* globals QUnit */ var RemainingViewAssert = function(env){ this.env = env; }; RemainingViewAssert.prototype = { reset: function(){}, inject: function(){}, assert: function(){ if (this.env.Ember && this.env.Ember.View) { var viewIds = [], id; for (id in this.env.Ember.View.views) { if (this.env.Ember.View.views[id] != null) { viewIds.push(id); } } if (viewIds.length > 0) { QUnit.deepEqual(viewIds, [], "Ember.View.views should be empty"); this.env.Ember.View.views = []; } } }, restore: function(){} }; __exports__["default"] = RemainingViewAssert; }); enifed("ember-dev/test-helper/run-loop", ["exports"], function(__exports__) { "use strict"; /* globals QUnit */ function RunLoopAssertion(env){ this.env = env; } RunLoopAssertion.prototype = { reset: function(){}, inject: function(){}, assert: function(){ var run = this.env.Ember.run; if (run.currentRunLoop) { QUnit.ok(false, "Should not be in a run loop at end of test"); while (run.currentRunLoop) { run.end(); } } if (run.hasScheduledTimers()) { QUnit.ok(false, "Ember run should not have scheduled timers at end of test"); run.cancelTimers(); } }, restore: function(){} }; __exports__["default"] = RunLoopAssertion; }); enifed("ember-dev/test-helper/setup-qunit", ["exports"], function(__exports__) { "use strict"; /* globals QUnit */ __exports__["default"] = function setupQUnit(assertion, _qunitGlobal) { var qunitGlobal = QUnit; if (_qunitGlobal) { qunitGlobal = _qunitGlobal; } var originalModule = qunitGlobal.module; qunitGlobal.module = function(name, _options) { var options = _options || {}; var originalSetup = options.setup || function() { }; var originalTeardown = options.teardown || function() { }; options.setup = function() { assertion.reset(); assertion.inject(); originalSetup.call(this); }; options.teardown = function() { originalTeardown.call(this); assertion.assert(); assertion.restore(); }; return originalModule(name, options); }; } }); enifed("ember-dev/test-helper/utils", ["exports"], function(__exports__) { "use strict"; function callForEach(prop, func) { return function() { for (var i=0, l=this[prop].length;i'); 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}} (DEPRECATED)", function() { TemplateTests.set('value', 'Test'); view = EmberView.create({ template: EmberHandlebars.compile('TemplateTests.value') }); expectDeprecation(function(){ appendView(); }, /Global lookup of TemplateTests.value from a Handlebars template is deprecated/); equal(view.$('img').attr('alt'), "Test", "renders initial 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() { expect(4); var originalBindAttr = EmberHandlebars.helpers['bind-attr']; try { EmberHandlebars.helpers['bind-attr'] = function() { equal(arguments[0], 'foo', 'First arg match'); equal(arguments[1], 'bar', 'Second arg match'); return 'result'; }; expectDeprecation(function() { var result = EmberHandlebars.helpers.bindAttr('foo', 'bar'); equal(result, 'result', 'Result match'); }, "The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); } finally { EmberHandlebars.helpers['bind-attr'] = originalBindAttr; } }); test("should be able to bind element attributes using {{bind-attr}} inside a block", function() { var template = EmberHandlebars.compile('{{#with view.content as image}}image.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}} (DEPRECATED)", function() { TemplateTests.set('isOpen', true); view = EmberView.create({ template: EmberHandlebars.compile('') }); expectDeprecation(function(){ appendView(); }, /Global lookup of TemplateTests.isOpen from a Handlebars template is deprecated/); ok(view.$('img').hasClass('is-open'), "sets classname to the dasherized value of the global property"); }); test("should be able to bind-attr to 'this' in an {{#each}} block [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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}} [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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 teardown observers from bind-attr on rerender", function() { view = EmberView.create({ template: EmberHandlebars.compile('wat'), foo: 'bar' }); appendView(); equal(observersFor(view, 'foo').length, 1); run(function() { view.rerender(); }); equal(observersFor(view, 'foo').length, 1); }); }); enifed("ember-handlebars/tests/bind_attr_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests'); test('ember-handlebars/tests/bind_attr_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/bind_attr_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/controls/checkbox_test", ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-views/views/view","ember-views/system/event_dispatcher","ember-handlebars-compiler"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "use strict"; var get = __dependency1__.get; var o_set = __dependency2__.set; var run = __dependency3__["default"]; var EmberView = __dependency4__["default"]; var EventDispatcher = __dependency5__["default"]; var EmberHandlebars = __dependency6__["default"]; // import {expectAssertion} from "ember-metal/tests/debug_helpers"; function set(obj, key, value) { run(function() { o_set(obj, key, value); }); } var checkboxView, dispatcher, controller; var compile = EmberHandlebars.compile; function destroy(view) { run(function() { view.destroy(); }); } QUnit.module("{{input type='checkbox'}}", { setup: function() { controller = { tab: 6, name: 'hello', val: false }; checkboxView = EmberView.extend({ controller: controller, template: compile('{{input type="checkbox" disabled=disabled tabindex=tab name=name checked=val}}') }).create(); append(); }, teardown: function() { destroy(checkboxView); } }); test("should append a checkbox", function() { equal(checkboxView.$('input[type=checkbox]').length, 1, "A single checkbox is added"); }); test("should begin disabled if the disabled attribute is true", function() { ok(checkboxView.$('input').is(':not(:disabled)'), "The checkbox isn't disabled"); set(controller, 'disabled', true); ok(checkboxView.$('input').is(':disabled'), "The checkbox is now disabled"); }); test("should support the tabindex property", function() { equal(checkboxView.$('input').prop('tabindex'), '6', 'the initial checkbox tabindex is set in the DOM'); set(controller, 'tab', 3); equal(checkboxView.$('input').prop('tabindex'), '3', 'the checkbox tabindex changes when it is changed in the view'); }); test("checkbox name is updated", function() { equal(checkboxView.$('input').attr('name'), "hello", "renders checkbox with the name"); set(controller, 'name', 'bye'); equal(checkboxView.$('input').attr('name'), "bye", "updates checkbox after name changes"); }); test("checkbox checked property is updated", function() { equal(checkboxView.$('input').prop('checked'), false, "the checkbox isn't checked yet"); set(controller, 'val', true); equal(checkboxView.$('input').prop('checked'), true, "the checkbox is checked now"); }); QUnit.module("{{input type='checkbox'}} - prevent value= usage", { setup: function() { checkboxView = EmberView.extend({ controller: controller, template: compile('{{input type="checkbox" disabled=disabled tabindex=tab name=name value=val}}') }).create(); }, teardown: function() { destroy(checkboxView); } }); test("It works", function() { expectAssertion(function() { append(); }, /you must use `checked=/); }); QUnit.module("{{input type=boundType}}", { setup: function() { controller = { inputType: "checkbox", isChecked: true }; checkboxView = EmberView.extend({ controller: controller, template: compile('{{input type=inputType checked=isChecked}}') }).create(); append(); }, teardown: function() { destroy(checkboxView); } }); test("should append a checkbox", function() { equal(checkboxView.$('input[type=checkbox]').length, 1, "A single checkbox is added"); }); // Checking for the checked property is a good way to verify that the correct // view was used. test("checkbox checked property is updated", function() { equal(checkboxView.$('input').prop('checked'), true, "the checkbox is checked"); }); QUnit.module("{{input type='checkbox'}} - static values", { setup: function() { controller = { tab: 6, name: 'hello', val: false }; checkboxView = EmberView.extend({ controller: controller, template: compile('{{input type="checkbox" disabled=true tabindex=6 name="hello" checked=false}}') }).create(); append(); }, teardown: function() { destroy(checkboxView); } }); test("should begin disabled if the disabled attribute is true", function() { ok(checkboxView.$().is(':not(:disabled)'), "The checkbox isn't disabled"); }); test("should support the tabindex property", function() { equal(checkboxView.$('input').prop('tabindex'), '6', 'the initial checkbox tabindex is set in the DOM'); }); test("checkbox name is updated", function() { equal(checkboxView.$('input').attr('name'), "hello", "renders checkbox with the name"); }); test("checkbox checked property is updated", function() { equal(checkboxView.$('input').prop('checked'), false, "the checkbox isn't checked yet"); }); QUnit.module("Ember.Checkbox", { setup: function() { dispatcher = EventDispatcher.create(); dispatcher.setup(); }, teardown: function() { run(function() { dispatcher.destroy(); checkboxView.destroy(); }); } }); function append() { run(function() { checkboxView.appendTo('#qunit-fixture'); }); } test("should begin disabled if the disabled attribute is true", function() { checkboxView = Ember.Checkbox.create({}); checkboxView.set('disabled', true); append(); ok(checkboxView.$().is(":disabled")); }); test("should become disabled if the disabled attribute is changed", function() { checkboxView = Ember.Checkbox.create({}); append(); ok(checkboxView.$().is(":not(:disabled)")); run(function() { checkboxView.set('disabled', true); }); ok(checkboxView.$().is(":disabled")); run(function() { checkboxView.set('disabled', false); }); ok(checkboxView.$().is(":not(:disabled)")); }); test("should begin indeterminate if the indeterminate attribute is true", function() { checkboxView = Ember.Checkbox.create({}); checkboxView.set('indeterminate', true); append(); equal(checkboxView.$().prop('indeterminate'), true, "Checkbox should be indeterminate"); }); test("should become indeterminate if the indeterminate attribute is changed", function() { checkboxView = Ember.Checkbox.create({}); append(); equal(checkboxView.$().prop('indeterminate'), false, "Checkbox should not be indeterminate"); run(function() { checkboxView.set('indeterminate', true); }); equal(checkboxView.$().prop('indeterminate'), true, "Checkbox should be indeterminate"); run(function() { checkboxView.set('indeterminate', false); }); equal(checkboxView.$().prop('indeterminate'), false, "Checkbox should not be indeterminate"); }); test("should support the tabindex property", function() { checkboxView = Ember.Checkbox.create({}); run(function() { checkboxView.set('tabindex', 6); }); append(); equal(checkboxView.$().prop('tabindex'), '6', 'the initial checkbox tabindex is set in the DOM'); run(function() { checkboxView.set('tabindex', 3); }); equal(checkboxView.$().prop('tabindex'), '3', 'the checkbox tabindex changes when it is changed in the view'); }); test("checkbox name is updated when setting name property of view", function() { checkboxView = Ember.Checkbox.create({}); run(function() { checkboxView.set('name', 'foo'); }); append(); equal(checkboxView.$().attr('name'), "foo", "renders checkbox with the name"); run(function() { checkboxView.set('name', 'bar'); }); equal(checkboxView.$().attr('name'), "bar", "updates checkbox after name changes"); }); test("checked property mirrors input value", function() { checkboxView = Ember.Checkbox.create({}); run(function() { checkboxView.append(); }); equal(get(checkboxView, 'checked'), false, "initially starts with a false value"); equal(!!checkboxView.$().prop('checked'), false, "the initial checked property is false"); set(checkboxView, 'checked', true); equal(checkboxView.$().prop('checked'), true, "changing the value property changes the DOM"); run(function() { checkboxView.remove(); }); run(function() { checkboxView.append(); }); equal(checkboxView.$().prop('checked'), true, "changing the value property changes the DOM"); run(function() { checkboxView.remove(); }); run(function() { set(checkboxView, 'checked', false); }); run(function() { checkboxView.append(); }); equal(checkboxView.$().prop('checked'), false, "changing the value property changes the DOM"); }); test("checking the checkbox updates the value", function() { checkboxView = Ember.Checkbox.create({ checked: true }); append(); equal(get(checkboxView, 'checked'), true, "precond - initially starts with a true value"); equal(!!checkboxView.$().prop('checked'), true, "precond - the initial checked property is true"); // IE fires 'change' event on blur. checkboxView.$()[0].focus(); checkboxView.$()[0].click(); checkboxView.$()[0].blur(); equal(!!checkboxView.$().prop('checked'), false, "after clicking a checkbox, the checked property changed"); equal(get(checkboxView, 'checked'), false, "changing the checkbox causes the view's value to get updated"); }); }); enifed("ember-handlebars/tests/controls/checkbox_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/controls'); test('ember-handlebars/tests/controls/checkbox_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/controls/checkbox_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/controls/select_test", ["ember-runtime/system/object","ember-metal/run_loop","ember-views/views/view","ember-views/system/jquery","ember-metal/enumerable_utils","ember-views/system/event_dispatcher","ember-metal/computed","ember-runtime/system/namespace","ember-runtime/controllers/array_controller","ember-runtime/system/array_proxy","ember-handlebars/controls/select"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__) { "use strict"; var EmberObject = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var jQuery = __dependency4__["default"]; var map = __dependency5__.map; var EventDispatcher = __dependency6__["default"]; var computed = __dependency7__.computed; var Namespace = __dependency8__["default"]; var ArrayController = __dependency9__["default"]; var ArrayProxy = __dependency10__["default"]; var SelectView = __dependency11__["default"]; var trim = jQuery.trim; var dispatcher, select, view; QUnit.module("Ember.Select", { setup: function() { dispatcher = EventDispatcher.create(); dispatcher.setup(); select = Ember.Select.create(); }, teardown: function() { run(function() { dispatcher.destroy(); select.destroy(); }); } }); function append() { run(function() { select.appendTo('#qunit-fixture'); }); } function selectedOptions() { return select.get('childViews').mapBy('selected'); } test("has 'ember-view' and 'ember-select' CSS classes", function() { deepEqual(select.get('classNames'), ['ember-view', 'ember-select']); }); test("should render", function() { append(); ok(select.$().length, "Select renders"); }); test("should begin disabled if the disabled attribute is true", function() { select.set('disabled', true); append(); ok(select.$().is(":disabled")); }); test("should begin required if the required attribute is true", function() { select.set('required', true); append(); ok(select.element.required, 'required property is truthy'); }); test("should become required if the required attribute is changed", function() { append(); ok(!select.element.required, 'required property is falsy'); run(function() { select.set('required', true); }); ok(select.element.required, 'required property is truthy'); run(function() { select.set('required', false); }); ok(!select.element.required, 'required property is falsy'); }); test("should become disabled if the disabled attribute is changed", function() { append(); ok(!select.element.disabled, 'disabled property is falsy'); run(function() { select.set('disabled', true); }); ok(select.element.disabled, 'disabled property is truthy'); run(function() { select.set('disabled', false); }); ok(!select.element.disabled, 'disabled property is falsy'); }); test("can have options", function() { select.set('content', Ember.A([1, 2, 3])); append(); equal(select.$('option').length, 3, "Should have three options"); // IE 8 adds whitespace equal(trim(select.$().text()), "123", "Options should have content"); }); test("select tabindex is updated when setting tabindex property of view", function() { run(function() { select.set('tabindex', '4'); }); append(); equal(select.$().attr('tabindex'), "4", "renders select with the tabindex"); run(function() { select.set('tabindex', '1'); }); equal(select.$().attr('tabindex'), "1", "updates select after tabindex changes"); }); test("select name is updated when setting name property of view", function() { run(function() { select.set('name', 'foo'); }); append(); equal(select.$().attr('name'), "foo", "renders select with the name"); run(function() { select.set('name', 'bar'); }); equal(select.$().attr('name'), "bar", "updates select after name changes"); }); test("can specify the property path for an option's label and value", function() { select.set('content', Ember.A([ { id: 1, firstName: 'Yehuda' }, { id: 2, firstName: 'Tom' } ])); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); append(); equal(select.$('option').length, 2, "Should have two options"); // IE 8 adds whitespace equal(trim(select.$().text()), "YehudaTom", "Options should have content"); deepEqual(map(select.$('option').toArray(), function(el) { return jQuery(el).attr('value'); }), ["1", "2"], "Options should have values"); }); test("can retrieve the current selected option when multiple=false", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; select.set('content', Ember.A([yehuda, tom])); append(); equal(select.get('selection'), yehuda, "By default, the first option is selected"); select.$()[0].selectedIndex = 1; // select Tom select.$().trigger('change'); equal(select.get('selection'), tom, "On change, the new option should be selected"); }); test("can retrieve the current selected options when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.firstName'); append(); deepEqual(select.get('selection'), [], "By default, nothing is selected"); select.$('option').each(function() { if (this.value === 'Tom' || this.value === 'David') { this.selected = true; } }); select.$().trigger('change'); deepEqual(select.get('selection'), [tom, david], "On change, the new options should be selected"); }); test("selection can be set when multiple=false", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; run(function() { select.set('content', Ember.A([yehuda, tom])); select.set('multiple', false); select.set('selection', tom); }); append(); equal(select.get('selection'), tom, "Initial selection should be correct"); run(function() { select.set('selection', yehuda); }); equal(select.$()[0].selectedIndex, 0, "After changing it, selection should be correct"); }); test("selection can be set when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('selection', tom); }); append(); deepEqual(select.get('selection'), [tom], "Initial selection should be correct"); run(function() { select.set('selection', yehuda); }); deepEqual(select.get('selection'), [yehuda], "After changing it, selection should be correct"); }); test("selection can be set when multiple=true and prompt", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('prompt', 'Pick one!'); select.set('selection', tom); }); append(); deepEqual(select.get('selection'), [tom], "Initial selection should be correct"); run(function() { select.set('selection', yehuda); }); deepEqual(select.get('selection'), [yehuda], "After changing it, selection should be correct"); }); test("multiple selections can be set when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('optionLabelPath', 'content.firstName'); select.set('multiple', true); select.set('selection', Ember.A([yehuda, david])); }); append(); deepEqual(select.get('selection'), [yehuda, david], "Initial selection should be correct"); run(function() { select.set('selection', Ember.A([tom, brennain])); }); deepEqual( select.$(':selected').map(function() { return trim(jQuery(this).text());}).toArray(), ['Tom', 'Brennain'], "After changing it, selection should be correct"); }); test("multiple selections can be set by changing in place the selection array when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; var selection = Ember.A([yehuda, tom]); run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('optionLabelPath', 'content.firstName'); select.set('multiple', true); select.set('selection', selection); }); append(); deepEqual(select.get('selection'), [yehuda, tom], "Initial selection should be correct"); run(function() { selection.replace(0, selection.get('length'), Ember.A([david, brennain])); }); deepEqual( select.$(':selected').map(function() { return trim(jQuery(this).text());}).toArray(), ['David', 'Brennain'], "After updating the selection array in-place, selection should be correct"); }); test("multiple selections can be set indirectly via bindings and in-place when multiple=true (issue #1058)", function() { var indirectContent = EmberObject.create(); var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; var cyril = { id: 5, firstName: 'Cyril' }; run(function() { select.destroy(); // Destroy the existing select run(function() { select = Ember.Select.extend({ indirectContent: indirectContent, contentBinding: 'indirectContent.controller.content', selectionBinding: 'indirectContent.controller.selection', multiple: true, optionLabelPath: 'content.firstName' }).create(); indirectContent.set('controller', EmberObject.create({ content: Ember.A([tom, david, brennain]), selection: Ember.A([david]) })); }); append(); }); deepEqual(select.get('content'), [tom, david, brennain], "Initial content should be correct"); deepEqual(select.get('selection'), [david], "Initial selection should be correct"); run(function() { indirectContent.set('controller.content', Ember.A([david, cyril])); indirectContent.set('controller.selection', Ember.A([cyril])); }); deepEqual(select.get('content'), [david, cyril], "After updating bound content, content should be correct"); deepEqual(select.get('selection'), [cyril], "After updating bound selection, selection should be correct"); }); test("select with group can group options", function() { var content = Ember.A([ { firstName: 'Yehuda', organization: 'Tilde' }, { firstName: 'Tom', organization: 'Tilde' }, { firstName: 'Keith', organization: 'Envato' } ]); run(function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); }); append(); equal(select.$('optgroup').length, 2); var labels = []; select.$('optgroup').each(function() { labels.push(this.label); }); equal(labels.join(''), ['TildeEnvato']); equal(trim(select.$('optgroup').first().text()), 'YehudaTom'); equal(trim(select.$('optgroup').last().text()), 'Keith'); }); test("select with group doesn't break options", function() { var content = Ember.A([ { id: 1, firstName: 'Yehuda', organization: 'Tilde' }, { id: 2, firstName: 'Tom', organization: 'Tilde' }, { id: 3, firstName: 'Keith', organization: 'Envato' } ]); run(function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); }); append(); equal(select.$('option').length, 3); equal(trim(select.$().text()), 'YehudaTomKeith'); run(function() { content.set('firstObject.firstName', 'Peter'); }); equal(select.$().text(), 'PeterTomKeith'); select.$('option').get(0).selected = true; select.$().trigger('change'); deepEqual(select.get('selection'), content.get('firstObject')); }); test("select with group observes its content", function() { var wycats = { firstName: 'Yehuda', organization: 'Tilde' }; var content = Ember.A([ wycats ]); run(function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); }); append(); run(function() { content.pushObject({ firstName: 'Keith', organization: 'Envato' }); }); equal(select.$('optgroup').length, 2); equal(select.$('optgroup[label=Envato]').length, 1); run(function() { select.set('optionGroupPath', 'firstName'); }); var labels = []; select.$('optgroup').each(function() { labels.push(this.label); }); equal(labels.join(''), 'YehudaKeith'); }); test("select with group whose content is undefined doesn't breaks", function() { var content; run(function() { select.set('content', content); select.set('optionGroupPath', 'organization'); select.set('optionLabelPath', 'content.firstName'); }); append(); equal(select.$('optgroup').length, 0); }); test("selection uses the same array when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; var selection = Ember.A([yehuda, david]); run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('optionLabelPath', 'content.firstName'); select.set('selection', selection); }); append(); deepEqual(select.get('selection'), [yehuda, david], "Initial selection should be correct"); select.$('option').each(function() { this.selected = false; }); select.$(':contains("Tom"), :contains("David")').each(function() { this.selected = true; }); select.$().trigger('change'); deepEqual(select.get('selection'), [tom,david], "On change the selection is updated"); deepEqual(selection, [tom,david], "On change the original selection array is updated"); }); test("Ember.SelectedOption knows when it is selected when multiple=false", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', false); select.set('selection', david); }); append(); deepEqual(selectedOptions(), [false, false, true, false], "Initial selection should be correct"); run(function() { select.set('selection', brennain); }); deepEqual(selectedOptions(), [false, false, false, true], "After changing it, selection should be correct"); }); test("Ember.SelectedOption knows when it is selected when multiple=true", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; run(function() { select.set('content', Ember.A([yehuda, tom, david, brennain])); select.set('multiple', true); select.set('selection', [yehuda, david]); }); append(); deepEqual(selectedOptions(), [true, false, true, false], "Initial selection should be correct"); run(function() { select.set('selection', [tom, david]); }); deepEqual(selectedOptions(), [false, true, true, false], "After changing it, selection should be correct"); }); test("Ember.SelectedOption knows when it is selected when multiple=true and options are primatives", function() { run(function() { select.set('content', Ember.A([1, 2, 3, 4])); select.set('multiple', true); select.set('selection', [1, 3]); }); append(); deepEqual(selectedOptions(), [true, false, true, false], "Initial selection should be correct"); run(function() { select.set('selection', [2, 3]); }); deepEqual(selectedOptions(), [false, true, true, false], "After changing it, selection should be correct"); }); test("a prompt can be specified", function() { var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; run(function() { select.set('content', Ember.A([yehuda, tom])); select.set('prompt', 'Pick a person'); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); }); append(); equal(select.$('option').length, 3, "There should be three options"); equal(select.$()[0].selectedIndex, 0, "By default, the prompt is selected in the DOM"); equal(trim(select.$('option:selected').text()), 'Pick a person', "By default, the prompt is selected in the DOM"); equal(select.$().val(), '', "By default, the prompt has no value"); equal(select.get('selection'), null, "When the prompt is selected, the selection should be null"); run(function() { select.set('selection', tom); }); equal(select.$()[0].selectedIndex, 2, "The selectedIndex accounts for the prompt"); select.$()[0].selectedIndex = 0; select.$().trigger('change'); equal(select.get('selection'), null, "When the prompt is selected again after another option, the selection should be null"); select.$()[0].selectedIndex = 2; select.$().trigger('change'); equal(select.get('selection'), tom, "Properly accounts for the prompt when DOM change occurs"); }); test("handles null content", function() { append(); run(function() { select.set('content', null); select.set('selection', 'invalid'); select.set('value', 'also_invalid'); }); equal(select.get('element').selectedIndex, -1, "should have no selection"); run(function() { select.set('multiple', true); select.set('selection', [{ content: 'invalid' }]); }); equal(select.get('element').selectedIndex, -1, "should have no selection"); }); test("valueBinding handles 0 as initiated value (issue #2763)", function() { var indirectData = EmberObject.create({ value: 0 }); run(function() { select.destroy(); // Destroy the existing select select = Ember.Select.extend({ content: Ember.A([1,0]), indirectData: indirectData, valueBinding: 'indirectData.value' }).create(); // append(); run(function() { select.appendTo('#qunit-fixture'); }); }); equal(select.get('value'), 0, "Value property should equal 0"); }); test("should be able to select an option and then reselect the prompt", function() { run(function() { select.set('content', Ember.A(['one', 'two', 'three'])); select.set('prompt', 'Select something'); }); append(); select.$()[0].selectedIndex = 2; select.$().trigger('change'); equal(select.get('selection'), 'two'); select.$()[0].selectedIndex = 0; select.$().trigger('change'); equal(select.get('selection'), null); equal(select.$()[0].selectedIndex, 0); }); test("should be able to get the current selection's value", function() { run(function() { select.set('content', Ember.A([ {label: 'Yehuda Katz', value: 'wycats'}, {label: 'Tom Dale', value: 'tomdale'}, {label: 'Peter Wagenet', value: 'wagenet'}, {label: 'Erik Bryn', value: 'ebryn'} ])); select.set('optionLabelPath', 'content.label'); select.set('optionValuePath', 'content.value'); }); append(); equal(select.get('value'), 'wycats'); }); test("should be able to set the current selection by value", function() { var ebryn = {label: 'Erik Bryn', value: 'ebryn'}; run(function() { select.set('content', Ember.A([ {label: 'Yehuda Katz', value: 'wycats'}, {label: 'Tom Dale', value: 'tomdale'}, {label: 'Peter Wagenet', value: 'wagenet'}, ebryn ])); select.set('optionLabelPath', 'content.label'); select.set('optionValuePath', 'content.value'); select.set('value', 'ebryn'); }); append(); equal(select.get('value'), 'ebryn'); equal(select.get('selection'), ebryn); }); QUnit.module("Ember.Select - usage inside templates", { setup: function() { dispatcher = EventDispatcher.create(); dispatcher.setup(); }, teardown: function() { run(function() { dispatcher.destroy(); if (view) { view.destroy(); } }); } }); test("works from a template with bindings", function() { var Person = EmberObject.extend({ id: null, firstName: null, lastName: null, fullName: computed(function() { return this.get('firstName') + " " + this.get('lastName'); }).property('firstName', 'lastName') }); var erik = Person.create({id: 4, firstName: 'Erik', lastName: 'Bryn'}); var application = Namespace.create(); application.peopleController = ArrayController.create({ content: Ember.A([ Person.create({id: 1, firstName: 'Yehuda', lastName: 'Katz'}), Person.create({id: 2, firstName: 'Tom', lastName: 'Dale'}), Person.create({id: 3, firstName: 'Peter', lastName: 'Wagenet'}), erik ]) }); application.selectedPersonController = EmberObject.create({ person: null }); view = EmberView.create({ app: application, selectView: SelectView, template: Ember.Handlebars.compile( '{{view view.selectView viewName="select"' + ' contentBinding="view.app.peopleController"' + ' optionLabelPath="content.fullName"' + ' optionValuePath="content.id"' + ' prompt="Pick a person:"' + ' selectionBinding="view.app.selectedPersonController.person"}}' ) }); run(function() { view.appendTo('#qunit-fixture'); }); var select = view.get('select'); ok(select.$().length, "Select was rendered"); equal(select.$('option').length, 5, "Options were rendered"); equal(select.$().text(), "Pick a person:Yehuda KatzTom DalePeter WagenetErik Bryn", "Option values were rendered"); equal(select.get('selection'), null, "Nothing has been selected"); run(function() { application.selectedPersonController.set('person', erik); }); equal(select.get('selection'), erik, "Selection was updated through binding"); run(function() { application.peopleController.pushObject(Person.create({id: 5, firstName: "James", lastName: "Rosen"})); }); equal(select.$('option').length, 6, "New option was added"); equal(select.get('selection'), erik, "Selection was maintained after new option was added"); }); test("upon content change, the DOM should reflect the selection (#481)", function() { var userOne = {name: 'Mike', options: Ember.A(['a', 'b']), selectedOption: 'a'}; var userTwo = {name: 'John', options: Ember.A(['c', 'd']), selectedOption: 'd'}; view = EmberView.create({ user: userOne, selectView: SelectView, template: Ember.Handlebars.compile( '{{view view.selectView viewName="select"' + ' contentBinding="view.user.options"' + ' selectionBinding="view.user.selectedOption"}}' ) }); run(function() { view.appendTo('#qunit-fixture'); }); var select = view.get('select'); var selectEl = select.$()[0]; equal(select.get('selection'), 'a', "Precond: Initial selection is correct"); equal(selectEl.selectedIndex, 0, "Precond: The DOM reflects the correct selection"); run(function() { view.set('user', userTwo); }); equal(select.get('selection'), 'd', "Selection was properly set after content change"); equal(selectEl.selectedIndex, 1, "The DOM reflects the correct selection"); }); test("upon content change with Array-like content, the DOM should reflect the selection", function() { var tom = {id: 4, name: 'Tom'}; var sylvain = {id: 5, name: 'Sylvain'}; var proxy = ArrayProxy.create({ content: Ember.A(), selectedOption: sylvain }); view = EmberView.create({ proxy: proxy, selectView: SelectView, template: Ember.Handlebars.compile( '{{view view.selectView viewName="select"' + ' contentBinding="view.proxy"' + ' selectionBinding="view.proxy.selectedOption"}}' ) }); run(function() { view.appendTo('#qunit-fixture'); }); var select = view.get('select'); var selectEl = select.$()[0]; equal(selectEl.selectedIndex, -1, "Precond: The DOM reflects the lack of selection"); run(function() { proxy.set('content', Ember.A([tom, sylvain])); }); equal(select.get('selection'), sylvain, "Selection was properly set after content change"); equal(selectEl.selectedIndex, 1, "The DOM reflects the correct selection"); }); function testValueBinding(templateString) { view = EmberView.create({ collection: Ember.A([{name: 'Wes', value: 'w'}, {name: 'Gordon', value: 'g'}]), val: 'g', selectView: SelectView, template: Ember.Handlebars.compile(templateString) }); run(function() { view.appendTo('#qunit-fixture'); }); var select = view.get('select'); var selectEl = select.$()[0]; equal(view.get('val'), 'g', "Precond: Initial bound property is correct"); equal(select.get('value'), 'g', "Precond: Initial selection is correct"); equal(selectEl.selectedIndex, 2, "Precond: The DOM reflects the correct selection"); select.$('option:eq(2)').removeAttr('selected'); select.$('option:eq(1)').prop('selected', true); select.$().trigger('change'); equal(view.get('val'), 'w', "Updated bound property is correct"); equal(select.get('value'), 'w', "Updated selection is correct"); equal(selectEl.selectedIndex, 1, "The DOM is updated to reflect the new selection"); } test("select element should correctly initialize and update selectedIndex and bound properties when using valueBinding (old xBinding='' syntax)", function() { testValueBinding( '{{view view.selectView viewName="select"' + ' contentBinding="view.collection"' + ' optionLabelPath="content.name"' + ' optionValuePath="content.value"' + ' prompt="Please wait..."' + ' valueBinding="view.val"}}' ); }); test("select element should correctly initialize and update selectedIndex and bound properties when using valueBinding (new quoteless binding shorthand)", function() { testValueBinding( '{{view view.selectView viewName="select"' + ' content=view.collection' + ' optionLabelPath="content.name"' + ' optionValuePath="content.value"' + ' prompt="Please wait..."' + ' value=view.val}}' ); }); }); enifed("ember-handlebars/tests/controls/select_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/controls'); test('ember-handlebars/tests/controls/select_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/controls/select_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/controls/text_area_test", ["ember-runtime/system/object","ember-metal/array","ember-metal/run_loop","ember-views/views/view","ember-handlebars/controls/text_area","ember-handlebars","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; /*globals TestObject:true */ var EmberObject = __dependency1__["default"]; var forEach = __dependency2__.forEach; var run = __dependency3__["default"]; var View = __dependency4__["default"]; var TextArea = __dependency5__["default"]; var EmberHandlebars = __dependency6__["default"]; var get = __dependency7__.get; var o_set = __dependency8__.set; var textArea, controller, TestObject; function set(object, key, value) { run(function() { o_set(object, key, value); }); } var compile = EmberHandlebars.compile; function append() { run(function() { textArea.appendTo('#qunit-fixture'); }); } function destroy(object) { run(function() { object.destroy(); }); } QUnit.module("{{textarea}}", { setup: function() { controller = { val: 'Lorem ipsum dolor' }; textArea = View.extend({ controller: controller, template: compile('{{textarea disabled=disabled value=val}}') }).create(); append(); }, teardown: function() { destroy(textArea); } }); test("Should insert a textarea", function() { equal(textArea.$('textarea').length, 1, "There is a single textarea"); }); test("Should become disabled when the controller changes", function() { ok(textArea.$('textarea').is(':not(:disabled)'), "Nothing is disabled yet"); set(controller, 'disabled', true); ok(textArea.$('textarea').is(':disabled'), "The disabled attribute is updated"); }); test("Should bind its contents to the specified value", function() { equal(textArea.$('textarea').val(), "Lorem ipsum dolor", "The contents are included"); set(controller, 'val', "sit amet"); equal(textArea.$('textarea').val(), "sit amet", "The new contents are included"); }); QUnit.module("TextArea", { setup: function() { TestObject = window.TestObject = EmberObject.create({ value: null }); textArea = TextArea.create(); }, teardown: function() { run(function() { textArea.destroy(); }); TestObject = window.TestObject = textArea = null; } }); test("should become disabled if the disabled attribute is true", function() { textArea.set('disabled', true); append(); ok(textArea.$().is(":disabled")); }); test("should become disabled if the disabled attribute is true", function() { append(); ok(textArea.$().is(":not(:disabled)")); run(function() { textArea.set('disabled', true); }); ok(textArea.$().is(":disabled")); run(function() { textArea.set('disabled', false); }); ok(textArea.$().is(":not(:disabled)")); }); test("input value is updated when setting value property of view", function() { run(function() { set(textArea, 'value', 'foo'); textArea.append(); }); equal(textArea.$().val(), "foo", "renders text field with value"); run(function() { set(textArea, 'value', 'bar'); }); equal(textArea.$().val(), "bar", "updates text field after value changes"); }); test("input placeholder is updated when setting placeholder property of view", function() { run(function() { set(textArea, 'placeholder', 'foo'); textArea.append(); }); equal(textArea.$().attr('placeholder'), "foo", "renders text area with placeholder"); run(function() { set(textArea, 'placeholder', 'bar'); }); equal(textArea.$().attr('placeholder'), "bar", "updates text area after placeholder changes"); }); test("input name is updated when setting name property of view", function() { run(function() { set(textArea, 'name', 'foo'); textArea.append(); }); equal(textArea.$().attr('name'), "foo", "renders text area with name"); run(function() { set(textArea, 'name', 'bar'); }); equal(textArea.$().attr('name'), "bar", "updates text area after name changes"); }); test("input maxlength is updated when setting maxlength property of view", function() { run(function() { set(textArea, 'maxlength', '300'); textArea.append(); }); equal(textArea.$().attr('maxlength'), "300", "renders text area with maxlength"); run(function() { set(textArea, 'maxlength', '400'); }); equal(textArea.$().attr('maxlength'), "400", "updates text area after maxlength changes"); }); test("input rows is updated when setting rows property of view", function() { run(function() { set(textArea, 'rows', '3'); textArea.append(); }); equal(textArea.$().attr('rows'), "3", "renders text area with rows"); run(function() { set(textArea, 'rows', '4'); }); equal(textArea.$().attr('rows'), "4", "updates text area after rows changes"); }); test("input cols is updated when setting cols property of view", function() { run(function() { set(textArea, 'cols', '30'); textArea.append(); }); equal(textArea.$().attr('cols'), "30", "renders text area with cols"); run(function() { set(textArea, 'cols', '40'); }); equal(textArea.$().attr('cols'), "40", "updates text area after cols changes"); }); test("input tabindex is updated when setting tabindex property of view", function() { run(function() { set(textArea, 'tabindex', '4'); textArea.append(); }); equal(textArea.$().attr('tabindex'), "4", "renders text area with the tabindex"); run(function() { set(textArea, 'tabindex', '1'); }); equal(textArea.$().attr('tabindex'), "1", "updates text area after tabindex changes"); }); test("input title is updated when setting title property of view", function() { run(function() { set(textArea, 'title', 'FooTitle'); textArea.append(); }); equal(textArea.$().attr('title'), "FooTitle", "renders text area with the title"); run(function() { set(textArea, 'title', 'BarTitle'); }); equal(textArea.$().attr('title'), 'BarTitle', "updates text area after title changes"); }); test("value binding works properly for inputs that haven't been created", function() { run(function() { textArea.destroy(); // destroy existing textarea textArea = TextArea.createWithMixins({ valueBinding: 'TestObject.value' }); }); equal(get(textArea, 'value'), null, "precond - default value is null"); equal(textArea.$(), undefined, "precond - view doesn't have its layer created yet, thus no input element"); run(function() { set(TestObject, 'value', 'ohai'); }); equal(get(textArea, 'value'), 'ohai', "value property was properly updated"); run(function() { textArea.append(); }); equal(get(textArea, 'value'), 'ohai', "value property remains the same once the view has been appended"); equal(textArea.$().val(), 'ohai', "value is reflected in the input element once it is created"); }); forEach.call([ 'cut', 'paste', 'input' ], function(eventName) { test("should update the value on " + eventName + " events", function() { run(function() { textArea.append(); }); textArea.$().val('new value'); textArea.trigger(eventName, EmberObject.create({ type: eventName })); equal(textArea.get('value'), 'new value', 'value property updates on ' + eventName + ' events'); }); }); test("should call the insertNewline method when return key is pressed", function() { var wasCalled; var event = EmberObject.create({ keyCode: 13 }); run(function() { textArea.append(); }); textArea.insertNewline = function() { wasCalled = true; }; textArea.trigger('keyUp', event); ok(wasCalled, "invokes insertNewline method"); }); test("should call the cancel method when escape key is pressed", function() { var wasCalled; var event = EmberObject.create({ keyCode: 27 }); run(function() { textArea.append(); }); textArea.cancel = function() { wasCalled = true; }; textArea.trigger('keyUp', event); ok(wasCalled, "invokes cancel method"); }); }); enifed("ember-handlebars/tests/controls/text_area_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/controls'); test('ember-handlebars/tests/controls/text_area_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/controls/text_area_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/controls/text_field_test", ["ember-metal/core","ember-metal/run_loop","ember-metal/property_get","ember-metal/property_set","ember-handlebars","ember-runtime/system/object","ember-views/views/view","ember-handlebars/controls/text_field","ember-views/system/event_dispatcher","ember-views/system/jquery"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__) { "use strict"; /*globals TestObject:true */ var Ember = __dependency1__["default"]; // Ember.K var run = __dependency2__["default"]; var get = __dependency3__.get; var o_set = __dependency4__.set; var EmberHandlebars = __dependency5__["default"]; var EmberObject = __dependency6__["default"]; var View = __dependency7__["default"]; var TextField = __dependency8__["default"]; var EventDispatcher = __dependency9__["default"]; var jQuery = __dependency10__["default"]; var textField; var K = Ember.K; var controller; var TestObject; function set(object, key, value) { run(function() { o_set(object, key, value); }); } function append() { run(function() { textField.appendTo('#qunit-fixture'); }); } function destroy(view) { run(function() { view.destroy(); }); } QUnit.module("{{input type='text'}}", { setup: function() { controller = { val: "hello", place: "Enter some text", name: "some-name", max: 30, size: 30, tab: 5 }; textField = View.extend({ controller: controller, template: compile('{{input type="text" disabled=disabled value=val placeholder=place name=name maxlength=max size=size tabindex=tab}}') }).create(); append(); }, teardown: function() { destroy(textField); } }); var compile = EmberHandlebars.compile; test("should insert a text field into DOM", function() { equal(textField.$('input').length, 1, "A single text field was inserted"); }); test("should become disabled if the disabled attribute is true", function() { ok(textField.$('input').is(':not(:disabled)'), "There are no disabled text fields"); set(controller, 'disabled', true); ok(textField.$('input').is(':disabled'), "The text field is disabled"); set(controller, 'disabled', false); ok(textField.$('input').is(':not(:disabled)'), "There are no disabled text fields"); }); test("input value is updated when setting value property of view", function() { equal(textField.$('input').val(), "hello", "renders text field with value"); set(controller, 'val', 'bye!'); equal(textField.$('input').val(), "bye!", "updates text field after value changes"); }); test("input placeholder is updated when setting placeholder property of view", function() { equal(textField.$('input').attr('placeholder'), "Enter some text", "renders text field with placeholder"); set(controller, 'place', 'Text, please enter it'); equal(textField.$('input').attr('placeholder'), "Text, please enter it", "updates text field after placeholder changes"); }); test("input name is updated when setting name property of view", function() { equal(textField.$('input').attr('name'), "some-name", "renders text field with name"); set(controller, 'name', 'other-name'); equal(textField.$('input').attr('name'), "other-name", "updates text field after name changes"); }); test("input maxlength is updated when setting maxlength property of view", function() { equal(textField.$('input').attr('maxlength'), "30", "renders text field with maxlength"); set(controller, 'max', 40); equal(textField.$('input').attr('maxlength'), "40", "updates text field after maxlength changes"); }); test("input size is updated when setting size property of view", function() { equal(textField.$('input').attr('size'), "30", "renders text field with size"); set(controller, 'size', 40); equal(textField.$('input').attr('size'), "40", "updates text field after size changes"); }); test("input tabindex is updated when setting tabindex property of view", function() { equal(textField.$('input').attr('tabindex'), "5", "renders text field with the tabindex"); set(controller, 'tab', 3); equal(textField.$('input').attr('tabindex'), "3", "updates text field after tabindex changes"); }); QUnit.module("{{input type='text'}} - static values", { setup: function() { controller = {}; textField = View.extend({ controller: controller, template: compile('{{input type="text" disabled=true value="hello" placeholder="Enter some text" name="some-name" maxlength=30 size=30 tabindex=5}}') }).create(); append(); }, teardown: function() { destroy(textField); } }); test("should insert a text field into DOM", function() { equal(textField.$('input').length, 1, "A single text field was inserted"); }); test("should become disabled if the disabled attribute is true", function() { ok(textField.$('input').is(':disabled'), "The text field is disabled"); }); test("input value is updated when setting value property of view", function() { equal(textField.$('input').val(), "hello", "renders text field with value"); }); test("input placeholder is updated when setting placeholder property of view", function() { equal(textField.$('input').attr('placeholder'), "Enter some text", "renders text field with placeholder"); }); test("input name is updated when setting name property of view", function() { equal(textField.$('input').attr('name'), "some-name", "renders text field with name"); }); test("input maxlength is updated when setting maxlength property of view", function() { equal(textField.$('input').attr('maxlength'), "30", "renders text field with maxlength"); }); test("input size is updated when setting size property of view", function() { equal(textField.$('input').attr('size'), "30", "renders text field with size"); }); test("input tabindex is updated when setting tabindex property of view", function() { equal(textField.$('input').attr('tabindex'), "5", "renders text field with the tabindex"); }); QUnit.module("{{input type='text'}} - dynamic type", { setup: function() { controller = { someProperty: 'password' }; textField = View.extend({ controller: controller, template: compile('{{input type=someProperty}}') }).create(); append(); }, teardown: function() { destroy(textField); } }); test("should insert a text field into DOM", function() { equal(textField.$('input').attr('type'), 'password', "a bound property can be used to determine type."); }); QUnit.module("{{input}} - default type", { setup: function() { controller = {}; textField = View.extend({ controller: controller, template: compile('{{input}}') }).create(); append(); }, teardown: function() { destroy(textField); } }); test("should have the default type", function() { equal(textField.$('input').attr('type'), 'text', "Has a default text type"); }); QUnit.module("Ember.TextField", { setup: function() { TestObject = window.TestObject = EmberObject.create({ value: null }); textField = TextField.create(); }, teardown: function() { run(function() { textField.destroy(); }); TestObject = window.TestObject = textField = null; } }); test("should become disabled if the disabled attribute is true", function() { textField.set('disabled', true); append(); ok(textField.$().is(":disabled")); }); test("should become disabled if the disabled attribute is true", function() { append(); ok(textField.$().is(":not(:disabled)")); run(function() { textField.set('disabled', true); }); ok(textField.$().is(":disabled")); run(function() { textField.set('disabled', false); }); ok(textField.$().is(":not(:disabled)")); }); test("input value is updated when setting value property of view", function() { run(function() { set(textField, 'value', 'foo'); textField.append(); }); equal(textField.$().val(), "foo", "renders text field with value"); run(function() { set(textField, 'value', 'bar'); }); equal(textField.$().val(), "bar", "updates text field after value changes"); }); test("input placeholder is updated when setting placeholder property of view", function() { run(function() { set(textField, 'placeholder', 'foo'); textField.append(); }); equal(textField.$().attr('placeholder'), "foo", "renders text field with placeholder"); run(function() { set(textField, 'placeholder', 'bar'); }); equal(textField.$().attr('placeholder'), "bar", "updates text field after placeholder changes"); }); test("input name is updated when setting name property of view", function() { run(function() { set(textField, 'name', 'foo'); textField.append(); }); equal(textField.$().attr('name'), "foo", "renders text field with name"); run(function() { set(textField, 'name', 'bar'); }); equal(textField.$().attr('name'), "bar", "updates text field after name changes"); }); test("input maxlength is updated when setting maxlength property of view", function() { run(function() { set(textField, 'maxlength', '30'); textField.append(); }); equal(textField.$().attr('maxlength'), "30", "renders text field with maxlength"); run(function() { set(textField, 'maxlength', '40'); }); equal(textField.$().attr('maxlength'), "40", "updates text field after maxlength changes"); }); test("input size is updated when setting size property of view", function() { run(function() { set(textField, 'size', '30'); textField.append(); }); equal(textField.$().attr('size'), "30", "renders text field with size"); run(function() { set(textField, 'size', '40'); }); equal(textField.$().attr('size'), "40", "updates text field after size changes"); }); test("input tabindex is updated when setting tabindex property of view", function() { run(function() { set(textField, 'tabindex', '5'); textField.append(); }); equal(textField.$().attr('tabindex'), "5", "renders text field with the tabindex"); run(function() { set(textField, 'tabindex', '3'); }); equal(textField.$().attr('tabindex'), "3", "updates text field after tabindex changes"); }); test("input title is updated when setting title property of view", function() { run(function() { set(textField, 'title', 'FooTitle'); textField.append(); }); equal(textField.$().attr('title'), "FooTitle", "renders text field with the title"); run(function() { set(textField, 'title', 'BarTitle'); }); equal(textField.$().attr('title'), "BarTitle", "updates text field after title changes"); }); test("input type is configurable when creating view", function() { run(function() { set(textField, 'type', 'password'); textField.append(); }); equal(textField.$().attr('type'), 'password', "renders text field with type"); }); test("value binding works properly for inputs that haven't been created", function() { run(function() { textField.destroy(); // destroy existing textField textField = TextField.createWithMixins({ valueBinding: 'TestObject.value' }); }); equal(get(textField, 'value'), null, "precond - default value is null"); equal(textField.$(), undefined, "precond - view doesn't have its layer created yet, thus no input element"); run(function() { set(TestObject, 'value', 'ohai'); }); equal(get(textField, 'value'), 'ohai', "value property was properly updated"); run(function() { textField.append(); }); equal(get(textField, 'value'), 'ohai', "value property remains the same once the view has been appended"); equal(textField.$().val(), 'ohai', "value is reflected in the input element once it is created"); }); test("value binding sets value on the element", function() { run(function() { textField.destroy(); // destroy existing textField textField = TextField.createWithMixins({ valueBinding: 'TestObject.value' }); textField.append(); }); // Set the value via the DOM run(function() { textField.$().val('via dom'); // Trigger lets the view know we changed this value (like a real user editing) textField.trigger('input', EmberObject.create({ type: 'input' })); }); equal(get(textField, 'value'), 'via dom', "value property was properly updated via dom"); equal(textField.$().val(), 'via dom', "dom property was properly updated via dom"); // Now, set it via the binding run(function() { set(TestObject, 'value', 'via view'); }); equal(get(textField, 'value'), 'via view', "value property was properly updated via view"); equal(textField.$().val(), 'via view', "dom property was properly updated via view"); }); test("should call the insertNewline method when return key is pressed", function() { var wasCalled; var event = EmberObject.create({ keyCode: 13 }); run(function() { textField.append(); }); textField.insertNewline = function() { wasCalled = true; }; textField.trigger('keyUp', event); ok(wasCalled, "invokes insertNewline method"); }); test("should call the cancel method when escape key is pressed", function() { var wasCalled; var event = EmberObject.create({ keyCode: 27 }); run(function() { textField.append(); }); textField.cancel = function() { wasCalled = true; }; textField.trigger('keyUp', event); ok(wasCalled, "invokes cancel method"); }); test("should send an action if one is defined when the return key is pressed", function() { expect(2); var StubController = EmberObject.extend({ send: function(actionName, value, sender) { equal(actionName, 'didTriggerAction', "text field sent correct action name"); equal(value, "textFieldValue", "text field sent its current value as first argument"); } }); textField.set('action', 'didTriggerAction'); textField.set('value', "textFieldValue"); textField.set('targetObject', StubController.create()); run(function() { textField.append(); }); var event = { keyCode: 13, stopPropagation: K }; textField.trigger('keyUp', event); }); test("should send an action on keyPress if one is defined with onEvent=keyPress", function() { expect(2); var StubController = EmberObject.extend({ send: function(actionName, value, sender) { equal(actionName, 'didTriggerAction', "text field sent correct action name"); equal(value, "textFieldValue", "text field sent its current value as first argument"); } }); textField.set('action', 'didTriggerAction'); textField.set('onEvent', 'keyPress'); textField.set('value', "textFieldValue"); textField.set('targetObject', StubController.create()); run(function() { textField.append(); }); var event = { keyCode: 48, stopPropagation: K }; textField.trigger('keyPress', event); }); test("bubbling of handled actions can be enabled via bubbles property", function() { textField.set('bubbles', true); textField.set('action', 'didTriggerAction'); textField.set('controller', EmberObject.create({ send: K })); append(); var stopPropagationCount = 0; var event = { keyCode: 13, stopPropagation: function() { stopPropagationCount++; } }; textField.trigger('keyUp', event); equal(stopPropagationCount, 0, "propagation was not prevented if bubbles is true"); textField.set('bubbles', false); textField.trigger('keyUp', event); equal(stopPropagationCount, 1, "propagation was prevented if bubbles is false"); }); var dispatcher, StubController; QUnit.module("Ember.TextField - Action events", { setup: function() { dispatcher = EventDispatcher.create(); dispatcher.setup(); StubController = EmberObject.extend({ send: function(actionName, value, sender) { equal(actionName, 'doSomething', "text field sent correct action name"); } }); }, teardown: function() { run(function() { dispatcher.destroy(); if (textField) { textField.destroy(); } }); } }); test("when the text field is blurred, the `focus-out` action is sent to the controller", function() { expect(1); textField = TextField.create({ 'focus-out': 'doSomething', targetObject: StubController.create({}) }); append(); run(function() { textField.$().blur(); }); }); test("when the text field is focused, the `focus-in` action is sent to the controller", function() { expect(1); textField = TextField.create({ 'focus-in': 'doSomething', targetObject: StubController.create({}) }); append(); run(function() { textField.$().focusin(); }); }); test("when the user presses a key, the `key-press` action is sent to the controller", function() { expect(1); textField = TextField.create({ 'key-press': 'doSomething', targetObject: StubController.create({}) }); append(); run(function() { var event = jQuery.Event("keypress"); event.keyCode = event.which = 13; textField.$().trigger(event); }); }); test("when the user inserts a new line, the `insert-newline` action is sent to the controller", function() { expect(1); textField = TextField.create({ 'insert-newline': 'doSomething', targetObject: StubController.create({}) }); append(); run(function() { var event = jQuery.Event("keyup"); event.keyCode = event.which = 13; textField.$().trigger(event); }); }); test("when the user presses the `enter` key, the `enter` action is sent to the controller", function() { expect(1); textField = TextField.create({ 'enter': 'doSomething', targetObject: StubController.create({}) }); append(); run(function() { var event = jQuery.Event("keyup"); event.keyCode = event.which = 13; textField.$().trigger(event); }); }); test("when the user hits escape, the `escape-press` action is sent to the controller", function() { expect(1); textField = TextField.create({ 'escape-press': 'doSomething', targetObject: StubController.create({}) }); append(); run(function() { var event = jQuery.Event("keyup"); event.keyCode = event.which = 27; textField.$().trigger(event); }); }); test("when the user presses a key, the `key-down` action is sent to the controller", function() { expect(3); var event; textField = TextField.create({ 'key-down': 'doSomething', targetObject: StubController.create({ send: function(actionName, value, evt) { equal(actionName, 'doSomething', "text field sent correct action name"); equal(value, '', 'value was blank in key-down'); equal(evt, event, 'event was received as param'); } }) }); append(); run(function() { event = jQuery.Event("keydown"); event.keyCode = event.which = 65; textField.$().val('foo'); textField.$().trigger(event); }); }); test("when the user releases a key, the `key-up` action is sent to the controller", function() { expect(3); var event; textField = TextField.create({ 'key-up': 'doSomething', targetObject: StubController.create({ send: function(actionName, value, evt) { equal(actionName, 'doSomething', "text field sent correct action name"); equal(value, 'bar', 'value was received'); equal(evt, event, 'event was received as param'); } }) }); append(); run(function() { event = jQuery.Event("keyup"); event.keyCode = event.which = 65; textField.$().val('bar'); textField.$().trigger(event); }); }); }); enifed("ember-handlebars/tests/controls/text_field_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/controls'); test('ember-handlebars/tests/controls/text_field_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/controls/text_field_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/handlebars_get_test", ["ember-metal/core","ember-handlebars/views/metamorph_view","ember-views/views/view","ember-metal/run_loop","ember-handlebars","ember-handlebars/ext","ember-runtime/system/container"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.lookup var _MetamorphView = __dependency2__["default"]; var EmberView = __dependency3__["default"]; var run = __dependency4__["default"]; var EmberHandlebars = __dependency5__["default"]; var handlebarsGet = __dependency6__.handlebarsGet; var Container = __dependency7__["default"]; var compile = EmberHandlebars.compile; var originalLookup = Ember.lookup; var TemplateTests, container, lookup, view; function appendView() { run(view, 'appendTo', '#qunit-fixture'); } QUnit.module("Ember.Handlebars.get", { setup: function() { Ember.lookup = lookup = {}; container = new Container(); container.optionsForType('template', { instantiate: false }); container.optionsForType('helper', { instantiate: false }); 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; }); Ember.lookup = lookup = originalLookup; TemplateTests = null; } }); test('it can lookup a path from the current context', function() { expect(1); container.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; ignoreDeprecation(function() { equal(handlebarsGet(context, path, options), 'bar'); }); }); view = EmberView.create({ container: container, controller: { foo: 'bar' }, template: compile('{{handlebars-get "foo"}}') }); appendView(); }); test('it can lookup a path from the current keywords', function() { expect(1); container.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; ignoreDeprecation(function() { equal(handlebarsGet(context, path, options), 'bar'); }); }); view = EmberView.create({ container: container, controller: { foo: 'bar' }, template: compile('{{#with foo as bar}}{{handlebars-get "bar"}}{{/with}}') }); appendView(); }); test('it can lookup a path from globals', function() { expect(1); lookup.Blammo = { foo: 'blah'}; container.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; ignoreDeprecation(function() { equal(handlebarsGet(context, path, options), lookup.Blammo.foo); }); }); view = EmberView.create({ container: container, controller: { }, template: compile('{{handlebars-get "Blammo.foo"}}') }); appendView(); }); test('it raises a deprecation warning on use', function() { expect(1); container.register('helper:handlebars-get', function(path, options) { var context = options.contexts && options.contexts[0] || this; expectDeprecation(function() { handlebarsGet(context, path, options); }, 'Usage of Ember.Handlebars.get is deprecated, use a Component or Ember.Handlebars.makeBoundHelper instead.'); }); view = EmberView.create({ container: container, controller: { foo: 'bar' }, template: compile('{{handlebars-get "foo"}}') }); appendView(); }); }); enifed("ember-handlebars/tests/handlebars_get_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests'); test('ember-handlebars/tests/handlebars_get_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/handlebars_get_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/handlebars_test", ["ember-metal/core","ember-views/system/jquery","ember-metal/enumerable_utils","ember-metal/run_loop","ember-runtime/system/namespace","ember-views/views/view","ember-handlebars/views/metamorph_view","ember-handlebars","ember-runtime/system/object","ember-runtime/controllers/object_controller","ember-runtime/system/native_array","ember-metal/computed","ember-runtime/system/string","ember-metal/utils","ember-runtime/system/array_proxy","ember-views/views/collection_view","ember-views/views/container_view","ember-metal/binding","ember-metal/observer","ember-handlebars/controls/text_field","ember-runtime/system/container","ember-metal/platform","ember-handlebars/string","ember-metal/property_get","ember-metal/property_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__) { "use strict"; /*jshint newcap:false*/ var Ember = __dependency1__["default"]; // Ember.lookup var jQuery = __dependency2__["default"]; // import {expectAssertion} from "ember-metal/tests/debug_helpers"; var forEach = __dependency3__.forEach; var run = __dependency4__["default"]; var Namespace = __dependency5__["default"]; var EmberView = __dependency6__["default"]; var _MetamorphView = __dependency7__["default"]; var EmberHandlebars = __dependency8__["default"]; var EmberObject = __dependency9__["default"]; var ObjectController = __dependency10__["default"]; var A = __dependency11__.A; var computed = __dependency12__.computed; var fmt = __dependency13__.fmt; var typeOf = __dependency14__.typeOf; var ArrayProxy = __dependency15__["default"]; var CollectionView = __dependency16__["default"]; var ContainerView = __dependency17__["default"]; var Binding = __dependency18__.Binding; var observersFor = __dependency19__.observersFor; var TextField = __dependency20__["default"]; var Container = __dependency21__["default"]; var o_create = __dependency22__.create; var htmlSafe = __dependency23__["default"]; var trim = jQuery.trim; var get = __dependency24__.get; var set = __dependency25__.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 originalLog, logCalls; var caretPosition = function (element) { var ctrl = element[0]; var caretPos = 0; // IE Support if (document.selection) { ctrl.focus(); var selection = document.selection.createRange(); selection.moveStart('character', -ctrl.value.length); caretPos = selection.text.length; } // Firefox support else if (ctrl.selectionStart || ctrl.selectionStart === '0') { caretPos = ctrl.selectionStart; } return caretPos; }; var setCaretPosition = function (element, pos) { var ctrl = element[0]; if (ctrl.setSelectionRange) { ctrl.focus(); ctrl.setSelectionRange(pos,pos); } else if (ctrl.createTextRange) { var range = ctrl.createTextRange(); range.collapse(true); range.moveEnd('character', pos); range.moveStart('character', pos); range.select(); } }; var view; var appendView = function() { run(function() { view.appendTo('#qunit-fixture'); }); }; var originalLookup = Ember.lookup; var TemplateTests, container, lookup; /** This module specifically tests integration with Handlebars and Ember-specific Handlebars extensions. If you add additional template support to View, you should create a new file in which to test. */ QUnit.module("View - handlebars integration", { setup: function() { Ember.lookup = lookup = {}; lookup.TemplateTests = TemplateTests = Namespace.create(); container = new Container(); container.optionsForType('template', { instantiate: false }); 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; }); Ember.lookup = lookup = originalLookup; TemplateTests = null; } }); test("template view should call the function of the associated template", function() { container.register('template:testTemplate', EmberHandlebars.compile("

    template was called

    ")); view = EmberView.create({ container: container, templateName: 'testTemplate' }); appendView(); ok(view.$('#twas-called').length, "the named template was called"); }); test("{{view}} should not override class bindings defined on a child view", function() { var LabelView = EmberView.extend({ container: container, templateName: 'nested', classNameBindings: ['something'], something: 'visible' }); container.register('controller:label', ObjectController, { instantiate: true }); container.register('view:label', LabelView); container.register('template:label', EmberHandlebars.compile('
    ')); container.register('template:nester', EmberHandlebars.compile('{{render "label"}}')); view = EmberView.create({ container: container, templateName: 'nester', controller: ObjectController.create({ container: container }) }); appendView(); ok(view.$('.visible').length > 0, 'class bindings are not overriden'); }); 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 as person}}{{person.firstName}} {{person.lastName}} (and {{person.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("should read from globals (DEPRECATED)", function() { Ember.lookup.Global = 'Klarg'; view = EmberView.create({ template: EmberHandlebars.compile('{{Global}}') }); expectDeprecation(function(){ appendView(); }, "Global lookup of Global from a Handlebars template is deprecated."); equal(view.$().text(), Ember.lookup.Global); }); test("should read from globals with a path (DEPRECATED)", function() { Ember.lookup.Global = { Space: 'Klarg' }; view = EmberView.create({ template: EmberHandlebars.compile('{{Global.Space}}') }); expectDeprecation(function(){ appendView(); }, "Global lookup of Global.Space from a Handlebars template is deprecated."); equal(view.$().text(), Ember.lookup.Global.Space); }); test("with context, should read from globals (DEPRECATED)", function() { Ember.lookup.Global = 'Klarg'; view = EmberView.create({ context: {}, template: EmberHandlebars.compile('{{Global}}') }); expectDeprecation(function(){ appendView(); }, "Global lookup of Global from a Handlebars template is deprecated."); equal(view.$().text(), Ember.lookup.Global); }); test("with context, should read from globals with a path (DEPRECATED)", function() { Ember.lookup.Global = { Space: 'Klarg' }; view = EmberView.create({ context: {}, template: EmberHandlebars.compile('{{Global.Space}}') }); expectDeprecation(function(){ appendView(); }, "Global lookup of Global.Space from a Handlebars template is deprecated."); equal(view.$().text(), Ember.lookup.Global.Space); }); test("should read from a global-ish simple local path without deprecation", function() { view = EmberView.create({ context: { NotGlobal: 'Gwar' }, template: EmberHandlebars.compile('{{NotGlobal}}') }); expectNoDeprecation(); appendView(); equal(view.$().text(), 'Gwar'); }); test("should read a number value", function() { var context = { aNumber: 1 }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{aNumber}}') }); appendView(); equal(view.$().text(), '1'); Ember.run(function(){ Ember.set(context, 'aNumber', 2); }); equal(view.$().text(), '2'); }); test("should read an escaped number value", function() { var context = { aNumber: 1 }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{{aNumber}}}') }); appendView(); equal(view.$().text(), '1'); Ember.run(function(){ Ember.set(context, 'aNumber', 2); }); equal(view.$().text(), '2'); }); test("should read from an Object.create(null)", function() { // Use ember's polyfill for Object.create var nullObject = o_create(null); nullObject['foo'] = 'bar'; view = EmberView.create({ context: { nullObject: nullObject }, template: EmberHandlebars.compile('{{nullObject.foo}}') }); appendView(); equal(view.$().text(), 'bar'); Ember.run(function(){ Ember.set(nullObject, 'foo', 'baz'); }); equal(view.$().text(), 'baz'); }); 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 view.labelView}}")); container.register('template:nested', EmberHandlebars.compile("
    Goodbye {{cruel}} {{world}}
    ")); var context = { world: "world!" }; var LabelView = EmberView.extend({ container: container, tagName: "aside", templateName: 'nested' }); view = EmberView.create({ labelView: LabelView, 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("child views can be inserted inside a bind block", function() { container.register('template:nester', EmberHandlebars.compile("

    Hello {{world}}

    {{view view.bqView}}")); container.register('template:nested', EmberHandlebars.compile("
    Goodbye {{#with content as thing}}{{thing.blah}} {{view view.otherView}}{{/with}} {{world}}
    ")); container.register('template:other', EmberHandlebars.compile("cruel")); var context = { world: "world!" }; var OtherView = EmberView.extend({ container: container, templateName: 'other' }); var BQView = EmberView.extend({ container: container, otherView: OtherView, tagName: "blockquote", templateName: 'nested' }); view = EmberView.create({ container: container, bqView: BQView, 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("using Handlebars helper that doesn't exist should result in an error", function() { var names = [{ name: 'Alex' }, { name: 'Stef' }]; var 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 update when a property changes and the bind helper is used", function() { container.register('template:foo', EmberHandlebars.compile('

    {{#with view.content as thing}}{{bind "thing.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 as thing}}{{thing.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 [DEPRECATED]", 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" }) }); expectDeprecation(function() { appendView(); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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; var child = null; view = EmberView.create({ cond1: true, cond2: false, viewClass: EmberView.extend({ init: function() { this._super(); childCreated = true; child = this; } }), template: EmberHandlebars.compile('{{#if view.cond1}}{{#if view.cond2}}{{#view view.viewClass}}test{{/view}}{{/if}}{{/if}}') }); appendView(); ok(!childCreated, 'precondition'); run(function() { // The order of these sets is important for the test view.set('cond2', true); view.set('cond1', false); }); // TODO: Priority Queue, for now ensure correct result. //ok(!childCreated, 'child should not be created'); ok(child.isDestroyed, 'child should be gone'); equal(view.$().text(), ''); }); 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 view.childView}}
    ')); container.register('template:child', EmberHandlebars.compile("I can't believe it's not butter.")); var ChildView = EmberView.extend({ container: container, templateName: 'child' }); view = EmberView.create({ container: container, childView: ChildView, 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}}It worked!{{/view}}

    ')); 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 as person}}{{#view}}{{person.firstName}} {{person.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 as person}}{{#view person.subview}}{{view.firstName}} {{person.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() { var template = '{{#view}}{{#view}}{{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() { var template = '{{view}}{{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() { var template = '{{#view}}{{view viewName="ohai"}}{{/view}}'; view = EmberView.create({ template: EmberHandlebars.compile(template) }); appendView(); var parentView = firstChild(view); var 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() { var ExampleViewCollection = CollectionView.extend({ itemViewClass: EmberView.extend({ isCustom: true }), content: A(['foo']) }); view = EmberView.create({ exampleViewCollection: ExampleViewCollection, template: EmberHandlebars.compile('{{#collection view.exampleViewCollection}}OHAI{{/collection}}') }); run(function() { view.append(); }); ok(firstGrandchild(view).isCustom, "uses the example view class"); }); test("itemViewClass works in the #collection helper with a global (DEPRECATED)", function() { TemplateTests.ExampleItemView = EmberView.extend({ isAlsoCustom: true }); view = EmberView.create({ exampleController: ArrayProxy.create({ content: A(['alpha']) }), template: EmberHandlebars.compile('{{#collection content=view.exampleController itemViewClass=TemplateTests.ExampleItemView}}beta{{/collection}}') }); expectDeprecation(function(){ run(view, 'append'); }, /Resolved the view "TemplateTests.ExampleItemView" on the global context/); ok(firstGrandchild(view).isAlsoCustom, "uses the example view class specified in the #collection helper"); }); test("itemViewClass works in the #collection helper with a property", function() { var ExampleItemView = EmberView.extend({ isAlsoCustom: true }); var ExampleCollectionView = CollectionView; view = EmberView.create({ possibleItemView: ExampleItemView, exampleCollectionView: ExampleCollectionView, exampleController: ArrayProxy.create({ content: A(['alpha']) }), template: EmberHandlebars.compile('{{#collection view.exampleCollectionView content=view.exampleController itemViewClass=view.possibleItemView}}beta{{/collection}}') }); run(function() { view.append(); }); ok(firstGrandchild(view).isAlsoCustom, "uses the example view class specified in the #collection helper"); }); test("itemViewClass works in the #collection via container", function() { container.register('view:example-item', EmberView.extend({ isAlsoCustom: true })); view = EmberView.create({ container: container, exampleCollectionView: CollectionView.extend(), exampleController: ArrayProxy.create({ content: A(['alpha']) }), template: EmberHandlebars.compile('{{#collection view.exampleCollectionView content=view.exampleController itemViewClass="example-item"}}beta{{/collection}}') }); run(function() { view.append(); }); ok(firstGrandchild(view).isAlsoCustom, "uses the example view class specified in the #collection helper"); }); 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("{{view}} id attribute should set id on layer", function() { container.register('template:foo', EmberHandlebars.compile('{{#view view.idView id="bar"}}baz{{/view}}')); var IdView = EmberView; view = EmberView.create({ idView: IdView, 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 view.tagView tag="span"}}baz{{/view}}')); var TagView = EmberView; view = EmberView.create({ tagView: TagView, 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 view.idView class="bar"}}baz{{/view}}')); var IdView = EmberView; view = EmberView.create({ idView: IdView, 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 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 DEPRECATED", function() { var App; run(function() { lookup.App = App = Namespace.create({ isApp: true, isGreat: true, directClass: "app-direct", isEnabled: true }); }); view = EmberView.create({ textField: TextField, template: EmberHandlebars.compile('{{view view.textField class="unbound" classBinding="App.isGreat:great App.directClass App.isApp App.isEnabled:enabled:disabled"}}') }); expectDeprecation(function() { 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, textField: TextField, template: EmberHandlebars.compile('{{view view.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 from globals DEPRECATED", function() { var App; run(function() { lookup.App = App = Namespace.create({ isGreat: true, isEnabled: true }); }); view = EmberView.create({ textField: TextField, template: EmberHandlebars.compile('{{view view.textField class="unbound" classBinding="App.isGreat:great App.isEnabled:enabled:disabled" classNameBindings="App.isGreat:really-great App.isEnabled:really-enabled:really-disabled"}}') }); expectDeprecation(function() { 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({ textField: TextField, template: EmberHandlebars.compile('{{view view.textField valueBinding="App.name"}}') }); expectDeprecation(function() { appendView(); }, 'Global lookup of App.name from a Handlebars template is deprecated.'); equal(view.$('input').val(), "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", textField: TextField, template: EmberHandlebars.compile('{{view view.textField valueBinding="view.name"}}') }); appendView(); equal(view.$('input').val(), "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 view.classBindingView classBinding="view.number:is-truthy"}}foo{{/view}}')); var ClassBindingView = EmberView.extend(); view = EmberView.create({ classBindingView: ClassBindingView, 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 view.classBindingView classBinding="view.number:is-truthy:is-falsy"}}foo{{/view}}')); var ClassBindingView = EmberView.extend(); view = EmberView.create({ classBindingView: ClassBindingView, 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 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 output a property without binding", function() { var context = { content: EmberObject.create({ anUnboundString: "No spans here, son." }) }; view = EmberView.create({ context: context, template: EmberHandlebars.compile( '
    {{unbound content.anUnboundString}}
    ' ) }); appendView(); equal(view.$('#first').html(), "No spans here, son."); }); 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 item in view.items}}
    • {{unbound item}}
    • {{/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 item in view.items}}
    • {{unbound item.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}}"); var 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 view.containerView}}"; view = EmberView.create({ containerView: ContainerView, 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 [DEPRECATED]", 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) }); expectDeprecation(function() { appendView(); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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' }); var CustomContextView = EmberView.extend({ context: context, template: EmberHandlebars.compile("{{test}}") }); view = EmberView.create({ customContextView: CustomContextView, template: EmberHandlebars.compile("{{view view.customContextView}}") }); appendView(); equal(view.$().text(), "test"); }); test("should escape HTML in primitive value contexts when using normal mustaches", function() { view = EmberView.create({ context: 'MaxJames', template: EmberHandlebars.compile('{{this}}'), }); appendView(); equal(view.$('b').length, 0, "does not create an element"); equal(view.$().text(), 'MaxJames', "inserts entities, not elements"); run(function() { set(view, 'context', 'MaxJames'); }); equal(view.$().text(), 'MaxJames', "updates with entities, not elements"); equal(view.$('i').length, 0, "does not create an element when value is updated"); }); test("should not escape HTML in primitive value contexts when using triple mustaches", function() { view = EmberView.create({ context: 'MaxJames', template: EmberHandlebars.compile('{{{this}}}'), }); appendView(); equal(view.$('b').length, 2, "creates an element"); run(function() { set(view, 'context', 'MaxJames'); }); 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' }; view = EmberView.create({ context: context, template: EmberHandlebars.compile('{{log value}}') }); appendView(); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one', "should call log with value"); }); 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({ context: 'one', template: EmberHandlebars.compile('{{log this}}'), }); appendView(); equal(view.$().text(), "", "shouldn't render any text"); equal(logCalls[0], 'one', "should call log with item one"); }); 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}}{{input}}{{/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({ context: 'abc', template: EmberHandlebars.compile('{{#if view.show}}{{this}}{{/if}}'), show: true }); 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 [DEPRECATED]", 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 museum=this}} {{/with}}{{/if}}') }); expectDeprecation(function() { appendView(); }, 'Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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 LinkView = EmberView.extend({ classNames: ['app-link'], tagName: 'a', attributeBindings: ['href'], href: '#none', click: function() { return false; } }); var parentView = EmberView.create({ linkView: LinkView, test: EmberObject.create({ href: 'test' }), template: EmberHandlebars.compile('{{#view view.linkView hrefBinding="view.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(); }); }); 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() { expectDeprecation("Setting `childViews` on a Container is deprecated."); 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 provide a helpful assertion for bindings within HTML comments", function() { view = EmberView.create({ template: EmberHandlebars.compile(''), someThing: 'foo', _debugTemplateName: 'blahzorz' }); expectAssertion(function() { appendView(); }, 'An error occured while setting up template bindings. Please check "blahzorz" template for invalid markup or bindings within HTML comments.'); }); }); enifed("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.'); }); }); enifed("ember-handlebars/tests/helpers/bind_test", ["ember-views/views/view","ember-runtime/system/object","ember-metal/run_loop"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; var EmberView = __dependency1__["default"]; var EmberObject = __dependency2__["default"]; var run = __dependency3__["default"]; function appendView(view) { run(function() { view.appendTo('#qunit-fixture'); }); } var view; QUnit.module("Handlebars {{#bind}} helper", { teardown: function() { if (view) { run(view, view.destroy); view = null; } } }); test("it should render the current value of a property on the context", function() { view = EmberView.create({ template: Ember.Handlebars.compile('{{bind "foo"}}'), context: EmberObject.create({ foo: "BORK" }) }); appendView(view); equal(view.$().text(), "BORK", "initial value is rendered"); run(view, view.set, 'context.foo', 'MWEEER'); equal(view.$().text(), "MWEEER", "value can be updated"); }); test("it should render the current value of a path on the context", function() { view = EmberView.create({ template: Ember.Handlebars.compile('{{bind "foo.bar"}}'), context: EmberObject.create({ foo: { bar: "BORK" } }) }); appendView(view); equal(view.$().text(), "BORK", "initial value is rendered"); run(view, view.set, 'context.foo.bar', 'MWEEER'); equal(view.$().text(), "MWEEER", "value can be updated"); }); }); enifed("ember-handlebars/tests/helpers/bind_test.jshint", [], function() { "use strict"; module('JSHint - ember-handlebars/tests/helpers'); test('ember-handlebars/tests/helpers/bind_test.js should pass jshint', function() { ok(true, 'ember-handlebars/tests/helpers/bind_test.js should pass jshint.'); }); }); enifed("ember-handlebars/tests/helpers/bound_helper_test", ["ember-views/views/view","ember-metal/run_loop","ember-runtime/system/object","ember-runtime/system/native_array","ember-metal/property_get","ember-metal/property_set","ember-handlebars-compiler"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { "use strict"; /*jshint newcap:false*/ var EmberView = __dependency1__["default"]; var run = __dependency2__["default"]; var EmberObject = __dependency3__["default"]; var A = __dependency4__.A; // import {expectAssertion} from "ember-metal/tests/debug_helpers"; var get = __dependency5__.get; var set = __dependency6__.set; var EmberHandlebars = __dependency7__["default"]; var compile = EmberHandlebars.compile; var view; var originalLookup = Ember.lookup; 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() { }, teardown: function() { run(function() { if (view) { view.destroy(); } }); Ember.lookup = originalLookup; } }); test("primitives should work correctly [DEPRECATED]", function() { expectDeprecation('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); expectDeprecation('Using the context switching form of `{{with}}` is deprecated. Please use the keyword form (`{{with foo as bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); view = EmberView.create({ prims: Ember.A(["string", 12]), template: compile('{{#each view.prims}}{{#if this}}inside-if{{/if}}{{#with this}}inside-with{{/with}}{{/each}}') }); appendView(view); equal(view.$().text(), 'inside-ifinside-withinside-ifinside-with'); }); 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: 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: 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: compile("{{repeat text count=3}}") }); appendView(); equal(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: compile("{{capitalize view.text}}") }); appendView(); equal(view.$().text(), 'AB', "helper output is correct"); }); test("bound helpers should support global paths [DEPRECATED]", function() { EmberHandlebars.helper('capitalize', function(value) { return value.toUpperCase(); }); Ember.lookup = {Text: 'ab'}; view = EmberView.create({ template: compile("{{capitalize Text}}") }); expectDeprecation(function() { appendView(); }, /Global lookup of Text from a Handlebars template is deprecated/); equal(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: compile("{{capitalize this}}") }); appendView(); equal(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: 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: 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: 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: 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: 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: 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(3); var simplyObject = EmberObject.create({ firstName: 'Jim', lastName: 'Owen', birthday: EmberObject.create({ year: '2009' }) }); EmberHandlebars.registerBoundHelper('fullName', function(value){ return [ value.get('firstName'), value.get('lastName'), value.get('birthday.year') ].join(' '); }, 'firstName', 'lastName', 'birthday.year'); view = EmberView.create({ template: compile('{{fullName this}}'), context: simplyObject }); appendView(view); equal(view.$().text(), 'Jim Owen 2009', 'simply render the helper'); run(simplyObject, simplyObject.set, 'firstName', 'Tom'); equal(view.$().text(), 'Tom Owen 2009', 'render the helper after prop change'); run(simplyObject, simplyObject.set, 'birthday.year', '1692'); equal(view.$().text(), 'Tom Owen 1692', 'render the helper after path change'); } 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: 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: 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) [DEPRECATED]", 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: compile("{{#each things}}{{this}}|{{reverse this}} {{/each}}{{#each thing in things}}{{thing}}|{{reverse thing}} {{/each}}") }); expectDeprecation(function() { appendView(); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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: compile("{{#each things}}{{foo}}|{{print-foo this}} {{/each}}{{#each thing in things}}{{thing.foo}}|{{print-foo thing}} {{/each}}") }); expectDeprecation(function() { appendView(); }, 'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); 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({ context: 'alex', template: compile("{{shout this}}") }); appendView(); equal(view.$().text(), 'alex!', "helper output is correct"); run(function() { set(view, 'context', ''); }); equal(view.$().text(), '!', "helper output is correct"); run(function() { set(view, 'context', 'wallace'); }); equal(view.$().text(), 'wallace!', "helper output is correct"); }); test("should have correct argument types", function() { EmberHandlebars.helper('getType', function(value) { return typeof value; }); view = EmberView.create({ controller: EmberObject.create(), template: EmberHandlebars.compile('{{getType null}}, {{getType undefProp}}, {{getType "string"}}, {{getType 1}}, {{getType}}') }); appendView(); equal(view.$().text(), 'undefined, undefined, string, number, object', "helper output is correct"); }); }); enifed("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.'); }); }); enifed("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_set"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { "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 set = __dependency6__.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"); }); }); enifed("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.'); }); }); enifed("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; var 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); }); }); enifed("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.'); }); }); enifed("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, MyView; function templateFor(template) { return EmberHandlebars.compile(template); } var originalLookup = Ember.lookup; var lookup; QUnit.module("the #each helper [DEPRECATED]", { 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 = MyView = EmberView.extend({ template: templateMyView }); expectDeprecation(function() { append(view); },'Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.'); }, 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 does not mark each option tag as selected", function() { var selectView = EmberView.create({ template: templateFor(''), people: people }); append(selectView); equal(selectView.$('option').length, 3, "renders 3