Ember Unit Testing Templates - unit-testing

I'm in the early stages of using Ember in a project. So far I'm very excited about the possibilities it opens up!
We're writing integration tests as described in their docs. We also plan to do unit testing of various things at a later stage when required (components, controllers, views etc).
Right now, however, we just have a basic template to which a model is bound. For illustrative purposes it could simply be:
<h1>{{title}}</h1>
We're precompiling our templates on the server: they're available in the Ember.TEMPLATES collection.
We'd like to unit test just this template with a model (created in the test) bound to it. My thinking is that we should just be able to load the app on a page, specify a page element into which the template should be rendered, create a dummy model, bind it 'somehow' to the template, render the template and then do some jQuery assertions. Sounds straight-forward, but I can't seem to find out how to do this in an easy way.
I've already looked at this post and posts like this, but they either seem to be out of date or deal with views, which I don't think we have a need for with such a simple template.
Can anyone point me in the right direction? Am I looking at this problem the wrong way? We're using qunit and ember-qunit combi as recommended in the Ember docs, in case that's of importance.

You can always create a view on the fly and attach a controller and your template to it, then attach it to the page and test that the bindings are working.
var controller = Em.ObjectController.create({
model: { title: 'hello'}
});
var fooView = Em.View.create({
container: App.__container__,
templateName: 'foo',
controller: controller
});
// start up the run loop and inject template into the page
Em.run(function(){
fooView.appendTo('#foo');
});
equal(find("h1").length, 1, "h1 is injected");
equal(find("h1").html(), 'hello', "text says hello");
fooView.remove(); // cleanup
http://emberjs.jsbin.com/wipo/45/edit

Related

Is there any way to render Handlebars templates on server via Backbone in front-end?

edited
This question is in addition to previous Handlebars with Backbone template not rendering problem in which browseser was not rendering forms at all now the problem was solved for the form but returns another error which is possibly also with the rendering.
I have an app with Backbone on front-end and Express in back-end with express-handlebars templates and I was trying to change from Underscore templating to Handlebars while still leaving backbone logic behind. So the way I did it - installing handlebars via Bower and then required it.
app.ContactView = Backbone.View.extend({
//....
template: Handlebars.compile( $('#tmpl-contact').html() ),
//....
});
instead of
app.ContactView = Backbone.View.extend({
//....
template: _.template( $('#tmpl-contact').html() ),
//....
});
But it returns errors and I don't really get what causes them.
When I try to load a page, for example:
http://192.168.33.10:3000/contact/
It doesn't shows any errors in the browser. But if I move to:
http://192.168.33.10:3000/about
which doesn't even have Backbone logic behind it returns an error:
You must pass a string or Handlebars AST to Handlebars.compile. You
passed undefined
Which means that template is compiled before the DOM loads on the page. But my Backbone script is loading after html script e. g. after the {{{body}}} element. Also if there is a problem with an order I should get this error on every, not only when moving to another.
So I think the cause is some kind of a conflict between front-end handlebars and express-handlebars on a server. How to solve this, preferably that templates to be rendered via expHbs instance?
If the only change that you made was:
template: Handlebars.compile( $('#tmpl-contact').html() ),
to:
template: _.template( $('#tmpl-contact').html() ),
it wouldn't cause the problems you're describing. As the error message told you "You passed undefined", and undefined wouldn't have worked with the Underscore code either.
It seems more likely that you changed something else, and that change caused the issue you described. However, without more code (ideally a JS Fiddle) it's hard to say what.
Problem solved by loading each Backbone part as a separate file for its route.

Qunit testing an ember controller, located in a file that contains multiple controllers?

So, I've been trying to qunit test an Ember controller, The problem is, The controller is inside a coffeeScript file, that contains multiple controllers.
Now, The ember testing guide says, In order to test a controller, I should use the 'moduleFor' helper like so:
moduleFor(fullName [, description [, callbacks]])
In my case, the full name is say: "CustomersIndexController" , But because it's included in "customers_controller.coffee" that in it self includes multiple controller, Testing it became problematic .
After an Endless digging online, I found out (Please correct me if I'm wrong) that the resolver cares only about the file name, not about the name that 'export default myModel' provides
To make it more clear, Here is my "customers_controller.coffee" :
`export { CustomersIndexController, CustomersItemController }`
CustomersIndexController = Ember.ArrayController.extend
#Code goes here ......
CustomerItemController = Ember.ObjectController.extend
#Code goes here .....
And here is the customers-controller-test.coffee file :
`import { test, moduleFor } from 'ember-qunit';`
moduleFor("controller:customers-index-controller", 'C Controller')
test "it's an App.Controller", -> ok(#subject())
I've tried all the ideas that my brain could produce...without any luck(changing the controller name from camelCase to dasherized, to absolute path, even tried importing customers_controller.coffee), But I keep getting:
Setup failed on it's a App.Controller: Attempting to register an unknown factory: `controller:customers-index-controller`
Any Help/Advice/Links are highly appreciated.
You should be able to defined it in lower camelCase.
moduleFor('controller:postsIndex', 'Posts Index Controller');
http://jsbin.com/ruhalota/1/edit
If you take a look at the documentation for the resolver with ember-cli, you'll see that it does indeed only care about the names of the files, and what is the default export of them: http://www.ember-cli.com/#using-modules
In your case, you'll need to split your controllers into multiple files, so the resolver can find and instantiate them properly. So, the two files would be:
app/controllers/customers/index.coffee
app/controllers/customers/item.coffee
This is all assuming you are using ember-cli. If you are still using ember-app-kit, you might need to adjust this slightly, but the same basic idea should apply.

ember js development mode runtime template compile?

I'm learning to work with Ember and Grunt precompiled HBS templates, but want to also run in a 'developer mode' where the the App will compile the HBS's at runtime, if it doesn't find them.
I've read about a few ways to go about this and am trying to do it with '$.ajax()' and 'Ember.Handlebars.compile(data)' where each template returned is added to the Ember.TEMPLATES array. This won't work off the file system, so I test it on a localhost tomcat where the Ember App is added to webapps/ROOT.
I'm working with a demo 'user admin' App I found online which uses a couple of components, helpers and 'generated controllers'. So the Templates compile OK, but there are problems with Helper registration, such as:
Handlebars error: Could not find property 'modal-box' on object (generated modal-demo controller).
...so after adding the Component Template to the Templates array, I try to register it by name:
if (templateName == 'components/modal-box') {
Ember.Handlebars.helper('modal-box', function(value, options) {
var escaped = Handlebars.Utils.escapeExpression(value);
return new Handlebars.SafeString(tmpl);
});
}
...but then I get this new error:
registerBoundHelper-generated helpers do not support use with Handlebars blocks.
"Template was precompiled with an older version of Handlebars than the current runtime."
This is all done in an 'App create ready' function which iterates a list of template names, which I'd like to further develop to where it reads the template file names dynamically. The Grunt process also compacts the CSS and concatenates the scripts, so I would want to work out a 'developer mode' process for these too. But right now I'm focused on the HBS Templates & Components.
I'm thinking this must be a FAQ so am wondering if the community has arrived at a best practice for this sort of runtime compile for development?
If not how can I resolve my issue with getting the component template helper generated controllers registered correctly?
They just need to be registered before the other templates are compiled.
Em.TEMPLATES["components/cow-dude"] = Ember.Handlebars.compile("I'm a cow");
App = Ember.Application.create();
http://emberjs.jsbin.com/apIRef/28/edit
Order of operations is important, if it compiles the other templates first, they will just assume cow-dude is a property on the model in context (which will probably be undefined) (http://emberjs.jsbin.com/apIRef/27/edit). That being said, if you are going to lazy load componenets/helpers, those need to be loaded before any of their dependencies (Ember handles this all for you when you just toss them all in at once).
Additionally it sounds like you are using two different versions of Handlebars, which is why it's giving you the Template was precompiled with an older version of Handlebars than the current runtime.
I wrote up a similar response to someone else that might be of use to you: Ember.js with external handlebars template

The story with Index Controllers, Views, Templates?

I've created the following route in Ember:
this.resource('password_reset', { path: '/password_reset' }, function() {
this.route("request");
this.route("claim");
});
The Ember debugger -- which I LOVE btw -- shows me this results in the following:
I have created two templates so far:
/templates/password_reset.hbs
/templates/password_reset/index.hbs
When I go to the URL http://my.server.com/#/password_reset I would expect that -- based on what the debugger's telling me -- that the 2nd template listed (aka, password_reset/index) above is used but in fact it uses the frist one. What doing? Anyone care to shed some light on this mystery?
Ok, I think it can be chalked up to a newbie question. The relationship between these two controllers/views/templates becomes far more clear when I put an {{outlet}} into the /password_reset template. Then I can see that the password_reset/index shows up as the outlet. The index, in effect, becomes the default outlet when a sub-route is not defined. Pretty basic but somehow I didn't get it until I bumped into a wall or two.

using an ember-rails handlebars template outside the context of an ember view

I have a rails app that is using the ember-rails gem.
There is a section of my site that is not on ember, but where it would be convenient to use one of the handlebar templates served via the asset pipeline. However, something seems to be going wrong. Specifically, my template is returned like so:
Ember.TEMPLATES["views/wanderlists/templates/gallery"] = Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) { helpers = helpers || Ember.Handlebars.helpers; var self=this;
data.buffer.push("<h1>Gallery!</h2>\n"); });
However, if I try to use this template:
Ember.TEMPLATES["views/wanderlists/templates/gallery"]({})
TypeError: Cannot read property 'buffer' of undefined
Any idea why the generated template would be having trouble?
Any idea why the generated template would be having trouble?
You can't call handlebars templates compiled by the ember handlebars compiler as if they were normal handlebars templates. They expect a completely different set of arguments. Specifically, they expect to be passed (context, options) where options has a data.buffer that output will be written to. So for example, if you try:
Ember.TEMPLATES["views/wanderlists/templates/gallery"](this, {data: {buffer: 'NOT-A-BUFFER'}})
console should output TypeError: Object NOT-A-BUFFER has no method 'push'
There is a section of my site that is not on ember, but where it would be convenient to use one of the handlebar templates served via the asset pipeline.
OK. This is really easy to do, just not by accessing Ember.TEMPLATES directly. Instead use an Ember.View, and call appendTo() directly to render. For example:
App = Ember.Application.create({});
var view = Ember.View.create({
templateName: "views/wanderlists/templates/gallery",
name: "Bob"
});
view.appendTo("#message");
Working example here: http://jsfiddle.net/mgrassotti/VWmFq/1/
For more details see Ember Guides: Defining a view