Acceptance Testing

How does your client, manager, or tester, or any other non-technical person, know your web application is working? By opening the browser, accessing a site, clicking on links, filling in the forms, and actually seeing the content on a web page.

Acceptance (also called End to End) tests can cover standard but complex scenarios from a user's perspective. With acceptance tests you can be confident that users, following all defined scenarios, won't get errors. We check not just functionality of application but a user interface (UI) as well.

By default CodeceptJS uses WebDriverIO helper and Selenium to automate browser. Within web page you can locate elements, interact with them, and check that expected elements are present on a page. However, you can also choose Nightmare or Protractor helpers, driven by corresponding libraries. No matter of helper and library you use for acceptance testing, CodeceptJS should execute same actions in similar manner.

In case of CodeceptJS you can be sure that in code it will be as easy as it sounds. You just describe a test scenario with JavaScript DSL and allow the framework to handle the rest.

Within web page you can locate elements, interact with them, and check that expected elements are present on a page. That is what a test look like. That is what a test look like.

I.fillField('Username', 'john');
I.fillField('Password', '123456');
I.see('Welcome, John');

This is how we can check that login form of a simple web application works. At first we opened /login page, then filled forms and in the end we saw the greetings text.

Locating Element

Element can be found by CSS or XPath locators. Practically every steps in WebDriverIO helper accept them both.

I.seeElement('.user'); // element with CSS class user
I.seeElement('//button[contains(., "press me")]'); // button

By default CodeceptJS tries to guess the locator type. In order to specify exact locator type you can pass a hash called strict locator.

I.seeElement({css: 'div.user'});
I.seeElement({xpath: '//div[@class=user]'});

Strict locators allow to specify additional locator types:

// locate form element by name
I.seeElement({name: 'password'});
// locate element by id
I.seeElement({id: 'users'});

In mobile testing you can use ~ to specify accessibility id to locate an element. In web application you can locate element by their aria-label value.

// locate element by [aria-label] attribute in web
// or by accessibility id in mobile


CodeceptJS provides a flexible syntax to specify an element to click.

By default CodeceptJS tries to find button or link with exact text on it

// search for link or button

If none found, CodeceptJS tries to find link or button containing that text. In case an image is clickable its alt attribute will be checked for text inclusion. Form buttons will also be searched by name.

To narrow down the results you can specify a context in second parameter.

I.click('Login', '.nav'); // search only in .nav
I.click('Login', {css: 'footer'}); // search only in footer

To skip the global search pass exact strict locator (or start locator with // or . or #). In this case you are not limited to buttons and links. Any element found by that locator is clicked.

// click element by CSS
// click element located by name inside a form
I.click({name: 'submit'}, '#user>form');

Filling Fields

Clicking the links is not what takes the most time during testing a web site. If your site consists only of links you can skip test automation. The most routine waste of time goes into the testing of forms. CodeceptJS provides several ways of doing that.

Let's submit this sample form for a test:

<form method="post" action="/update" id="update_form">
     <label for="user_name">Name</label>
     <input type="text" name="user[name]" id="user_name" />
     <label for="user_email">Email</label>
     <input type="text" name="user[email]" id="user_email" />
     <label for="user_gender">Gender</label>
     <select id="user_gender" name="user[gender]">
          <option value="m">Male</option>
          <option value="f">Female</option>
     <input type="submit" name="submitButton" value="Update" />

We need to fill in all those fields and click "Update" button. CodeceptJS matches form elements by their label, name, or by CSS or XPath locators.

// we are using label to match user_name field
I.fillField('Name', 'Miles');
// we can use input name
// select element by label, choose option by text
// click 'Update' button, found by text

Alternative scenario:

// we are using CSS
I.fillField('#user_name', 'Miles');
// select element by label, option by value
// click 'Update' button, found by name
I.click('submitButton', '#update_form');


In order to verify the expected behavior of a web application, web page connects should be checked. CodeceptJS provides built-in assertions for that. They start with see (or dontSee) prefix, as they describe user's current vision.

The most general and common assertion is see:

// Just a visible text on a page
// text inside .msg element
I.see('Hello', '.msg');
// opposite

You should provide a text as first argument, and optionally a locator to narrow the search context.

You can check that specific element exists (or not) on a page, as it was described in Locating Element section.


Additional assertions:

I.seeInField('user[name]', 'Miles');
I.seeInTitle('My Website');

To see all possible assertions see the helper's reference.


Sometimes you need to retrieve a data from a page to use it in next steps of a scenario. Imagine, application generates a password and you want to ensure that user can login using this password.

Scenario('login with generated password', async (I) => {
  I.fillField('email', 'miles@davis.com');
  I.click('Generate Password');
  const password = await I.grabTextFrom('#password');
  I.fillField('email', 'miles@davis.com');
  I.fillField('password', password);
  I.click('Log in!');
  I.see('Hello, Miles');

grabTextFrom action is used here to retrieve text from an element. All actions starting with grab prefix are expected to return data. In order to synchronize this step with a scenario you should pause test execution with await keyword of ES6. To make it work your test should be written inside a async function (notice async in its definition).

Scenario('use page title', async (I) => {
  // ...
  const password = await I.grabTextFrom('#password');
  I.fillField('password', password);


In modern web applications rendering is happen on client side. Sometimes that may cause delays. A test may fail while trying to click an element which has not appeared on a page yet. To handle this cases wait* methods introduced.

I.waitForElement('#agree_button', 30); // secs
// clicks a button only when it is visible

More wait actions can be found in helper's reference.


It is possible to wait for elements pragmatically. If a test uses element which is not on a page yet, CodeceptJS will wait for few extra seconds before failing. This feature is based on Implicit Wait of Selenium. CodeceptJS enables implicit wait only when searching for a specific element and disables in all other cases. Thus, the performance of a test is not affected.

SmartWait can be enabled by setting wait option in WebDriverIO config. Add "smartWait": 5000 to wait for additional 5s.

SmartWait works with a CSS/XPath locators in click, seeElement and other methods. See where it is enabled and where is not:

I.click('Login'); // DISABLED, not a locator
I.fillField('user', 'davert'); // DISABLED, not a specific locator
I.fillField({name: 'password'}, '123456'); // ENABLED, strict locator
I.click('#login'); // ENABLED, locator is CSS ID
I.see('Hello, Davert'); // DISABLED, Not a locator
I.seeElement('#userbar'); // ENABLED
I.dontSeeElement('#login'); // DISABLED, can't wait for element to hide
I.seeNumberOfElements('button.link', 5); // DISABLED, can wait only for one element

SmartWait doesn't check element for visibility, so tests may fail even element is on a page.

Usage example:

// we use smartWait: 5000 instead of
// I.waitForElement('#click-me', 5);
// to wait for element on page

If it's hard to define what to wait, it is recommended to use retries to rerun flaky steps.


within operator can be used to work inside IFrames. Special frame locator is required to locate the iframe and get into its context.

See example:

within({frame: "#editor"}, () => {

Nested IFrames can be set by passing array (WebDriverIO, Nightmare & Puppeteer only):

within({frame: [".content", "#editor"]}, () => {

Multiple Sessions

CodeceptJS allows to run several browser sessions inside a test. This can be useful for testing communication between users inside a system, for instance in chats. To open another browser use session() function as shown in example:

Scenario('test app', (I) => {
  I.fillField('name', 'davert');
  I.click('Sign In');
  I.see('Hello, davert');
  session('john', () => {
    // another session started
    I.fillField('name', 'john');
    I.click('Sign In');
    I.see('Hello, john');
  // switching back to default session
  I.fillField('message', 'Hi, john');
  // there is a message from current user
  I.see('me: Hi, john', '.messages');
  session('john', () => {
    // let's check if john received it
    I.see('davert: Hi, john', '.messages');

session function expects a first parameter to be a name of a session. You can switch back to session by using the same name.

You can override config for session by passing second parameter:

session('john', { browser: 'firefox' } , () => {
  // run this steps in firefox

or just start session without switching to it. Call session passing only its name:

Scenario('test', (I) => {
  // opens 3 additional browsers


  // switch to session by its name
  session('mary', () => {

session can return value which can be used in scenario:

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

Function passed into session can use I, page objects, and any objects declared for the scenario. This function can also be declared as async (but doesn't work as generator).

Also, you can use within inside a session but you can't call session from inside within.


CodeceptJS through helpers provides user friendly API to interact with a webpage. In this section we described using WebDriverIO helper which allows to control browser through Selenium WebDriver.

© 2015 DavertMik <davert@codegyre.com> (http://codegyre.com)
Licensed under the MIT License.