/**
This module specifically tests integration with Handlebars and SproutCore-specific
Handlebars extensions.
If you add additional template support to SC.TemplateView, you should create a new
file in which to test.
*/
module("SC.TemplateView - handlebars integration");
test("template view should call the function of the associated template", function() {
var view = SC.TemplateView.create({
templateName: 'test_template',
templates: SC.Object.create({
test_template: SC.Handlebars.compile("
template was called
")
})
});
view.createLayer();
ok(view.$('#twas-called').length, "the named template was called");
});
test("template view should call the function of the associated template with itself as the context", function() {
var view = SC.TemplateView.create({
templateName: 'test_template',
_personName: "Tom DAAAALE",
_i: 0,
personName: function() {
this._i++;
return this._personName + this._i;
}.property().cacheable(),
templates: SC.Object.create({
test_template: SC.Handlebars.compile("
template was called for {{personName}}. Yea {{personName}}
")
})
});
view.createLayer();
equals("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");
});
TemplateTests = {};
test("child views can be inserted using the {{view}} Handlebars helper", function() {
var templates = SC.Object.create({
nester: SC.Handlebars.compile("
')
});
var removeCalled = 0;
var view = SC.TemplateView.create({
templateName: 'foo',
templates: templates,
content: SC.Object.create({
foo: SC.Object.create({
baz: "unicorns",
removeObserver: function(property, func) {
sc_super();
removeCalled++;
}
})
})
});
view.createLayer();
equals(view.$('#first').text(), "unicorns", "precond - renders the bound value");
var oldContent = view.get('content');
SC.run(function() {
view.set('content', SC.Object.create({
foo: SC.Object.create({
baz: "ninjas"
})
}));
});
equals(view.$('#first').text(), 'ninjas', "updates to new content value");
SC.run(function() {
oldContent.setPath('foo.baz', 'rockstars');
});
SC.run(function() {
oldContent.setPath('foo.baz', 'ewoks');
});
equals(removeCalled, 1, "does not try to remove observer more than once");
equals(view.$('#first').text(), "ninjas", "does not update removed object");
});
test("Handlebars templates update properties if a content object changes", function() {
var templates;
templates = SC.Object.create({
menu: SC.Handlebars.compile('
Today\'s Menu
{{#bind "coffee"}}
{{color}} coffee
{{bind "price"}}{{/bind}}')
});
var view = SC.TemplateView.create({
templateName: 'menu',
templates: templates,
coffee: SC.Object.create({
color: 'brown',
price: '$4'
})
});
view.createLayer();
equals(view.$('h2').text(), "brown coffee", "precond - renders color correctly");
equals(view.$('#price').text(), '$4', "precond - renders price correctly");
view.set('coffee', SC.Object.create({
color: "mauve",
price: "$4.50"
}));
equals(view.$('h2').text(), "mauve coffee", "should update name field when content changes");
equals(view.$('#price').text(), "$4.50", "should update price field when content changes");
view.set('coffee', SC.Object.create({
color: "mauve",
price: "$5.50"
}));
equals(view.$('h2').text(), "mauve coffee", "should update name field when content changes");
equals(view.$('#price').text(), "$5.50", "should update price field when content changes");
view.setPath('coffee.price', "$5");
equals(view.$('#price').text(), "$5", "should update price field when price property is changed");
});
test("Template updates correctly if a path is passed to the bind helper", function() {
var templates;
templates = SC.Object.create({
menu: SC.Handlebars.compile('
{{bind "coffee.price"}}
')
});
var view = SC.TemplateView.create({
templateName: 'menu',
templates: templates,
coffee: SC.Object.create({
price: '$4'
})
});
view.createLayer();
equals(view.$('h1').text(), "$4", "precond - renders price");
view.setPath('coffee.price', "$5");
equals(view.$('h1').text(), "$5", "updates when property changes");
view.set('coffee', { price: "$6" });
equals(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 SC.ObjectController", function() {
var templates;
templates = SC.Object.create({
menu: SC.Handlebars.compile('
{{bind "coffee.price"}}
')
});
var controller = SC.ObjectController.create();
var realObject = SC.Object.create({
price: "$4"
});
controller.set('content', realObject);
var view = SC.TemplateView.create({
templateName: 'menu',
templates: templates,
coffee: controller
});
view.createLayer();
equals(view.$('h1').text(), "$4", "precond - renders price");
realObject.set('price', "$5");
equals(view.$('h1').text(), "$5", "updates when property is set on real object");
SC.run(function() {
controller.set('price', "$6" );
});
equals(view.$('h1').text(), "$6", "updates when property is set on object controller");
});
test("Should insert a localized string if the {{loc}} helper is used", function() {
SC.stringsFor('en', {
'Brazil': 'Brasilia'
});
templates = SC.Object.create({
'loc': SC.Handlebars.compile('
Country: {{loc "Brazil"}}')
});
var view = SC.TemplateView.create({
templateName: 'loc',
templates: templates,
country: 'Brazil'
});
view.createLayer();
equals(view.$('h1').text(), 'Country: Brasilia', "returns localized value");
});
test("Template views return a no-op function if their template cannot be found", function() {
var view = SC.TemplateView.create({
templateName: 'cantBeFound'
});
var template = view.get('template');
ok(SC.typeOf(template) === 'function', 'template should be a function');
equals(template(), '', 'should return an empty string');
});
test("Template views can belong to a pane and a parent view", function() {
var templates = SC.Object.create({
toDo: SC.Handlebars.compile('
{{title}}
(Created at {{createdAt}})')
});
var didCreateLayerWasCalled = NO;
var pane = SC.MainPane.design({
childViews: ['container'],
container: SC.View.design({
childViews: ['normalView', 'template'],
normalView: SC.View,
template: SC.TemplateView.design({
templates: templates,
templateName: 'toDo',
title: 'Do dishes',
createdAt: "Today",
didCreateLayer: function() {
didCreateLayerWasCalled = YES;
}
})
})
});
pane = pane.create().append();
equals(pane.$().children().length, 1, "pane has one child DOM element");
equals(pane.$().children().children().length, 2, "container view has two child DOM elements");
equals(pane.$().children().children().eq(1).html(), "
Do dishes
(Created at Today)", "renders template to the correct DOM element");
ok(didCreateLayerWasCalled, "didCreateLayer gets called on a template view after it gets rendered");
pane.remove();
});
test("Template views add a layerId to child views created using the view helper", function() {
var templates = SC.Object.create({
parent: SC.Handlebars.compile(''),
child: SC.Handlebars.compile("I can't believe it's not butter.")
});
TemplateTests.ChildView = SC.TemplateView.extend({
templates: templates,
templateName: 'child'
});
var view = SC.TemplateView.create({
templates: templates,
templateName: 'parent'
});
view.createLayer();
var childView = view.getPath('childViews.firstObject');
equals(view.$().children().first().children().first().attr('id'), childView.get('layerId'));
});
test("Template views set the template of their children to a passed block", function() {
var templates = SC.Object.create({
parent: SC.Handlebars.compile('
.*.*<\/span>.*<\/h1>/), "renders the passed template inside the parent template");
});
test("Child views created using the view helper should have their parent view set properly", function() {
TemplateTests = {};
var template = '{{#view "SC.TemplateView"}}{{#view "SC.TemplateView"}}{{view "SC.TemplateView"}}{{/view}}{{/view}}';
var view = SC.TemplateView.create({
template: SC.Handlebars.compile(template)
});
view.createLayer();
var childView = view.childViews[0].childViews[0];
equals(childView, childView.childViews[0].parentView, 'parent view is correct');
});
test("Collection views that specify an example view class have their children be of that class", function() {
TemplateTests.ExampleViewCollection = SC.TemplateCollectionView.create({
itemView: SC.TemplateView.extend({
isCustom: YES
}),
content: ['foo']
});
var parentView = SC.TemplateView.create({
template: SC.Handlebars.compile('{{#collection "TemplateTests.ExampleViewCollection"}}OHAI{{/collection}}')
});
parentView.createLayer();
ok(parentView.childViews[0].childViews[0].isCustom, "uses the example view class");
});
test("should update boundIf blocks if the conditional changes", function() {
var templates = SC.Object.create({
foo: SC.Handlebars.compile('
')
});
var view = SC.TemplateView.create({
templateName: 'foo',
templates: templates,
content: SC.Object.create({
wham: 'bam',
thankYou: "ma'am",
myApp: SC.Object.create({
isEnabled: YES
})
})
});
view.createLayer();
equals(view.$('#first').text(), "bam", "renders block when condition is true");
SC.run(function() { view.get('content').setPath('myApp.isEnabled', NO); });
equals(view.$('#first').text(), "", "re-renders without block when condition is false");
});
test("{{view}} id attribute should set id on layer", function() {
var templates = SC.Object.create({
foo: SC.Handlebars.compile('{{#view "TemplateTests.IdView" id="bar"}}baz{{/view}}')
});
TemplateTests.IdView = SC.TemplateView.create();
var view = SC.TemplateView.create({
templateName: 'foo',
templates: templates
});
view.createLayer();
equals(view.$('#bar').length, 1, "adds id attribute to layer");
equals(view.$('#bar').text(), 'baz', "emits content");
});
test("{{view}} class attribute should set class on layer", function() {
var templates = SC.Object.create({
foo: SC.Handlebars.compile('{{#view "TemplateTests.IdView" class="bar"}}baz{{/view}}')
});
TemplateTests.IdView = SC.TemplateView.create();
var view = SC.TemplateView.create({
templateName: 'foo',
templates: templates
});
view.createLayer();
equals(view.$('.bar').length, 1, "adds class attribute to layer");
equals(view.$('.bar').text(), 'baz', "emits content");
});
test("should be able to bind view class names to properties", function() {
var templates = SC.Object.create({
template: SC.Handlebars.compile('{{#view "TemplateTests.classBindingView" classBinding="isDone"}}foo{{/view}}')
});
TemplateTests.classBindingView = SC.TemplateView.create({
isDone: YES
});
var view = SC.TemplateView.create({
templateName: 'template',
templates: templates
});
view.createLayer();
equals(view.$('.is-done').length, 1, "dasherizes property and sets class name");
SC.run(function() {
TemplateTests.classBindingView.set('isDone', NO);
});
equals(view.$('.is-done').length, 0, "removes class name if bound property is set to false");
});
test("should be able to bind element attributes using {{bindAttr}}", function() {
var template = SC.Handlebars.compile('');
var view = SC.TemplateView.create({
template: template,
content: SC.Object.create({
url: "http://www.sproutcore.com/assets/images/logo.png",
title: "The SproutCore Logo"
})
});
view.createLayer();
equals(view.$('img').attr('src'), "http://www.sproutcore.com/assets/images/logo.png", "sets src attribute");
equals(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute");
SC.run(function() {
view.setPath('content.title', "El logo de Esproutcore");
});
equals(view.$('img').attr('alt'), "El logo de Esproutcore", "updates alt attribute when content's title attribute changes");
});
test("should be able to bind boolean element attributes using {{bindAttr}}", function() {
var template = SC.Handlebars.compile('');
var content = SC.Object.create({
isDisabled: false,
isChecked: true,
});
var view = SC.TemplateView.create({
template: template,
content: content
});
view.createLayer();
ok(!view.$('input').attr('disabled'), 'attribute does not exist upon initial render');
ok(view.$('input').attr('checked'), 'attribute is present upon initial render');
content.set('isDisabled', true);
content.set('isChecked', false);
ok(view.$('input').attr('disabled'), 'attribute exists after update');
ok(!view.$('input').attr('checked'), 'attribute is not present after update');
});