Skip to content

Interact with the app

Your test gets two things you’ll use constantly: app to navigate and make requests, and I to interact with the UI. I is Interactor.js, rooted in your app’s current document.

await app.goto('/contacts/new');
await I.find('Name').then.fill('Robert');
await I.click('Save');
await I.find('$([role=status])').text('Contact saved');

app.goto waits for the document. The last assertion waits for the result you care about. You don’t need a sleep between those steps.

Strings match text, placeholders and related label controls. They match the whole text; use a regular expression for a partial match. Use $(...) when you need CSS:

await I.find('Email').then.fill('rob@example.com');
await I.find(/Contact saved/).rendered();
await I.find('$(input[name=email])').value('rob@example.com');

rendered() checks displayed content, including below the fold. visible() also requires it to be in the viewport. Assertions retry until they match, then continue immediately. expectTimeout sets the assertion deadline.

For changing browser state, use an assertion callback:

await I.assert(
() => app.window.location.pathname === '/dashboard',
'Expected the dashboard URL'
);

Use scope() to narrow the root. It finds that root again after navigation, so you can reuse the scope within a test:

let contact = I.scope('$(form)');
await contact.find('Email').then.fill('rob@example.com');

A missing root fails. Create scopes before chaining; use I.scope(selector) rather than I.find(selector).then.scope().

fill() replaces an editable input or textarea value and sends input/change events. To select a file:

await contact.find('Photo').then.upload({
name: 'note.txt',
bytes: 'Storm notes',
type: 'text/plain',
});

Pass no files to clear the input. Use custom interactors when these actions form a workflow you’ll share.

Built-in assertions cover text, values, visibility and more. contains(selector) checks for a descendant, not a text substring. For a DOM predicate, bind a scope:

await I.scope('$([role=status])').assert(
({ $ }) => $.textContent.includes('Saved')
);

Ordinary actions use DOM methods and synthetic events. For browser defaults such as Tab order or CSS hover, use native browser behavior.