Skip to content
This repository has been archived by the owner on Jan 3, 2019. It is now read-only.

Latest commit

 

History

History
1213 lines (792 loc) · 25.5 KB

README.md

File metadata and controls

1213 lines (792 loc) · 25.5 KB

Cybernaut

npm build coverage semantic-release Greenkeeper TypeScript

Reliable, zero configuration end-to-end testing in BDD-style.

Example

const {browser, defineElement, it, test} = require('cybernaut');

test('Star the "clebert/cybernaut" repository on GitHub', async t => {
  await t.perform(browser.loadPage('https://github.com/clebert/cybernaut'));

  await t.assert(browser.pageTitle, it.should.contain('clebert/cybernaut'));

  await t.perform(browser.takeScreenshot());

  const starButton = defineElement('ul.pagehead-actions > li:nth-child(2) > a:nth-child(1)');

  await t.perform(starButton.click());
});

The above example can be executed without configuration or dependencies in a Docker container:

git clone https://github.com/clebert/cybernaut.git && \
cd cybernaut/example/ && \
mkdir -p screenshots && \
docker build -t clebert/cybernaut-example . && \
docker run -v screenshots:/opt/cybernaut-example/screenshots -t clebert/cybernaut-example

Contents

Installation

npm install --save-dev cybernaut

If the default configuration is used, Chrome and a matching version of chromedriver must also be installed:

npm install --save-dev chromedriver

Usage

Starting Cybernaut

Cybernaut must be started from the command line:

$(npm bin)/cybernaut

Directories are recursed, with all **/*.e2e.js files being treated as test files.

Cybernaut produces output in TAP format, tap-mocha-reporter can be used to format it:

npm install --save-dev tap-mocha-reporter
$(npm bin)/cybernaut | $(npm bin)/tap-mocha-reporter spec

Configuring Cybernaut

The following configuration is active by default:

{
  "capabilities": {"browserName": "chrome"},
  "concurrency": 1,
  "dependencies": ["chromedriver"],
  "exclude": ["**/node_modules/**/*"],
  "include": "**/*.e2e.js",
  "retries": 4,
  "retryDelay": 500
}

A separate configuration can be passed as a command line argument:

$(npm bin)/cybernaut firefox-config.js

Such a configuration can be validated with this JSON schema and written as a JSON file or JavaScript module:

firefox-config.json

{
  "capabilities": {"browserName": "firefox"},
  "dependencies": ["geckodriver"]
}

firefox-config.js

module.exports = {
  capabilities: {browserName: 'firefox'},
  dependencies: ['geckodriver']
};

A note for Firefox users: Cybernaut uses selenium-webdriver@3.3.0, which is incompatible with geckodriver@1.5.0. Until these incompatibilities have been solved, geckodriver@1.4.0 must be used.

Writing tests

It is recommended to write tests using async functions, which are natively supported by Node.js as of version 7. Alternatively, the tests must be transpiled using TypeScript or Babel.

If you write your tests with TypeScript, it is recommended to enable the TSLint rule no-floating-promises. This can prevent the await operators from being forgotten.

API

Type definition:

  • test(name: string, implementation?: Implementation): void
  • Implementation = (t: Test) => Promise<void>
  • Test

Example usage:

const {test} = require('cybernaut');

test('foo'); // This test will be marked as TODO

test('foo', async t => { // This test will be executed
  // ...
});

Type definition:

  • skip(name: string, implementation: Implementation): void
  • Implementation = (t: Test) => Promise<void>
  • Test

Example usage:

const {skip} = require('cybernaut');

skip('foo', async t => { // This test won't be executed (and marked as SKIP)
  // ...
});

Type definition:

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.loadPage('http://bar.baz'));
});

Type definition:

  • defineElement(selector: string): Element
  • Element

Example usage:

const {defineElement, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.perform(bar.click());
});

Type definition:

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.contain('bar'));
});

Type definition:

  • assert<T>(accessor: Accessor<T>, predicate: Predicate<T>, retries?: number, retryDelay?: number): Promise<void>

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.contain('bar')); // Throws an error if the condition isn't met
});

Type definition:

  • perform(action: Action, retries?: number, retryDelay?: number): Promise<void>

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.loadPage('http://bar.baz')); // Throws an error if the action fails
});

Type definition:

  • verify<T>(accessor: Accessor<T>, predicate: Predicate<T>, retries?: number, retryDelay?: number): Promise<boolean>

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  if (await t.verify(browser.pageTitle, it.should.contain('bar'))) { // Evaluates to false if the condition isn't met
    // ...
  }
});

Type definition:

  • fail(message: string, cause: Error): void

Example TAP output: not ok 1 - bar (cause: baz)

Example usage:

const {test} = require('cybernaut');

test('foo', async t => {
  t.fail('bar', new Error('baz')); // Throws a new error
});

Type definition:

  • pass(message: string): void

Example TAP output: ok 1 - bar

Example usage:

const {test} = require('cybernaut');

test('foo', async t => {
  t.pass('bar'); // Prints a successful-test line in TAP format on standard output
});

Type definition:

  • pageTitle: Accessor<string>

Example TAP output: ok 1 - page title should contain 'bar' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.contain('bar'));
});

Type definition:

  • pageUrl: Accessor<string>

Example TAP output: ok 1 - page url should contain 'http://bar.baz' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageUrl, it.should.contain('http://bar.baz'));
});

Type definition:

  • windowX: Accessor<number>

Example TAP output: ok 1 - window x-position should equal 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowX, it.should.equal(123));
});

Type definition:

  • windowY: Accessor<number>

Example TAP output: ok 1 - window y-position should equal 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowY, it.should.equal(123));
});

Type definition:

  • windowWidth: Accessor<number>

Example TAP output: ok 1 - window width should equal 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowWidth, it.should.equal(123));
});

Type definition:

  • windowHeight: Accessor<number>

Example TAP output: ok 1 - window height should equal 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowHeight, it.should.equal(123));
});

Type definition:

  • scriptResult(scriptName: string, script: Script): Accessor<any>
  • Script = (callback: (result?: any) => void) => void

Example TAP output: ok 1 - result of script 'bar' should equal 'baz' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.scriptResult('bar', callback => {
    // This function will be executed in browser context, so any references to outer scope won't work
    // ...

    callback('baz');
  }), it.should.equal('baz'));
});

Type definition:

  • executeScript(scriptName: string, script: Script): Action
  • Script = (callback: (result?: any) => void) => void

Example TAP output: ok 1 - execute script 'bar' (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.executeScript('bar', callback => {
    // This function will be executed in browser context, so any references to outer scope won't work
    // ...

    callback();
  }));
});

Type definition:

  • loadPage(url: string): Action

Example TAP output: ok 1 - load page 'http://bar.baz' (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.loadPage('http://bar.baz'));
});

Type definition:

  • maximizeWindow(): Action

Example TAP output: ok 1 - maximize window (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.maximizeWindow());
});

Type definition:

  • navigateBack(): Action

Example TAP output: ok 1 - navigate back (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.navigateBack());
});

Type definition:

  • navigateForward(): Action

Example TAP output: ok 1 - navigate forward (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.navigateForward());
});

Type definition:

  • reloadPage(): Action

Example TAP output: ok 1 - reload page (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.reloadPage());
});

Type definition:

  • setWindowPosition(x: number, y: number): Action

Example TAP output: ok 1 - set window position to 123,456 (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.setWindowPosition(123, 456));
});

Type definition:

  • setWindowSize(width: number, height: number): Action

Example TAP output: ok 1 - set window size to 123x456 (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.setWindowSize(123, 456));
});

Type definition:

  • sleep(duration: number): Action

Example TAP output: ok 1 - sleep for 123 ms (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.sleep(123));
});

Type definition:

  • takeScreenshot(): Action

Example TAP output: ok 1 - take screenshot 'screenshots/07cc9369-ab10-4221-9bc9-18ad12b87c7c.png' (attempt 1 of 5)

Example usage:

const {browser, test} = require('cybernaut');

test('foo', async t => {
  await t.perform(browser.takeScreenshot());
});

Type definition:

  • tagName: Accessor<string>

Example TAP output: ok 1 - tag name of element '#bar' should equal 'div' (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.tagName, it.should.equal('div'));
});

Type definition:

  • text: Accessor<string>

Example TAP output: ok 1 - text of element '#bar' should equal 'baz' (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.text, it.should.equal('baz'));
});

Type definition:

  • visibility: Accessor<boolean>

Example TAP output: ok 1 - visibility of element '#bar' should equal true (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.visibility, it.should.equal(true));
});

Type definition:

  • x: Accessor<number>

Example TAP output: ok 1 - x-position of element '#bar' should equal 123 (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.x, it.should.equal(123));
});

Type definition:

  • y: Accessor<number>

Example TAP output: ok 1 - y-position of element '#bar' should equal 123 (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.y, it.should.equal(123));
});

Type definition:

  • width: Accessor<number>

Example TAP output: ok 1 - width of element '#bar' should equal 123 (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.width, it.should.equal(123));
});

Type definition:

  • height: Accessor<number>

Example TAP output: ok 1 - height of element '#bar' should equal 123 (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.height, it.should.equal(123));
});

Type definition:

  • cssValue(cssName: string): Accessor<string>

Example TAP output: ok 1 - css value 'margin-left' of element '#bar' should equal '22px' (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.cssValue('margin-left'), it.should.equal('22px'));
});

Type definition:

  • propertyValue(propertyName: string): Accessor<string | null>

Example TAP output: ok 1 - property value 'id' of element '#bar' should equal 'bar' (attempt 1 of 5)

Example usage:

const {defineElement, it, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.assert(bar.propertyValue('id'), it.should.equal('bar'));
});

Type definition:

  • clearValue(): Action

Example TAP output: ok 1 - clear value of element '#bar' (attempt 1 of 5)

Example usage:

const {defineElement, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.perform(bar.clearValue());
});

Type definition:

  • click(): Action

Example TAP output: ok 1 - click on element '#bar' (attempt 1 of 5)

Example usage:

const {defineElement, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.perform(bar.click());
});

Type definition:

  • sendKeys(...keys: string[]): Action

Example TAP output: ok 1 - send keys [ 'text was', 'Key.CONTROL', 'a', 'Key.NULL', 'now text is' ] to element '#bar' (attempt 1 of 5)

Example usage:

const {Key, defineElement, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.perform(bar.sendKeys('text was', Key.CONTROL, 'a', Key.NULL, 'now text is'));
});

Modifier keys (Key.SHIFT, Key.CONTROL, Key.ALT, Key.META) are stateful; once a modifier is processed in the keysequence, that key state is toggled until one of the following occurs:

  • The modifier key is encountered again in the sequence. At this point the state of the key is toggled (along with the appropriate keyup/down events).

  • The Key.NULL key is encountered in the sequence. When this key is encountered, all modifier keys current in the down state are released (with accompanying keyup events).

  • The end of the keysequence is encountered. When there are no more keys to type, all depressed modifier keys are released (with accompanying keyup events).

-- selenium-webdriver.WebElement

Type definition:

  • submitForm(): Action

Example TAP output: ok 1 - submit form containing element '#bar' (attempt 1 of 5)

Example usage:

const {defineElement, test} = require('cybernaut');

test('foo', async t => {
  const bar = defineElement('#bar');

  await t.perform(bar.submitForm());
});

Type definition:

  • contain(expectedValue: string): Predicate<string>

Example TAP output: ok 1 - page title should contain 'bar' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.contain('bar'));
});

Type definition:

  • not.contain(expectedValue: string): Predicate<string>

Example TAP output: ok 1 - page title should not contain 'bar' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.not.contain('bar'));
});

Type definition:

  • equal<T>(expectedValue: T): Predicate<T>

Example TAP output: ok 1 - page title should equal 'bar' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.equal('bar'));
});

Note: The comparison is done via deep-strict-equal.

Type definition:

  • not.equal<T>(expectedValue: T): Predicate<T>

Example TAP output: ok 1 - page title should not equal 'bar' (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.not.equal('bar'));
});

Note: The comparison is done via deep-strict-equal.

Type definition:

  • match(regex: RegExp): Predicate<string>

Example TAP output: ok 1 - page title should match /bar/ (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.match(/bar/));
});

Type definition:

  • not.match(regex: RegExp): Predicate<string>

Example TAP output: ok 1 - page title should not match /bar/ (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.pageTitle, it.should.not.match(/bar/));
});

Type definition:

  • be.above(expectedValue: number): Predicate<number>

Example TAP output: ok 1 - window width should be above 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowWidth, it.should.be.above(123)); // windowWidth > 123
});

Type definition:

  • be.at.least(expectedValue: number): Predicate<number>

Example TAP output: ok 1 - window width should be at least 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowWidth, it.should.be.at.least(123)); // windowWidth >= 123
});

Type definition:

  • be.below(expectedValue: number): Predicate<number>

Example TAP output: ok 1 - window width should be below 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowWidth, it.should.be.below(123)); // windowWidth < 123
});

Type definition:

  • be.at.most(expectedValue: number): Predicate<number>

Example TAP output: ok 1 - window width should be at most 123 (attempt 1 of 5)

Example usage:

const {browser, it, test} = require('cybernaut');

test('foo', async t => {
  await t.assert(browser.windowWidth, it.should.be.at.most(123)); // windowWidth <= 123
});

Development

Installing dev dependencies

npm install

Watching sources and unit tests

npm run watch

Checking for formatting and linting errors

npm run check

Formatting sources

npm run format

Committing a new change

npm run cz

Built by (c) Clemens Akens. Released under the MIT license.