javascript

Testing focus in JavaScript

This blog post is an overview of testing focus behavior in web browser.

During the work on Azure Portal I spent quite a bit of time on ensuring rich keyboard support. This requires appropriate focus management. When user press some keyboard shortcut, the focus should move to appropriate element. There is a good article on MDN about Keyboard-navigable JavaScript widgets. Focus behavior should be also tested very succinctly. As it is very easy to change (break) with even the smallest change in the HTML or JavaScript.

Standard test goes as follows:

  • Arrange: open some page
  • Act: execute some keyboard shortcut that should open particular page and/or set focus on particular element
  • Assert: check if expected element is focused

How to check if element is focused

The simplest way is to use jQuery:

expect($(element).is(':focus')).equals(true);

However this may not always work. Especially if you run your unit tests in parallel, because $element.is(‘:focus’) will not work when window does not have focus.

The better (right) way is to use document.activeElement:

expect(document.activeElement).equals(element);

This will work even when browser window is not focused.

Testing async actions

Sometimes, keyboard invoke asynchronous actions that eventually will change focus. This can cause:

  • false negative tests: assertion was executed before focus was eventually set
  • false positives: finally focused element, got focus after assertion was executed

The simplest recipe for the first problem is to delay assertion:

setTimeout(() => {
    expect(document.activeElement).equals(element);
    done();
}, 100);

The problem with this approach is choosing appropriate delay for assertion. Therefore it is better to avoid raw setTimeout, and use polling approach that I described in my post setTimeout considered harmful:

poll(
    () => document.activeElement === element, 
    () => {
	       assert(true);
	       start();
	  },
    () => {
	       assert(false);
	       start();
	  }
    );

The polling function can be also used for the second solution (by changing assertion order in callback functions). However, for the false positives problem, simple setTimeout is good enough, because we do not have a choice other than wait some particular period of time to execute assertion.

Invoking keyboard actions

There are 3 types of events that we can use to simulate keyboard action:

  • keydown
  • keyup
  • keypress

The safest bet is to use keydown. Why? An example might be using special keys. While in case of keypress – various browsers handle it differently (e.g., by not triggering the event), keydown is pretty consistent. I recommend you to check this article if you are interested in details. It was not updated for some time, but it will give you an idea.

In order to detect keyboard actions you may handle events on capture or bubbling phase. You should choose model depending on your needs, but in general bubbling phase is optimal solution. Additionally it is supported by jQuery, with browser inconsistencies taken care of for you.

To invoke keydown event in your tests, you need to set focus on the element first:

$(element)
  .focus()
  .trigger("keydown", { which: keyCode });

You can find JavaScript key codes here.

Summary

Use document.activeElement to check which element is focused.
Use polling approach for testing async actions.
Use keydown event, and bubble phase to invoke/handle keyboard actions.


ConnectJS and All Things Open

Last month I had a pleasure to speak at ConnectJS and All Things Open conferences.

ConnectJS

ConnectJS 2015

ConnectJS was not only about JavaScript, but about web development in general. There was even track dedicated for UI Design and User eXperience. The most popular during the conference were talks about ES6/ES2015 and React.

I delivered two sessions:

TDD with TypeScript, AngularJS and Node.js

// video coming soon

Code

Aurelia – the next generation Framework you will love

// video coming soon

Code

I addition to my talks I attended the following sessions:

Everything I Needed to Know, I Learned in Rabbinical School (Yitzchok Willroth) – this session was about sharing knowledge, and cooperation between developers. One thing I noticed, not only at conferences, but also at local meetups in Seattle area is that many people would like to go and speak at the conferences, but “they aren’t doing anything interesting in day-to-day job and they are not expert in anything”. They should have been at this session. Yitzchok was explaining how you can help others, and engage in Open Source. Moreover, he emphasized one, simple truth: every developer has something interesting to share.

Building Web Sites that Work Everywhere (Doris Chen) – very useful overview of web compatibility problems, and recommendation of tools that can help with that, like Autoprefixer. I also liked the quote from Patrick Lauke: “The userAgent property is ever-growing pack of lies”. Doris recommended that we should prefer feature detection over relying on userAgent strings.

Re-evaluating Front-end Performance Best Practices (Ben Vinegar) – the most important lesson learned at this session is the fact that whatever you find in JavaScript books written 2+ years ago might be already obsolete. Moreover, whatever you learn today, might be obsolete tomorrow. This is definitely not a good news for developers, but we need to deal with that and when reading anything on the web – thinking for ourselves.

Video killed the Telephone Star (Ben Klang) – WebRTC is coming to the browser. In this talk Ben demonstrated web app that allows to do a video conference (his implementation of Google Hangouts in Rails).

Lessons learned with TypeScript and ES2015 (Dylan Schiemann) – it was an overview of TypeScript and ES6 based on experience working on Dojo 2 Framework – the second largest application written in TypeScript (after Azure Portal). After this presentation I talked to Dylan, and he showed me another projects his company SitePen is working on: Intern – very flexible and powerful testing framework, and Mayhem – JS Framework written in TypeScript (still under development).

Functional Programming Basics in ES6 (Jeremy Fairbank) – tips&tricks you can do in JavaScript(ES6), but you cannot in OO strongly typed languages like C# or Java.

Lessons from Open Source @ Scale (Christine Abernathy) – Facebook has over 300 repos in github (after this talk I checked how many Microsoft have – almost 300). Christine explained how they help community by delivering Open Source, and how community helps them by contributing to their software.

Introducing Trix (Javan Makhmali, Sam Stephenson) – Javan and Sam created web based text editor, and they open sourced it right after this talk.

The rise of “API” first applications (Travis Tidwell) – this talk was about Micro Services, and modern applications architecture where we have multiple, independent endpoints responsible for one functionality each.

It Was Like That When I Got Here (Paul M. Jones) – it was a great talk about approaching legacy applications, and refactoring techniques. I enjoyed it even despite the fact that Paul was using PHP examples…I actually felt a bit sentimental as PHP was a language that get me started with Web Development when I was back in middle/high school 🙂

All Things Open

All Things Open 2015

All Things Open is one of the largest Open Source conferences in the United States. This year there was over 1700 attendees, and 13 tracks!

I gave a talk about TDD with TypeScript, AngularJS, and Node.js – the same as at ConnectJS.

On a day before the conference there was 5k run/sightseeing event at the evening. It was exactly what I needed before 2 days of seating. Kudos for organizers 🙂

Most of my time during the conference I spent in the room with front-end related sessions. Carina C. Zona explained problems with artificial intelligence and machine learning – Consequences of an Insightful Algorithm. Seth Vargo made an overview of Vargant – product that allows to create and configure universal development environment for every developer in the team. Christian Heilmann was encouraging people to learn JavaScript, ECMAScript 6, and to stop supporting old browsers, such as IE8, that has security vulnerabilities. Yehuda Katz explained how he and other contributors of Ember.js created version 2 without breaking a lot of APIs from version 1, and thus allowing developers for a smooth transition. I also liked the session about Netflix architecture by Andrew Spyker. I wish Andrew had more time to explain details more deeply. The surprising takeaway is that Netflix has 3x of everything. Which means – for every server, service and API they have additional 2 redundant.

The most widely commented session at the conference was keynote by Mark Russinovich. I was pretty surprised that people were surprised by Microsoft doing so much Open Source. For me this is a known fact for a few years now, but it seems that the rest of the World doesn’t know yet, and still see Microsoft as closed-source corporation that want to lock you in their technology.Well…that’s not true anymore. I also had a pleasure to met Christian Heilmann – former Evangelist of Mozilla who joined Microsoft with one mission: kill the Internet Explorer. I really enjoyed his session on ES6, and keynote.

At the speaker dinner I had a pleasure to seat at the table with Andrew Spyker from Netflix, Michael Laing of New York Times, Christine Abernathy from Facebook, and Eric Martindale – entrepreneur from Silicon Valley. We had interesting conversation of the future of Netflix, Internet Television, bitcoin, and digital newspapers. I also learned that New York Times is the only news paper that is profitable in the transition from paper to electronic.

At All Things Open I finally got awesome Ninja Cat stickers:

ThinkPad X1 stickers


IP heatmap generator

IP heatmap generator

Recently, we were looking at the error logs of the Azure Portal. One of our ideas – in order to investigate errors that were hard to diagnose – was to check in which part of the World users who get errors are located. The assumption was that people from, e.g., New Zealand might have slow connection issues (timeouts) more likely than people from USA or Europe. I couldn’t find any tool that does that, so I put a simple website at ipheatmap.azurewebsites.net. The main page use geoip-lite node library to geolocate IP address. For cross-check, you go to ipheatmap.azurewebsites.net/ipinfo.html, and the IP will be located using ipinfo.io API.

Creating website + setting up deployment from github took me less than 5 minutes on Azure. I didn’t have to do any special configuration to get it work. I didn’t even have to leave the Azure Portal to lookup github url, because it fetches all available repos automatically after you provide your credentials. Even better: repos are sorted by creation date – thus the recently created repo (one that you would choose most likely) is on top of the list.

Source code is available on github: https://github.com/jj09/ip-heatmap-generator.


Speech Recognition in the Browser

Last Thursday I had a pleasure to give talk about Speech Recognition in the Browser at the Code Fellows in Seattle.

Many people were surprised how easy it is to add speech recognition to your website with pure JavaScript. So I thought I will share a few code snippets here. It works in Chrome only so far.

Recognizing speech

This is how you can translate speech to text:

var sr = new webkitSpeechRecognition();
sr.onresult = function (evt) {
    console.log(evt.results[0][0].transcript);
}
sr.start();

You can also get the confidence level of the result:

var sr = new webkitSpeechRecognition();
sr.onresult = function (evt) {
    console.log(evt.results[0][0].transcript, evt.results[0][0].confidence);
}
sr.start();

You can get interim results:

sr.interimResults = true;	// false by default
sr.onresult = function(evt) {
	for (var i = 0; i < evt.results.length; ++i) {
		console.log(evt.results[i][0].transcript);
	};
};

Or different alternatives of recognized speech:

sr.maxAlternatives = 10;	// default = 1
sr.onresult = function(evt) {
	for (var i = 0; i < evt.results[0].length; ++i) {
		console.log(evt.results[0][i].transcript);
	}
}

You can set a language, e.g., to Polish:

sr.lang = 'pl-PL'

All above will stop recognition when you stop speaking. In order to do not stop recognition you need to set continuous flag to true. Additionally, this will treat every fragment of you speech as interim result, so you need to update onresult callback too:

sr.continuous = true;	// false by default
sr.onresult = function(evt) {
	console.log(evt.results[evt.results.length-1][0].transcript);
};

Speech Recognition object has other callbacks (than onresult) that you can take advantage of:

sr.onstart = function() { console.log("onstart"); }
sr.onend = function() { console.log("onend"); }
sr.onspeechstart = function() { console.info("speech start"); }
sr.onspeechend = function() { console.info("speech end"); }

Emitting speech

var msg = new SpeechSynthesisUtterance('Hi, I\'m Jakub!');
speechSynthesis.speak(msg);

You can also change the speaker voice:

var voices = window.speechSynthesis.getVoices();
msg.voice = voices[10]; // Note: some voices don't support altering params

There is also other options you can set:

msg.volume = 1; // 0 to 1
msg.pitch = 2; //0 to 2
msg.text = 'Hello World';
msg.lang = 'en-US';

msg.onend = function(e) {
	console.log('Finished in ' + event.elapsedTime + ' seconds.');
};

Summary

Speech is coming to the browser, and you can not stop it. The question is when most of websites will add voice support. Check out voiceCmdr – a library that I blogged about earlier this year, which helps to add voice commands to your websites in very easy way. You can also check out website that can be navigated with voice commands – you can find available commands in my blog post. You can find entire logic for voice commands support in this file (lines: 38-103).


setTimeout considered harmful

setTimeout - Steve's Tweet

Recently I learned the hard way about setTimeouts side effects.

101 setTimeout issue

Let’s say we have a following piece of code:

var someVar = 0;

var changeVar = function () {
    someVar = 10;
};

And unit test:

QUnit.asyncTest('should change variable to 10', function () {
    // Arrange
    someVar = 0;

    // Act
    changeVar();

    // Assert
    equals(someVar, 10);
});

Of course test passes, and we are happy.

Then, after some time, because of a reason (e.g., we want to fix some bug), we are adding setTimeout to changeVar function:

var changeVar = function () {
    setTimeout(function () {
        someVar = 10;
    }, 10);
};

Unit test does not pass anymore. So we are adding setTimeout to our unit test as well (ideally: appropriately longer than one in changeVar function to avoid confusion!):

QUnit.asyncTest('should change variable to 10', function () {
    // Arrange
    someVar = 0;

    // Act
    changeVar();

    // Assert
    setTimeout(function () {
        equals(someVar, 10);
    }, 20);
});

Then, we are introducing change to our code. Update only when someVar is not zero. We update function, and test accordingly:

var changeVar = function () {
    setTimeout(function () {
        if (someVar !== 0) {
            someVar = 10;
        }
    }, 10);
};

QUnit.asyncTest('should change variable to 10 if someVar != 0', function () {
    // Arrange
    someVar = 0;

    // Act
    changeVar();

    // Assert
    setTimeout(function () {
        equal(someVar, 0);
    }, 20);
});

Everything works. Great! Then, somebody else is fixing another bug – of course by increasing timeout:

var changeVar = function () {
    setTimeout(function () {
        if (someVar !== 0) {
            someVar = 10;
        }
    }, 50);
};

Still works, but when after some time we decide to change our logic:

var changeVar = function () {
    setTimeout(function () {
        if (someVar === 0) {
            someVar = 10;
        }
    }, 50);
};

Our test is still passing…when it shouldn’t!

Of course all of this is happening in large codebase with more complex logic.

But this is not that bad. Let’s take a look at more interesting scenario.

More complex case

We are in worst situation when we have waterfall of setTimeouts.

var fun1 = function () {
    setTimeout(function () {
        someVar++;
    }, 10);
};

var fun2 = function () {
    setTimeout(function () {
        fun1();
        someVar = 10;
    }, 10);
};

Guess if this unit test will pass:

QUnit.asyncTest('should change variable to 11', function() {
    // Arrange
    someVar = 0;

    // Act
    fun2();

    // Assert
    setTimeout(function () {
    	equal(someVar, 11);
    	start();
    }, 20);
});

You think it should? You are wrong!

But this test will pass:

QUnit.asyncTest('should change variable to 11', function() {
    // Arrange
    someVar = 0;

    // Act
    fun2();

    // Assert
    setTimeout(function () {
    	equal(someVar, 11);
    	start();
    }, 21);
});

Do you see the difference? Yes, timeout is 21 instead of 20. And of course everything will fall apart if somebody increase timeout in fun1 or fun2.

Solution

The best solution is not to use setTimeout at all. Unfortunately we need async operations sometimes. In this case – you should use promises if possible. Unfortunately World is not perfect, especially Web Development World, and sometimes you have to use setTimeout (e.g., for UI effects etc.). You may think that if you set long enough timeout in your unit tests, everything should be good, right? Well…remember that it will make your unit tests slower. Instead – you should use polling approach.

To apply it to the last example – using “Without Deferreds” approach – copy poll function to your codebase:

function poll(fn, callback, errback, timeout, interval) {
    var endTime = Number(new Date()) + (timeout || 2000);
    interval = interval || 100;

    (function p() {
            // If the condition is met, we're done! 
            if(fn()) {
                callback();
            }
            // If the condition isn't met but the timeout hasn't elapsed, go again
            else if (Number(new Date()) < endTime) {
                setTimeout(p, interval);
            }
            // Didn't match and too much time, reject!
            else {
                errback(new Error('timed out for ' + fn + ': ' + arguments));
            }
    })();
}

And call it in your unit test:

QUnit.asyncTest('should change variable to 11', function() {
    // Arrange
    someVar = 0;

    // Act
    fun2();

    // Assert
    poll(
	    function() {
	        return someVar === 11;
	    },
	    function() {
	        ok(true);
	        start();
	    },
	    function() {
	        ok(false);
	        start();
	    }
	);
});

Now, you do not have to guess what value will be appropriate for setTimeout delay, and you will speed up your tests as well.

When I see some strange behavior in code, the first thing I am looking at are setTimeout calls.