Interactor.js

Using Actions

Interactor creators

Interactor.js provides interactor creators that simplify making quick interactions without needing to create a new interactor each time. Since these actions return scoped interactors, other actions can then be chained from them as well.

import { focus, click } from 'interactor.js';

await focus('.email').type('email@domain.tld').blur();
await focus('.password').type('CorrectHorseBatteryStaple').blur();
await click('.submit').timeout(500);

Built-in actions that ship with interactor.js can be found here.

Actions return interactors, and each interactor has it's own timeout. Always prefer creating interactions by chaining actions together rather than awaiting on multiple interactors. This way the entire interaction remains quick and succinct.

Custom actions

Creating custom actions is as easy as returning interactors from your own functions. A scoped() helper is even provided if you prefer not to use the new keyword inside of custom actions.

import { scoped } from 'interactor.js';

function fill(selector, value) {
  return scoped(selector)
    .focus()
    .type(value)
    .blur();
}

await fill('.full-name', 'Name Namerson');

Interacting with the DOM

When the default actions don't work for your particular use-case and you need to interact with the DOM node directly, you can use the do() method of interactors and provide a custom callback to add to the interaction queue. Callbacks provided to do() are only run once, and if an error occurs it will be immediately thrown.

import { scoped } from 'interactor.js';

function checkValidity(selector) {
  return scoped(selector)
    .do(element => {
      // might trigger an 'invalid' event
      element.checkValidity();
    });
}

Interactors also have an $element getter that will lazily return the DOM node or throw an error if the node does not exist.

Asynchronous actions

When creating custom actions, you sometimes need to wait for specific DOM state to be met before interacting with the DOM node. This can be achieved with assertions via the assert() method. These assertions will be run repeatedly until they pass or until the interactor timeout has been exceeded. An assertion is considered to be passing when it does not throw an error or return false.

scoped('input')
  .assert(element => {
    if (!element.required && !element.pattern) {
      throw new Error('no pattern to check validity');
    }
  })
  .do(element => {
    element.checkValidity();
  });

Since assertions can be run repeatedly, they should be pure and free of side-effects. If given an async function, or a function that returns a promise, an error will be thrown.