Mocha tests on focus-related behaviors (Backbone/CoffeeScript app) - javascript

I have a Backbone app written in CoffeeScript. I'm trying to use Mocha (with Chai and Sinon) to write tests for DOM-related behaviors, but it seems that hidden fixtures (I'm using js-fixtures now but I've also tried this unsuccessfully with a hidden '#fixtures' div) don't register certain DOM-related behaviors which makes testing certain types of DOM-related behaviors (seemingly) impossible.
For example, my main app view has several subviews which are never rendered at the same time: when the app view renders subview A, it remembers the focused element of the currently active subview B (#_visibleView), saves that information on subview B, closes the subview B, and then renders subview A.
_rememberFocusedElement: ->
focusedElement = $ document.activeElement
if focusedElement
focusedElementId = focusedElement.attr 'id'
if focusedElementId
#_visibleView?.focusedElementId = focusedElementId
This works when I test it manually, but when I try to write unit tests for this behavior they fail because I can't set focus (e.g., via $(selector).focus()) to an element in a hidden div/iframe. (I have the same issue with functionality which listens for window resize events.)
I thought that if I changed $ document.activeElement to #$ ':focus" I might get different results, but that doesn't fix the issue.
Here is what the relevant parts of my Mocha (BDD) tests look like. This spec will print TEXTAREA to the console and then undefined, indicating that there is a textarea with id='transcription' but I can't set focus to it.
beforeEach (done) ->
fixtures.path = 'fixtures'
callback = =>
#$fixture = fixtures.window().$ "<div id='js-fixtures-fixture'></div>"
#appView = new AppView el: #$fixture
done()
describe 'GUI stuff', ->
it 'remembers the currently focused element of a subview', (done) ->
#appView.mainMenuView.once 'request:formAdd', =>
#appView._visibleView.$('#transcription').focus()
console.log #appView._visibleView.$('#transcription').prop 'tagName'
console.log #appView._visibleView.$(':focus').prop 'tagName'
done()
#appView.mainMenuView.trigger 'request:formAdd'
Is there any way that I can write unit tests for these types of behaviors?

Ok, first off let me clarify something: the term "unit test" means man different things to many people. Often times it becomes synonymous with "any test written using a unit test framework (like Mocha)". When I use the term "unit test" that's not what I mean: what I mean is a test that tests only a single unit of work (which, in a JS environment, will usually be a single function, but might be a whole class).
Ok, with that out of the way, if you really are trying to unit test your code, you're sort of taking the wrong approach. A unit test really shouldn't rely on anything outside the context of the function being tested, and so relying on the (external) DOM is where your problem lies.
Let's assume your focus-handling code is in a function called handleFocus (I don't know the actual method name). Consider the following test, which I'll write using JavaScript since my CoffeScript is rusty:
describe('#handleFocus', function() {
it('remembers the currently focused element of a subview', function() {
var setFocusStub = sinon.stub($.fn, 'focus');
appView._visibleView.handleFocus();
expect(setFocusStub.calledOnce).to.be(true);
});
});
The above is a bit of an over-simplification, but hopefully it illustrates the point. What you're really trying to check isn't whether the DOM (fake or real) does X; what you're trying check is whether your function does X. By focusing on that in your test, and relying on a stub that checks whether "X" happened or not, you completely eliminate the need for the DOM to be involved.
Now of course you might wonder: "well great, that helps me in test-land, but how do I know it will work in a real environment?" My answer to that would be that your (probably Selenium-based) acceptance tests should cover that sort of thing. Acceptance tests should check whether your overall code works in the real world, while unit tests should ensure that individual pieces of that code work in a fake environment.
The former is great for ensuring your customers don't see bugs, while the latter is great for figuring out exactly where those bugs come from, and for making it possible for you to refactor safely.

Related

What is a real unit test?

So for a while now I've been pondering what a real unit test should consist of. It's best to demonstrate this with an example, here's my angular service I want to test:
function stringUtils() {
return {
splitFilterString: splitFilterString
}
function splitFilterString (filterString) {
return filterString.split(':');
}
}
I've been thinking which of my 2 approaches best describes a "real" unit test for splitFilterString. (The examples are written with jasmine)
1 Test that String.prototype.split has been called.
You don't test any "real" examples with this, you just test that the function actually calls String.prototype.split and returns whatever that function returns. This has been my way of testing so far because you don't test "external" API's for doing what they tell you they (should) be doing.
it('should call split with ":" as an argument and return whatever split returns', function () {
var filterString = 'foo:bar';
spyOn(String.prototype, 'split').and.returnValue('foo');
expect(stringUtils.splitFilterString(filterString)).toBe('foo');
expect(String.prototype.split).toHaveBeenCalledWith(':');
});
2 Test for the actual expected output of the function.
I also like this approach because you test for actual input/output how the function is meant to be used. On the downside you're indirectly testing String.prototype.split here as well, you're testing if it actually does what it says it does.
it('should return the correct output', function () {
expect(stringUtils.splitFilterString('foo:bar')).toEqual(['foo', 'bar']);
expect(stringUtils.splitFilterString('foobar')).toEqual(['foobar']);
});
In the first example you are coupling the test with the implementation, that's the main difference.
This might get in your way later on if you find out that there's a superFastSplit that you want to use, because you need to change your code and your test.
So in this case the test is not providing real value because it might hold you back when you want to refactor: "I want to refactor that portion of code, but those damn tests mean I have to do everything twice!".
In the second example you are testing behaviour, so your code will allow you to use superFastSplit because you are not interested in how you get to your result, you are interested that the result is consistent across implementations.
EDIT After OP comment
In the case of an external API I would still follow the "don't harm my future self / colleague" path, so I would do the easiest thing, which IMHO is to use a module that mocks the external API, something like this:
nock('http://external.api.com')
.get('/end/point')
.reply(200);
Of course you have to be careful and don't try to cover too many scenarios, because you are basically deciding what the external API will return, so I would say I would just test the ok and nok scenarios here, and cover all the details in an integration test.
Under the covers what nock does is:
Nock works by overriding Node's http.request function. Also, it overrides http.ClientRequest too to cover for modules that use it directly.
You can always test for any side effect that the API call has in your code, but I feel that that approach is more difficult to follow when applying TDD.

How to trace function calls in Marionette

Somewhere in the global scope:
let App = Backbone.Marionette.Application.extend({});
window.Ext = new App();
Inside Module A:
Ext.vent.trigger('Tracked:Email:send', {
from_email: tracking_data.sender,
to_emails: tracking_data.to,
cc_emails: tracking_data.cc,
email_id: tracking_data.email_id || '',
template: tracking_data.template_id || '',
subject: tracking_data.subject
});
Inside Module B:
Ext.vent.on('all', function (evt_name, params) {
// something else...
console.log(params);
}
The object's properties (from_email, to_emails & cc_emails) are undefined when I call console.log in Module B.
I've tried to debug this using console.trace, but the console doesn't show any functions that are involved in changing the object. I've also tried using Object.observe to capture the act of changing the object but no changes are detected.
Can some please teach me some debugging techniques to trace function calls and events in Marionette.
The scenario is:
our codebase is huge.
I'm a new guy at our company so I'm not sure if there are other functions or events that are involved.
I'm the only front end developer right now.
The good news: there's nothing special about Marionette here. Everyday JavaScript debugging techniques will help you out.
The bad news: as you're aware, debugging large, event-driven applications is difficult.
You could take these steps in Chrome dev tools (YMMV with other browsers):
Split the App.trigger line to get a reference to the parameters object:
let options = { from_email: ... }
App.vent.trigger('Tracked:Email:send', options);
Set a breakpoint on Ext.vent.trigger and run your code.
When execution pauses at the breakpoint, check that options contains the expected values. If not, the problem is somewhere before App.vent.trigger. Look up the call stack for any functions that affect tracking_data. You may need to check the Async checkbox if it is populated by asynchronous code.
If options contains the values you expect, carry on...
Whilst execution is paused use the console to save a global reference to your parameters object:
> OPTIONS = options
Add OPTIONS as a watch expression. Expand the item so you can watch changes to its properties.
Step through (including Marionette and Backbone code) until you see the OPTIONS properties change. If you don't see Marionette code when you step into App.vent.trigger you may need to remove Marionette from your black boxed libraries.

Testing tab navigation order

In one of our tests, we need to make sure that the tab keyboard navigation inside a form is performed in the correct order.
Question: What is the conventional way to check the tab navigation order with protractor?
Currently we are solving it by repeating the following step for as many input fields existing in a form (code below):
check the ID of the currently focused element (using getId())
send TAB key to the currently focused element
Here is the example spec:
it("should navigate with tab correctly", function () {
var regCodePage = new RegCodePage();
browser.wait(protractor.ExpectedConditions.visibilityOf(regCodePage.title), 10000);
// registration code field has focus by default
expect(regCodePage.registrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Remember Registration Code
regCodePage.registrationCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.rememberRegistrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Request Code
regCodePage.rememberRegistrationCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.requestCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Cancel
regCodePage.requestCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.cancelButton.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved back to the input
regCodePage.cancelButton.sendKeys(protractor.Key.TAB);
expect(regCodePage.registrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
});
where regCodePage is a Page Object:
var RegCodePage = function () {
this.title = element(by.css("div.modal-header b.login-modal-title"));
this.registrationCode = element(by.id("regCode"));
this.rememberRegistrationCode = element(by.id("rememberRegCode"));
this.requestCode = element(by.id("forgotCode"));
this.errorMessage = element(by.css("div.auth-reg-code-block div#message"));
this.sendRegCode = element(by.id("sendRegCode"));
this.cancelButton = element(by.id("cancelButton"));
this.closeButton = element(by.css("div.modal-header button.close"));
};
module.exports = RegCodePage;
It is working, but it is not really explicit and readable which makes it difficult to maintain. Also, another "smell" in the current approach is a code duplication.
If the current approach is how you would also do it, I would appreciate any insights about making it reusable.
I think the PageObject should define a tab order list, since that is really a direct property of the page, and should be expressible as simple data. An array of items seems like a sufficient representation, so something like:
this.tabOrder = [ this.registrationCode, this.rememberRegistrationCode, this.requestCode, this.cancelButton ];
Then you need a bit of generic code that can check a tab order.
function testTabOrder(tabOrder) {
// Assumes TAB order hasn't been messed with and page is on default element
tabOrder.forEach(function(el) {
expect(el.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
el.sendKeys(protractor.Key.TAB);
});
}
Then your test would be something like:
it('has correct tab order', function() {
var regCodePage = new RegCodePage(); // this should probably be in the beforeEach
testTabOrder(regCodePage.tabOrder);
});
Of course, this assumes each element has a "getId()" method that works. (That seems like a reasonable assumption to me, but some environments may not support it.)
I think this keeps the tab-order nicely isolated on the PageObject (so its easy to keep in sync with the page content and doesn't get lost in the code that verifies the order). The testing code seem "optimistic" (I suspect the real world will introduce enough problems that you will end up expanding this code a bit).
I haven't tried any of this yet, so feel free to downvote if this doesn't work. :)
Also, I believe the forEach loop will work as-is, but I wouldn't be surprised if it needs some more explicit promise handling to make the dependencies explicit.

Destroy a Backbone.Router and all Side effects after Creation

Let's say you want to be able to create a Backbone.Router instance that calls Backbone.history.start, destroy all traces of it, and create it again.
I need to do this for unit testing.
The problem is that creating a Backbone Router has global implications. Like Backbone.history, which is undefined until you make it.
var ReversibleRouter = Backbone.Router.extend({
initialize: function(){
_buildAppPaths(this.options) // Implementation not shown. Builds the application paths
Backbone.history.start()
},
destroy: function(){
// TODO: Implement this
}
})
I'd like to be able to create and completely destroy so that I can do some unit testing on my application's implementation of the Router.
It it enough to call Backbone.history.stop() and set Backbone.history = undefined?
If you're looking to do more full stack integration types of testing, you can reset your application's router by using:
Backbone.history.stop()
Backbone.history = undefined // Could also probably use delete here :)
This has been successful so far--our tests probably instantiate and kill the router 5 times during our qunit testing. Works great.
Maybe mocking like #AdamTerlson is saying is the better way to go long term, but I'm still learning unit and integration testing philosophy. I don't mind having a full stack test or two, though.
This might be cheating, but I'm of the opinion you should be mocking functions outside of your unit of functionality that your testing and be especially sure your unit tests are not be allowed to hit the live DOM, server or the browser.
Look into SinonJS, it's great for this.
Here's an example of testing the initialize method of the router without ever affecting the browser history state.
test('...', function () {
// Arrange
var buildAppPaths = sinon.stub(ReversibleRouter.prototype, '_buildAppPaths');
var startHistory = sinon.stub(Backbone.history, 'start');
var options = {};
// Act
new ReversibleRouter(options);
// Assert
ok(buildAppPaths.calledWith(options));
ok(startHistory.calledOnce); // or ok(Backbone.history.start.calledOnce);
});
Under this approach, you have successfully asserted that all calls were made to external units of functionality, including params. At that point, you can trust that the external call will do its job (proven by additional unit tests).
To make calls outside of this one unit would be doing integration tests and not unit tests.

Event handling in Dojo

Taking Jeff Atwood's advice, I decided to use a JavaScript library for the very basic to-do list application I'm writing. I picked the Dojo toolkit, version 1.1.1. At first, all was fine: the drag-and-drop code I wrote worked first time, you can drag tasks on-screen to change their order of precedence, and each drag-and-drop operation calls an event handler that sends an AJAX call to the server to let it know that order has been changed.
Then I went to add in the email tracking functionality. Standard stuff: new incoming emails have a unique ID number attached to their subject line, all subsequent emails about that problem can be tracked by simply leaving that ID number in the subject when you reply. So, we have a list of open tasks, each with their own ID number, and each of those tasks has a time-ordered list of associated emails. I wanted the text of those emails to be available to the user as they were looking at their list of tasks, so I made each task box a Dijit "Tree" control - top level contains the task description, branches contain email dates, and a single "leaf" off of each of those branches contains the email text.
First problem: I wanted the tree view to be fully-collapsed by default. After searching Google quite extensively, I found a number of solutions, all of which seemed to be valid for previous versions of Dojo but not the one I was using. I eventually figured out that the best solution would seem to be to have a event handler called when the Tree control had loaded that simply collapsed each branch/leaf. Unfortunately, even though the Tree control had been instantiated and its "startup" event handler called, the branches and leaves still hadn't loaded (the data was still being loaded via an AJAX call). So, I modified the system so that all email text and Tree structure is added server-side. This means the whole fully-populated Tree control is available when its startup event handler is called.
So, the startup event handler fully collapses the tree. Next, I couldn't find a "proper" way to have nice formatted text for the email leaves. I can put the email text in the leaf just fine, but any HTML gets escaped out and shows up in the web page. Cue more rummaging around Dojo's documentation (tends to be out of date, with code and examples for pre-1.0 versions) and Google. I eventually came up with the solution of getting JavaScript to go and read the SPAN element that's inside each leaf node and un-escape the escaped HTML code in it's innerHTML. I figured I'd put code to do this in with the fully-collapse-the-tree code, in the Tree control's startup event handler.
However... it turns out that the SPAN element isn't actually created until the user clicks on the expando (the little "+" symbol in a tree view you click to expand a node). Okay, fair enough - I'll add the re-formatting code to the onExpand() event handler, or whatever it's called. Which doesn't seem to exist. I've searched to documentation, I've searched Google... I'm quite possibly mis-understanding Dojo's "publish/subscribe" event handling system, but I think that mainly because there doesn't seem to be any comprehensive documentation for it anywhere (like, where do I find out what events I can subscribe to?).
So, in the end, the best solution I can come up with is to add an onClick event handler (not a "Dojo" event, but a plain JavaScript event that Dojo knows nothing about) to the expando node of each Tree branch that re-formats the HTML inside the SPAN element of each leaf. Except... when that is called, the SPAN element still doesn't exist (sometimes - other times it's been cached, just to further confuse you). Therefore, I have the event handler set up a timer that periodically calls a function that checks to see if the relevant SPAN element has turned up yet before then re-formatting it.
// An event handler called whenever a "email title" tree node is expanded.
function formatTreeNode(nodeID) {
if (dijit.byId(nodeID).getChildren().length != 0) {
clearInterval(nodeUpdateIntervalID);
messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML
if (messageBody.indexOf("<b>Message text:</b>") == -1) {
messageBody = messageBody.replace(/>/g, ">");
messageBody = messageBody.replace(/</g, "<");
messageBody = messageBody.replace(/&/g, "&");
dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>";
}
}
}
// An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed.
function setupTree(theTree) {
dijit.byId("tree-"+theTree).rootNode.collapse();
messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren();
for (pl = 0; pl < messageNode.length; pl++) {
messageNode[pl].collapse();
messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');");
}
}
The above has the feel of a truly horrible hack, and I feel sure I must have taken a wrong turn somewhere early on in my thought process. Can someone please tell me:
The correct way to go about putting nicely-formatted text inside a Dojo/Dijit Tree control.
The correct way to handle Dojo events, like where I can figure out what events are available for me to subscribe to.
A better JavaScript library to use (can I do what I want to with JQuery and avoid the all-around-the-houses approach seen above?).
PS: If you're naming a software project, give thought to its name's uniqueness in Google - I'm sure searching for "Dojo" documentation in Google would be easier without all the martial arts results getting in the way.
PPS: Firefox spellchecker knows how to spell "Atwood", correcting me when I put two 'T's instead of one. Is Jeff just that famous now?
I assume that you followed the dijit.Tree and dojo.data in Dojo 1.1 tutorial which directed you to pass the data to the tree control using a data store. That had me banging my head of a brick wall for a while.
Its not really a great approach and the alternative is not really well documented. You need to create a use model instead. I have included an example below of a tree model that I created for displaying the structure of an LDAP directory.
You will find the default implementation of the model in your dojo distribution at ./dijit/_tree/model.js. The comments should help you understand the functions supported by the model.
The IDirectoryService class the code below are stubs for server-side Java POJOs generated by Direct Web Remoting (DWR). I highly recommend DWR if you going to be doing a lot of client-server interaction.
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], {
getRoot : function(onItem) {
IDirectoryService.getRoots( function(roots) {
onItem(roots[0])
});
},
mayHaveChildren : function(item) {
return true;
},
getChildren : function(parentItem, onComplete) {
IDirectoryService.getChildrenImpl(parentItem, onComplete);
},
getIdentity : function(item) {
return item.dn;
},
getLabel : function(item) {
return item.rdn;
}
});
And here is an extract from the my JSP page where I created the model and used it to populate the tree control.
<div
dojoType="LDAPDirectoryTreeModel"
jsid="treeModel"
id="treeModel">
</div>
<div
jsid="tree"
id="tree"
dojoType="dijit.Tree" model="treeModel"
labelAttr="name"
label="${directory.host}:${directory.port}">
</div>

Categories

Resources