This blog post is an overview of testing focus behavior in web browser.
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:
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:
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:
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:
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:
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:
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.