selene
v1.1.1
Published
A friendly selenium-webdriver API
Downloads
7
Readme
The aim of Selene is to provide a WebDriver API with a strong focus on developer experience (DX).
Unlike other webdriver libraries, Selene uses the official Selenium JavaScript bindings. This has the big advantage that you don't need a Selenium server in order to control browsers on your local machine. This does not only make things much easier to set up but also makes things considerably faster as it saves a lot of roundtrips.
While being very powerful, the official API sometimes feels a little alien to JavaScript developers as it is very closely modeled after its Java ancestor.
Example:
var selene = require('selene');
var se = selene();
se.goto('https://www.google.com/');
se.fill({ q: 'selene npm' });
se.click('[jsaction=sf.lck]');
Contents
Concepts
Selene adds the concept of locators, filters and conditions on top of the WebDriver API.
Locators
By default Selene supports the following locators:
// CSS selectors
se.find('.button');
// XPath expressions
se.find({ xpath: '//main' });
// Client-side functions
se.find('ul').find({ js: function (el) {
return el.firstChild;
}});
You can add custom locators via the addLocator()
method.
Filters
When locating elements Selene also provides a way to filter the results. By default the following filters are supported:
se.find('.button', { visible: true });
se.find('.button', { text: 'click me' });
se.find('.button', { text: /click/ });
You can add custom filters via the addFilter()
method.
Conditions
You can use Selene to wait for certain conditions to be met. The following conditions are supported by default:
se.wait({ url: '/welcome' }, 2000);
se.wait({ title: /Selene/ }, 2000);
se.wait(() => se.find('.foo').find('.bar'), 2000);
se.wait({ stale: se.find('body') }, 2000);
You can add custom conditions via the addCondition()
method.
API
The top-level selene()
function is a factory for Selene
instances which are thin wrappers around Selenium WebDriver
objects.
Selene
In addition to the WebDriver API, Selene
instances provide the following methods:
goto
goto(url)
– Navigates to the given URL. Relative paths will be resolved against the configured base URL.
Returns this
for chaining.
find
Returns a SeElementPromise for the first matching DOM element.
find(locator, [filter], [timeout])
locator
: The locator to usefilter
: An optional filtertimeout
: Optional timeout in milliseconds to wait for the element
findAll
Returns a promise for an Array
of all matching DOM elements. Takes the same arguments as find()
.
click
Shorthand for finding and clicking on an element.
click(locator, [filter], [timeout])
exists
Returns a promise for a boolean value indicating whether the specified element exists.
exists(locator, [filter])
wait
Wraps the Selenium WebDriver wait
method to support custom conditions.
wait(condition, [timeout], [message])
condition
: The condition to wait fortimeout
: Optional timeout in milliseconds to waitmessage
: Optional error message in case of a timeout
reloadUntil
Reloads the page until the given condition is met. Takes the same arguments as wait()
.
fill
Fills multiple input elements at once. The input elements are looked up using a CSS attribute selector. By default Selene expects each element to have a unique name
attribute. Optionally a custom attribute can be specified. Returns this
for chaining.
fill([attribute], values)
attribute
: Attribute name to use in CSS selectors. Defaults toname
values
: The values to be filled in
se.fill({
user: 'John',
email: '[email protected]'
})
// shortcut for:
se.find('[name="user"]').type('John');
se.find('[name="email"]').type('[email protected]');
getLogEntries
Fetches available log entries for a given type since the last call to this method, or from the start of the session.
getLogMessages(type)
type
: The log type to fetch. Can bebrowser
,client
,driver
,performance
orserver
.
// activate logging
var se = selene({
logging: {
browser: 'severe',
driver: 'debug'
}
});
// fetch browser logs
se.getLogEntries('browser').then(entries => {
console.log(entries.map(e => e.message));
});
addLocator
Registers a custom locator.
addLocator(fn)
fn
A function that takes an arbitraryquery
object as argument and returns{ description: String, by: (webdriver.by.By | Function) }
if it wants to handle the given query.
The following example adds a locator that uses jQuery to locate elements:
se.addLocator(query => {
// Handle only objects that have a `jQuery` property
if (typeof query === 'object' && 'jQuery' in query) {
const selector = query.$;
return {
description: `$(${selector})`, // description used in error messages
by: driver => driver.executeScript(jQuery, selector)
};
}
// This function gets executed inside the browser:
function jQuery(selector) {
if (!window.$) throw new Error('jQuery not found in global scope');
return window.$(selector);
}
});
// Use it like this:
se.find({jQuery: 'div:animated' });
addFilter
Registers a custom filter.
addFilter(fn)
fn
A function that takes an arbitraryfilter
object as argument and returns{ description: String, test: Function) }
if it wants to handle the given filter.
The following example adds a min-width filter:
se.addFilter(filter => {
if (filter.minWidth) {
return {
description: `width >= ${filter.minWidth}px`,
test(el) {
return el.getSize().then(size => size.width >= filter.minWidth);
}
};
}
});
// Use it like this:
se.find('img', { minWidth: 200 });
addCondition
Registers a custom condition.
addCondition(fn)
fn
A function that takes an arbitraryuntil
object as argument and returns awebdriver.until.Contition
if it wants to handle the given object.
use
Registers a plugin.
use(plugin)
plugin
A function that is invoked with aSelene
instance so it can calladdLocator()
,addFilter()
oraddCondition()
.
SeElement
SeElement
extends Selenium's WebElement and adds the following methods:
attr
attr(name)
– Returns a promise for the value of the attribute with the specified name. Alias for getAttribute(name)
css
css(prop)
– Returns the runtime CSS style of the given property. Alias for getCssValue(prop)
find
find(locator, [filter], [timeout])
– Scoped version of se.find() that only takes the element's descendants into account.
findAll
findAll(selector, [filter], [timeout])
– Scoped version of se.findAll() that only takes the element's descendants into account.
fill
fill([attribute], values)
– Scoped version of se.fill() that only takes the element's descendants into account.
parent
parent()
– Returns a SeElementPromise
for the element's parent node.
type
type(text)
– Sends keystrokes to the element to type the given text.
press
press(sequence)
– Sends a sequence of key combinations to the element.
The given sequence is split at spaces into chords. Each chord is then split at +
or -
into keys. If a key is not found in the list of supported key names all the characters will be pressed at once.
el.press('ctrl+a ctrl+x'); // sequence of 2 chords
el.press('ctrl-alt-delete'); // minus signs work too
el.press('alt++ alt-+ ALT-+'); // synonym
el.press('qwertz'); // press all at once
el.press('h e l l o SPACE w o r l d'); // hello world
dragDrop
dragDrop(target)
– Drags the element to the given target.
The target can either be a SeElement
or {x: number, y: number}
or a promise for either of both.
SeElementPromise
SeElementPromise
mixes both SeElement
and an A+ compatible promise interface. This allows calls to the SeElement
API before the underlying element promise has been fulfilled.
Configuration
| Option | Description | | ------ | ----------- | | base | Base URL against which all relative paths are resolved. | | auth | Credentials for HTTP basic authentication. |
var se = selene({
base: 'https://www.example.com/',
auth: {
user: 'user',
pass: 'secret'
}
});
se.goto('/').click('a[href="/welcome"]').wait({ url: '/welcome' });
The following options map 1:1 to the underlying WebDriver settings. For the meaning of each option please refer to the linked Selenium docs.
| Option | Description |
| ------ | ----------- |
| alerts | Sets how alert popups should be handled. Can be either "accept"
, "dismiss"
or "ignore"
. Defaults to "dismiss"
. |
| nativeEvents | Whether native events should be used. |
| proxyURL | URL of the proxy to use for the WebDriver's HTTP connections. |
| remoteURL | URL of a remote WebDriver server to use. As an alternative to this method, you may also set the SELENIUM_REMOTE_URL
environment variable. |
| scrollTo | How elements should be scrolled into view for interaction. Can either be "top"
or "bottom"
. |
| logging | Set the log level of different log types. Valid types are: browser
, client
, driver
, performance
or server
. Valid levels are: off
, severe
, warning
, info
, fine
, finer
, finest
, debug
or all
. |
| capabilities | The desired capabilities when requesting a new session. |
| envOverrides | Whether to allow the configuration to be overwritten by environment variables. Defaults to true
. |
| browser | The desired target browser. You may also specify a browser by setting the SELENIUM_BROWSER
environment variable to browser[:[version][:platform]]
. Defaults to firefox
|
Additionally you can provide browser-specific options under the keys
chrome
, opera
, safari
, ie
, edge
or firefox
.
NOTE
All objects created by Selene inherit from their official counterparts, hence checks like
se instanceof webdriver.WebDriver
will still pass and you can use Selene as drop-in replacement inside your existing code.
Test runners
Selene does not come with its own test runner nor is it bound to a specific assertion framework. You can use whatever tool you want for that. The following example uses Mocha and unexpected-webdriver.
var selene = require('selene');
var expect = require('unexpected');
expect.use(require('unexpected-webdriver')());
describe('Google', function () {
this.timeout(60000); // don't timeout too quickly
it('should go to the selene npm page', function () {
var se = selene();
se.goto('https://www.google.com/');
se.fill({ q: 'selene npm' });
se.click('[jsaction=sf.lck]');
se.wait({ url: 'https://www.npmjs.com/package/selene' });
var name = se.find('.package-name');
return expect(name, 'to contain text', 'selene');
});
});
License
MIT