I am trying to write the tests for a Backbone
app which uses twig
and symphony
to render the underscore
templates, using mocha
, chai
and sinon
, with blanket
for code-coverage and phantomjs
for browser simulation. I have managed to load the template markup into test.html
by doing the following in the body
tag:
<!-- fixtures -->
<script>
$(function(){
$("#fixtures").load("fixtures/comment-fixture.html");
});
</script>
I have created a html fixture
, or at least that is what I was aiming for, however I still get the following error when running my tests:
TypeError: 'undefined' is not an object (evaluating 'text.replace')
I understand that it refers to basically underscore
evaluating _template(undefined)
, but I don't understand why it is doing that. Has anyone come across the a similar issue before or know why it is triggering this error?
This is my set-up:
Backbone view I want to test:
var CommentView = Backbone.View.extend({
//... is a list tag.
tagName: "li",
model: null,
// Cache the template function for a single item.
template: _.template($('#comment-template').html()),
// This is the timeout used to re-render the times
refreshTimer: null,
initialize: function(options) {
this.model = options.model;
this.listenTo(this.model, 'change', this.render);
this.listenTo(Backbone, 'stopCommentRefresh', this.stopRefreshTimer);
},
render: function() {
this.$el.html(this.template(this.model.renderAttributes()));
this.refreshTimer = setTimeout(_.bind(this.render, this), 60000);
return this;
},
// If you hit `enter`, we're through editing the item.
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
stopRefreshTimer: function () {
clearTimeout(this.refreshTimer);
this.remove();
}
});
My fixture file for it :
<script type="text/template" id="comment-template">
<article class="comment">
<h4><%- user.username %></h4>
<p><%- comment %></p>
<time><%- time %></time>
</article>
</script>
My test file for it (so far nothing special):
describe("Comment View", function () {
before(function () {
// create test fixture
this.$fixture = $('<li id="comment-view-fixture"></li>');
});
beforeEach(function () {
// empty out and rebind the fixture for each run
this.$fixture.empty().appendTo($("#fixtures"));
// new default model and view for each test
this.view = new CommentView ({
el: this.$fixture,
model: new Comment({
comment: "Hello world!",
created: new Date(),
modified: new Date(),
user: {
username: "Mario"
}
})
});
});
afterEach(function () {
this.view.model.destroy();
});
after(function () {
$("#fixtures").empty();
});
});
And my test.html
file:
<html>
<head>
<title>Backbone.js Tests</title>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="js/lib/mocha.css" />
</head>
<body>
<div id="blanket-main" class="hidden" style="display: none;"></div>
<div id="mocha"></div>
<!-- Utility libraries -->
<script src="js/lib/mocha.js"></script>
<script src="js/lib/chai.js"></script >
<script src="js/lib/sinon-chai.js"></script >
<script src="js/lib/sinon.js"></script >
<script src="js/lib/chai-datetime.js"></script>
<!-- JavaScript Coverage Libraries. -->
<script src="js/lib/blanket.js"></script>
<!-- jquery library -->
<script src="../../../../ApiBundle/Resources/public/js/thirdParty/jquery-1.11.js"></script>
<!-- fixtures -->
<script>
$(function(){
$("#fixtures").load("fixtures/comment-fixture.html");
});
</script>
<!-- JavaScript Core Libraries -->
<script src="../../../../ApiBundle/Resources/public/js/thirdParty/underscore.js"></script>
<script src="../../../../ApiBundle/Resources/public/js/thirdParty/jquery.dataTables.js"></script>
<script src="../../../../ApiBundle/Resources/public/js/thirdParty/backbone.js"></script>
<!-- Javascript Application Libraries - Views -->
<script src="../../../../ApiBundle/Resources/public/js/comment.js" data-cover></script>
<script>
var expect = chai.expect;
mocha.setup({
ui: "bdd",
globals: ['stats', 'failures', 'runner'], // Blanket leaks.
bail: false
});
// Set up Mocha with custom Blanket.js reporter.
mocha.reporter(function (_reporter) {
// Updated for Mocha 1.15.1 integration.
// See: https://github.com/alex-seville/blanket/pull/356
var blanketReporter = function (runner) {
// Listeners.
runner.on("start", function () { blanket.setupCoverage(); });
runner.on("suite", function () { blanket.onModuleStart(); });
runner.on("test", function () { blanket.onTestStart(); });
runner.on("test end", function (test) {
blanket.onTestDone(test.parent.tests.length,
test.state === 'passed');
});
runner.on("end", function () {
blanket.onTestsDone();
$("#blanket-main").removeClass("hidden").show("fast");
$("html, body").animate({ scrollTop: 0 });
});
_reporter.call(this, runner);
};
blanketReporter.prototype = _reporter.prototype;
return blanketReporter;
}(mocha._reporter));
blanket.beforeStartTestRunner({
callback: function () {
(window.mochaPhantomJS || mocha).run();
}
});
</script>
<script src="js/spec/views/view_CommentView.spec.js"></script>
<!-- Coverage style helpers -->
<style type="text/css">
#blanket-main {
margin-top: 65px;
margin-right: 20px;
margin-left: 20px;
border-radius: 5px;
border: 1px solid #666;
}
</style>
<!-- Test Fixtures. -->
<div id="fixtures" style="display: none; visibility: hidden;"></div>
</body>
</html>
It turns out its because
$("#fixtures").load("fixtures/comment-fixture.html")
is asynchronous and isn't actually loaded when I run the tests. By simply copying the contents offixture.html
intotest.html
body tag, the error stops happening!