Locators

CodeceptJS provides flexible strategies for locating elements:

Most methods in CodeceptJS use locators which can be either a string or an object.

If the locator is an object, it should have a single element, with the key signifying the locator type (id, name, css, xpath, link, or class) and the value being the locator itself. This is called a "strict" locator.

Examples:

Writing good locators can be tricky. The Mozilla team has written an excellent guide titled Writing reliable locators for Selenium and WebDriver tests.

If you prefer, you may also pass a string for the locator. This is called a "fuzzy" locator. In this case, CodeceptJS uses a variety of heuristics (depending on the exact method called) to determine what element you're referring to. If you are locating a clickable element or an input element, CodeceptJS will use semantic locators.

For example, here's the heuristic used for the fillField method:

  1. Does the locator look like an ID selector (e.g. "#foo")? If so, try to find an input element matching that ID.
  2. If nothing found, check if locator looks like a CSS selector. If so, run it.
  3. If nothing found, check if locator looks like an XPath expression. If so, run it.
  4. If nothing found, check if there is an input element with a corresponding name.
  5. If nothing found, check if there is a label with specified text for input element.
  6. If nothing found, throw an ElementNotFound exception.

Be warned that fuzzy locators can be significantly slower than strict locators. If speed is a concern, it's recommended you stick with explicitly specifying the locator type via object syntax.

CSS and XPath

Both CSS and XPath is supported. Usually CodeceptJS can guess locator's type:

// select by CSS
I.seeElement('.user .profile');
I.seeElement('#user-name');

// select by XPath
I.seeElement('//table/tr/td[position()=3]');

To specify exact locator type use strict locators:

// it's not clear that 'button' is actual CSS locator
I.seeElement({ css: 'button' });

// it's not clear that 'descendant::table/tr' is actual XPath locator
I.seeElement({ xpath: 'descendant::table/tr' });

Semantic Locators

CodeceptJS can guess an element's locator from context. For example, when clicking CodeceptJS will try to find a link or button by their text When typing into a field this field can be located by its name, placeholder.

I.click('Sign In');
I.fillField('Username', 'davert');

Various strategies are used to locate semantic elements. However, they may run slower than specifying locator by XPath or CSS.

Locator Builder

CodeceptJS provides a fluent builder to compose custom locators in JavaScript. Use locate function to start.

To locate a element inside label with text: 'Hello' use:

locate('a')
  .withAttr({ href: '#' })
  .inside(locate('label').withText('Hello'));

which will produce following XPath:

.//a[@href = '#'][ancestor::label[contains(., 'Hello')]]

Locator builder accepts both XPath and CSS as parameters but converts them to XPath as more feature-rich format. Sometimes provided locators can get very long so it's recommended to simplify the output by providing a brief description for generated XPath:

locate('//table')
  .find('a')
  .withText('Edit')
  .as('edit button')
// will be printed as 'edit button'

locate has following methods:

find

Finds an element inside a located.

// find td inside a table
locate('table').find('td');

Switches current element to found one. Can accept another locate call or strict locator.

withAttr

Find an element with provided attributes

// find input with placeholder 'Type in name'
locate('input').withAttr({ placeholder: 'Type in name' });

withChild

Finds an element which contains a child element provided:

// finds form with <select> inside it
locate('form').withChild('select');

withText

Finds element containing a text

locate('span').withText('Warning');

first

Get first element:

locate('#table td').first();

last

Get last element:

locate('#table td').last();

at

Get element at position:

// first element
locate('#table td').at(1);
// second element
locate('#table td').at(2);
// second element from end
locate('#table td').at(-2);

inside

Finds an element which contains an provided ancestor:

// finds `select` element inside #user_profile
locate('select').inside('form#user_profile');

before

Finds element located before the provided one

// finds `button` before .btn-cancel
locate('button').before('.btn-cancel');

after

Finds element located after the provided one

// finds `button` after .btn-cancel
locate('button').after('.btn-cancel');

ID Locators

ID locators are best to select the exact semantic element in web and mobile testing:

Within

To specify the exact area on a page where actions can be performed you can use within function. Everything executed in its context will be narrowed to context specified by locator:

Usage: within('section', ()=>{})

I.amOnPage('https://github.com');
within('.js-signup-form', () => {
  I.fillField('user[login]', 'User');
  I.fillField('user[email]', 'user@user.com');
  I.fillField('user[password]', 'user@user.com');
  I.click('button');
});
I.see('There were problems creating your account.');

within can also work with iframes

When running steps inside a within block will be shown with a shift:

within

Within can return a value which can be used in a scenario:

// inside async function
const val = await within('#sidebar', () => {
  return I.grabTextFrom({ css: 'h1' });
});
I.fillField('Description', val);

Next: Debugging >>>