I'm trying to use Jasmine (gem with yaml config) to test a Backbone.js application. I'm using the underscore templating like the Todo example.
template: _.template($('#item-template').html())
My problem is that I'm unable to have the templates loaded before my models/views so the template call causes those classes to error out at load.
I've read about the jasmine-jquery plugin to do fixtures but the problem is that my src files (models/views) are being loaded and failing before I ever get the the spec file and am able to setup the fixtures needed.
How do I get the templates loaded early enough they can be used for the reset of my classes?
You can delay the jQuery selector until you need it:
render: function(){
var templateHtml = $(this.template).html();
_.template(templateHtml);
}
Or you can run the selector when the view initializes:
initialize: function(){
this.template = _.template($(this.template).html());
}
Or, if you really want to leave your code as-is and have the selector evaluate when you define the View, you can wrap all of your Backbone code in a function that you call when you want to initialize your entire app code... such as a jQuery $(function(){} function on your real HTML page, or a beforeEach function in your Jasmine tests:
MyApp = (function(){
var myApp = {};
myApp.MyView = Backbone.View.extend({
template: _.template($("#item-template").html())
// ...
});
return myApp;
});
Then in your app, to start this up:
$(function(){
var myApp = MyApp();
new myApp.MyView();
// ...
});
And in your Jasmine test:
describe("how this thing works", function(){
beforeEach(function(){
var myApp = MyApp();
this.view = new myApp.MyView();
// ...
});
});
Once you have one of these solutions in place, you can use something like Jasmine-jQuery to load your fixtures.
FWIW: I tend to use a combination of these techniques, as needed.
The one issue with the first part of the accepted answer is that the template gets compiled every time the view is instantiated. One alternative is to set the template directly on the prototype of the view:
app.TodoView = Backbone.View.extend({
initialize: function(){
if (!app.TodoView.prototype.template) {
app.TodoView.prototype.template = _.template($("#item-template").html());
}
}
});
This way, the template is compiled once, when the first instance of TodoView is instantiated.
Related
According to the blog-post for ember-data version 1.0.0-beta.16 the store can now be used as a service:
TweetComposerComponent = Ember.Component.extend({
store: Ember.inject.service()
});
However, I can't figure out how to do qunit unit tests on such a component. I've tried the following:
moduleForComponent('tweet-composer', {
needs: ['service:store']
});
and:
moduleForComponent('tweet-composer', {
needs: ['store:main']
});
And when I do the former I get an error Attempting to register an unknown factory: 'service:store' and if I do the latter then store is undefined.
Thoughts?
(I'm writing a ember-cli style app).
Update:
It seems there's an open issue for this in the ember-test-helpers repo.
While I'm waiting for this fix, I cooked up a helper that can work as a stop-gap measure (coffeescript):
`import TestModuleForComponent from 'ember-test-helpers/test-module-for-component'`
`import { createModule } from 'ember-qunit/qunit-module'`
# This assumes the last argument, the callbacks, is present, although it
# does support the description being an optional argument.
moduleForStoreComponent = ->
args = Array.prototype.slice.call arguments
callbacks = args[args.length-1]
# Wrap the original beforeEach callback in a modified version that
# also sets up the store for the test container.
originalSetup = callbacks.beforeEach
callbacks.beforeEach = ->
DS._setupContainer(#container)
originalSetup.call(#) if originalSetup
callbacks.store = ->
#container.lookup('store:main')
args.unshift TestModuleForComponent
createModule.apply #, args
`export default moduleForStoreComponent`
A unit test is a place where everything works perfectly except the code/component/unit that you are testing.
So, even the store should be assumed to be working perfectly (0 errors/bugs).
Something like this should work in your test:
moduleForComponent('tweet-composer', {
beforeEach: function() {
this.subject({
store: {/*empty object*/}
});
}
});
If parts of your tests depend on data retrieved from the store, you can do something like:
this.subject({
store: {
find: function() {
var mockedModel = Ember.Object.create({/*empty*/});
return mockedModel;
}
}
});
This is to preserve the status of "unit test", if you start including and registering other objects from your app you 'll be actually writing integration tests.
Note:
In general, looking up models directly in a component is an
anti-pattern, and you should prefer to pass in any model you need in
the template that included the component.
http://discuss.emberjs.com/t/should-ember-components-load-data/4218/2?u=givanse
in ember application i want to call my custom function which does some modifications of dom elements, the only solution i found is to repeat below code as many times as many views/routes i have
for example
rendering indexView
indexView = Ember.View.Extend({
didInsertElement:function(){
//my custom function call goes here.. myFunction();
}
});
rendering OtherView
OtherView = Ember.View.Extend({
didInsertElement:function(){
//my custom function call goes here.. myFunction();
}
});
rendering MoreView
MoreView = Ember.View.Extend({
didInsertElement:function(){
//my custom function call goes here.. myFunction();
}
});
Is there a way of calling myfunction globaly whenever any view is rendered? I really do not want to repeat code for every single view i render.
thanks!
You can create a Mixin:
App.SomeMixin = Ember.Mixin.create({
didInsertElement: function() {
this._super();
//do your common stuff here
}
});
And use it in your views:
App.SomeView = Ember.View.Extend(App.SomeMixin, {
didInsertElement: function() {
this._super();
//do your custom stuff here
}
});
I would use a mixin to do this. If, however, you find that you are using this mixin into every single view that you create, it might be better to reopen the Ember.View class and add this functionality there. Also,if you reopened the class, what you could do is, depending upon the use case, create a static function inside Ember.View.reopenClass which you would pass to a
Ember.run.scheduleOnce()
utility that ember provides so that, if the function that you need doesn't need any state (for example, just does something to the page after the content has loaded or something.), it will just run the function once after all the views are rendered in the page.
There are two ways to handle this. Create a base class, or reopen the global View class and insert your code. I prefer the first, since it's easier to maintain and track down issues.
Reopen the View class and tack on an additional method that triggers on didInsertElement
Ember.View.reopen({
doStuff:function(){
//myFunction();
}.on('didInsertElement')
});
In the case of debouncing
Ember.View.reopen({
doStuff:function(){
Ember.run.debounce(this.doRealStuff, 200);
}.on('didInsertElement'),
doRealStuff: function(){
console.log('foo');
}
});
http://emberjs.jsbin.com/gecoziwe/1/edit
Create a base class and extend from it (My preferred method, since it's easier to track down issues, can't say this enough)
App.BaseView = Ember.View.extend({
doStuff:function(){
//myFunction();
}.on('didInsertElement')
});
App.FooView = App.BaseView.extend({
});
App.BarView = App.BaseView.extend({
});
I'm using ember-cli 0.0.35, and injecting a dependency onto my component via an initializer. It works great in development, but the property isn't present when I run tests. It appears that testing calls loadInitializers, but the dependency is not showing up on this.subject({});
I don't want to manually inject it for the tests. Is there a better way to handle this?
Initializer:
var FooServiceInitializer = {
name: 'foo',
initialize: function (container, application) {
application.inject('component:foo', 'foo', 'service:foo');
}
};
export default FooServiceInitializer;
Failing Test:
moduleForComponent('bar', 'Component: Bar', {
setup: function() {
App = startApp();
component = this.subject({});
},
teardown: function () {
Ember.run(App, App.destroy);
}
});
test('Properties: foo', function() {
// Make sure we injected the service
ok(component.foo, 'foo is injected');
});
As I said before, it really lends itself to an integration test since you are testing the container at this point (and not the mini container created by ic-ajax).
Your real test is along the lines of this
test("root lists 3 colors", function(){
var c = App.__container__.lookup('component:foo-bar');
ok(c.foo.blah);
});
If you feel guilty about using the container during testing (which you shouldn't) you can create a helper to avoid having to fix it all over the place when/if an api changes in the future.
Ember.Test.registerHelper('containerLookup',
function(app, look) {
return app.__container__.lookup(look);
}
);
Make sure you define this before
App.injectTestHelpers();
and then your test would look like
test("root lists 3 colors", function(){
var c = containerLookup('component:foo-bar');
ok(c.foo.blah);
});
http://emberjs.jsbin.com/doxigu/edit
I am unit testing my controller using mocha. My controller looks like:
AS.MyController = Ember.ObjectController.extend(Ember.Validations.Mixin, {
name: null,
description: null,
init: function () {
this._super();
this.get('store').find('something');
},
....
});
And my test begins like:
describe("MyControllerTest", function () {
//tried but didn't work
//delete AS.MyController.init;
var controller = AS.MyController.create();
.....
})
and the browser always throws error on "this.get('store')" call in init. I am not sure if I need to stub things out or there is a work around for it because my test case doesn't rely on store at all. In either case, I couldn't find much out there and would really appreciate any feedback.
Thanks, Dee
JSBIN : http://jsbin.com/aMASeq/3/
UPDATE :
There can be many ways to tackle this issue, but what I ended up doing is re-structuring the controller code a bit by putting all the function calls to store into separate actions and then in init I make calls to these action functions using this.send('actioName'). In my unit test, before instantiating the controller, I reopen the controller to modify these action functions(its easier to change action function than to change init function itself, when trying to change init I always got into some js error). Eg:
AS.MyController.reopen({actions: {setSomeActionThatUsesStore: function () {
//do something that doesn't involve using store
}}});
Controllers get access to the store from the container. You can create a mock container and instantiate the controller with it.
var mockContainer = new Ember.Container();
mockContainer.register('store:main', Ember.Object.extend({
find: function() { ... }
});
var controller = App.PostController.create({ container: mockContainer });
If you need access to the real store then you can just grab the controller from your App's container.
var controller = App.__container__.lookup('controller:post');
That will instantiate a PostController for you that has all of it's dependencies (such as store) wired together.
I'm trying out Ember.JS and I'm having a really tough time using it with Require.JS so far, even with a (pretty) basic example.
First of all, I'd like to say that Require.JS is supposed to (I think) improve two weak points I see in Ember.JS :
Organizing the app, especially in separate js files
Not loading unnecessary code
I'm basically trying to display an app with header/content/footer. So, when I'm creating my App I'm binding a ApplicationController and an ApplicationView, and the view handles the template. This works great in displaying (pretty easily) the header and the footer.
Then, I'm trying to render a template for the index (for example), and I would like to dynamically load IndexView/IndexController (for example) and bind it with a route. That's where I'm having a tough time.
I found an easy way to do this by setting IndexView directly as App.IndexView, but the problem with this solution is that if I load IndexView, I'm also loading the index template file content (using text.js plugin). That would be okay for my example, BUT since I'm trying to build a complex website, that would mean loading all the templates when loading the website, which is exactly what Require.JS was trying to avoid.
Where am I wrong here? How do I dynamically load the template depending on the routing?
EDIT: It's not really needed to declare a placeholder in the main html document as it's injected using view.append().
I've been struggling with the same thing, and I finally came up with a way to split over router, controller, views and templates loading them dynamically.
This is my main "embermain.js" file:
window.MyRanks = Ember.Application.create();
MyRanks.Router.map(
function() {
this.route('about');
}
);
MyRanks.AboutRoute = Ember.Route.extend({
setupController: function(controller, model) {
require(['app/controller/AboutController'], function(controller) {
});
}
});
Here is my AboutController:
require(
['app/view/AboutView'],
function (view) {
var controller = MyRanks.AboutController = Ember.Controller.extend({
});
return controller;
});
Here is my AboutView:
define(
['text!app/templates/about.html'],
function (template) {
var view = Ember.View.create({
template: Ember.Handlebars.compile(template),
templateName: 'about',
variable: 'my value',
didInsertElement: function() {
console.log( "Yes the view was included");
}
});
view.append();
return view;
}
);
And here is the template about.html
This is the template {{view.variable}}
Hope it helps! :)