Testing asynchronous UI

If the UI under test initializes asynchronously, or a method you're testing finishes asynchronously, your test must also be asynchronous.

One way to ensure a test runs at the appropriate time is to provide a Promise the test can hook onto, e.g. CrSettingsPrefs.initialized.

Otherwise, pay attention to the flavor of asynchronicity involved. If you're using mocha, you can combine these techniques with the done() callback or returning a Promise from a test.

Macrotask timing

Tasks scheduled for the next event loop: setTimeout/setInterval, user interaction.

Testing: call setTimeout and test UI in the callback.

Microtask timing

Tasks scheduled for the end of the current event loop:
Newly queued microtasks are processed in the current event loop. E.g., Promises will call chained resolve/reject handlers before a setTimeout callback, even if setTimeout was called before the chain of Promises was created.

Testing: create a Promise (or call setTimeout) and test the UI in the callback. Return a Promise from mocha tests.

Polymer microtask timing

Microtasks generated by Polymer are special*, and can be executed immediately via Polymer.dom.flush(). This is a useful alternative to nesting setTimeouts in tests to ensure the DOM under test is up to date.

Testing: prefer Polymer.dom.flush() over asynchronous callbacks.

*Polymer queues would-be microtasks and schedules them at the last possible moment. Flushing this queue runs the tasks. If the queue is not flushed, the microtasks are scheduled (strangely, via a MutationObserver).


Some Polymer methods are explicitly synchronous:

  • Lifecycle callbacks. created and ready are called immediately when creating the element, and attached is called immediately after the element is added to the DOM.
  • Events dispatched dynamically. el.fire('click'), MockInteractions.tap(el)