nodeca / navit

Simple client testing from your scripts
MIT License
48 stars 7 forks source link

navit

CI NPM version

Wrapper for Electron to simplify browser tests scripting.

Install

Note, you need to install electron with this package, it is not included as a dependency.

npm install navit electron --save

Examples

const browser = require('./')({ timeout: 30000, engine: 'electron' });

const stack = []; // You can use lazy functions to pass data between stages,
                  // but arrays have more compact notation.

try {
  await browser
    .open('https://dev.nodeca.com')
    .wait(() => {
      try { return window.NodecaLoader.booted; } catch (__) { return false; }
    })
    .get.url(stack)
    .click('forum-category__content:first forum-section__title-link')
    .wait(data => location.url !== data[data.length - 1], stack)
    .test.exists('.forum-topiclines')
    .close();

  console.log('Succeeded');
} catch (err) {
  console.log(err));
}

Also look files in test folder. Those are real examples how to use navit with mocha.

API

  1. All methods are chainable.
  2. Methods, marked with + have direct aliases without namespace.
  3. Chain is then-able. You can apply await anytime, to execute stacked commands.
  4. Almost everywhere String & Number params can be defined as functions for lazy evaluation.

Known limitations:

Some methods like .get.evaluate() allow to pass params to evaluated functions. navit uses function's .length property, to properly detect params count, because tailing callbacks are optional. That means, such functions must have explicit parameters list in definition, and you must pass exactly the same params count as defined. We decided, it's not a big price for nice API.

Electron is NOT headless. To run your script in headless environment, you should xvfb.

new Navit(options, engineOpts)

options (not mandatory):

engineOpts (not mandatory, camelCase):

See https://www.electronjs.org/docs/api/command-line-switches. You can pass any options, supported by browser engine. Option names should be in camelCase.

Actions: .do.*()

Navigation:

DOM:

Waiting:

Get data/params: .get.*()

All functions, passed to .get.*, can be sync (with 1 param) or async (with 2 params). If function returns not falsy type of result (usually a Error) or throws exception, chain will be terminated. That can be used to create complex test conditions.

Sugar:

  1. If you pass Array instead of Function, data will be pushed into it.
  2. If fn returns Error object (or anything else not falsy), chain will be stopped with that value.
  3. If last param (callback) exists, chain will be finished as with .run method.

Set data/params: .set.*()

Assertions: .test.*() & test()

Tests available for the most of get.* methods:

Additional:

Special sugar:

Tabs: .tab.*()

Other

Batches

navit allows record sequence or commands to run it later with one call as many times as you wish.

// create
.batch.create('init_page', function() {
  this.
    .wait(function () {
      try {
        return window.NodecaLoader.booted;
      } catch (__) {}
      return false;
    });
    .viewport(1600, 1200)
    .inject(require.resolve('jquery/dist/jquery'))
    .fn(function () {
      console.log('Batch done.');
    })
})
// run
.batch('init_page')

.afterOpen

If you assign function to this property, it will be called after any .open and .reload definition to stack additional commands. This is experimental feature, that can be changed.

Sometime you may wish to do global setup for all opened pages. For example:

You can record your sequence to batch and automate it's injection after every open / reload. See example how we setup navit in nodeca:

// Wait for nodeca scripts load and check status
//
navit.batch.create('waitNodecaBooted', function () {
  this
    .wait(function () {
      try {
        return window.NodecaLoader.booted;
      } catch (__) {}
      return false;
    })
    .test.status(200);
});

navit.afterOpen = function () {
  this.batch('waitNodecaBooted');
};

Note. .afterOpen is called on chain definition phase, not on execution phase. It's ~ equivalent of typing content manually in test body. That's why it doesn't have callback to wait async operations - it's not needed.

Debug

If you assign environment variable DEBUG to navit, you will see debug message for every action.

Output example for DEBUG=navit mocha:

...
navit do.open('http://localhost:17345/test/fixtures/do/type.html') +25ms
navit do.type('#type-test') +20ms
navit test.evaluate() +9ms
navit do.type('#contenteditable-test') +2ms
navit test.evaluate() +9ms
  ✓ type (64ms)
...