gugol gugol - 1 month ago 6
Javascript Question

How to test a bare JavaScript MVC app with Jasmine

When trying to test a JavaScript bare MVC app with Jasmine, I get:


ReferenceError: document is not defined


I am testing it with jasmine-npm from the cmd and I don't know how to simulate the document object.

(function() {
/* ======= Model ======= */

var model = {
currentCat: null,
cats: [
{
clickCount : 0,
name : 'Tabby',
imgSrc : 'http://www.elpussycat.co.uk/images/cat.jpg'
},
{
clickCount : 0,
name : 'Tiger',
imgSrc : 'http://img15.deviantart.net/19bf/i/2011/327/7/5/pussy_cat_ii_by_antiplod-d4h254m.jpg'
},
{
clickCount : 0,
name : 'Scaredy',
imgSrc : 'https://thumbs.dreamstime.com/z/scottish-straight-breed-young-pussycat-9769564.jpg'
},
{
clickCount : 0,
name : 'Shadow',
imgSrc : 'https://leadershipfreak.files.wordpress.com/2013/10/pussy-cat.jpg'
}
]
};


/* ======= Controller ======= */

var controller = {

init: function() {
// set our current cat to the first one in the list
model.currentCat = model.cats[0];

// tell our views to initialize
catListView.init();
catView.init();
},

getCurrentCat: function() {
return model.currentCat;
},

getCats: function() {
return model.cats;
},

// set the currently-selected cat to the object passed in
setCurrentCat: function(cat) {
model.currentCat = cat;
},

// increments the counter for the currently-selected cat
incrementCounter: function() {
model.currentCat.clickCount++;
catView.render();
}
};


/* ======= View ======= */

var catView = {

init: function() {
// store pointers to our DOM elements for easy access later
this.catElem = document.getElementById('cat');
this.catNameElem = document.getElementById('cat-name');
this.catImageElem = document.getElementById('cat-img');
this.countElem = document.getElementById('cat-count');

// on click, increment the current cat's counter
this.catImageElem.addEventListener('click', function(){
controller.incrementCounter();
});

// render this view (update the DOM elements with the right values)
this.render();
},

render: function() {
// update the DOM elements with values from the current cat
var currentCat = controller.getCurrentCat();
this.countElem.textContent = currentCat.clickCount;
this.catNameElem.textContent = currentCat.name;
this.catImageElem.src = currentCat.imgSrc;
}
};

var catListView = {

init: function() {
// store the DOM element for easy access later
this.catListElem = document.getElementById('cat-list');

// render this view (update the DOM elements with the right values)
this.render();
},

render: function() {
var cat, elem, i;
// get the cats we'll be rendering from the controller
var cats = controller.getCats();

// empty the cat list
this.catListElem.innerHTML = '';

// loop over the cats
for (i = 0; i < cats.length; i++) {
// this is the cat we're currently looping over
cat = cats[i];

// make a new cat list item and set its text
elem = document.createElement('li');
elem.textContent = cat.name;

// on click, setCurrentCat and render the catView
// (this uses our closure-in-a-loop trick to connect the value
// of the cat variable to the click event function)
elem.addEventListener('click', (function(catCopy) {
return function() {
controller.setCurrentCat(catCopy);
catView.render();
};
})(cat));

// finally, add the element to the list
this.catListElem.appendChild(elem);
}
}
};

// make it go!
controller.init();
})();

Answer

Node.js provides a command-line Javascript implementation, but the Javascript isn't the only thing that makes a web page work. In a real (ie. on the web) environment you combine a browser-generated (from HTML) DOM with the Javascript logic to get your page. In a Node.js app/test environment you are missing that DOM part of the equation.

Fortunately lots of other people have run in to this problem, and to solve it they've created several different "mock DOM" libraries, which you can use to simulate the DOM in a Node environment.

This is one, but you can find more if you Google for them: https://www.npmjs.com/package/mock-browser