keyboard

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.


Replacement for Logitech UltraX: Logitech Wireless Solar Keyboard K750

Logitech UltraX Media Keyboard

Six years ago I purchased Logitech UltraX, which is still the best keyboard I have ever had. Unfortunately, as you know, keyboard cannot live for ever. Two years ago I wanted to buy the same model again, but…UltraX is no longer made. I was looking for some on amazon and ebay, and I found only Logitech Ultra X with PS/2. I bought it and it was fine, but I needed also PS/2->USB converter (my laptop doesn’t have PS/2 like most of laptops today). It works fine, but sometimes it loses connection. Only solution I found is to disconnect and insert it again into the USB port.

Logitech UltraX Premium

Recently I split a tea on the keyboard and keys are not working very well anymore. Thus I needed a new one. I wanted to buy the same model. It was still available on eBay, but I found this discussion, where one guy recommended Logitech K750. Its price is around $60 (too much), but I found refurbished for $30 (acceptable) and I bought it.

Logitech wireless solar keyboard k750

After a month, I can say that I am satisfied. It is wireless, but works smoothly (almost like wired). I didn’t have any communication issues so far. The cool thing is the fact that it doesn’t have replaceable batteries. It charges itself through solar panels. Additionally Logitech provides Solar App, which shows the battery condition and how much ‘light power’ is it getting from the light/sun. The two screenshots below shows Solar App when light is turned on (left) and turned off (right).

Logitech Solar AppLogitech Solar App - Light Off

The most important capability: keys. They are nice, low profiled, but a bit loose and loud. It doesn’t bother me though. I can say that K750 is a one class lower than Apple Keyboard, which I was using for 2 months in last summer (working on Mac). I would purchase Apple keyboard (better keys), if it would have ‘Windows’ version. For now, I am satisfied with K750.

If you are looking for UltraX-like keyboard, then K750 is definitely worth to consider!