npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

friendly-webdriver

v2.0.0

Published

A friendly selenium-webdriver API

Downloads

33

Readme

Build Status

friendly-webdriver 🚕

This is a thin wrapper around the official Selenium JavaScript bindings.

While being very powerful, the official API often feels a little alien to JavaScript developers as it is very closely modeled after its Java ancestor.

NOTE: Since friendly-webdriver uses the official bindings under the hood 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.

Example:

var webdriver = require('friendly-webdriver');

var fwd = webdriver();
fwd.goto('https://www.google.com/');
fwd.fill({ q: 'friendly-webdriver npm' });
fwd.click('[jsaction=sf.lck]');

Contents

Concepts

FWD adds the concept of locators, filters and conditions on top of the WebDriver API.

Locators

By default FWD supports the following locators:

// CSS selectors
fwd.find('.button');

// XPath expressions
fwd.find({ xpath: '//main' });

// Client-side functions
fwd.find('ul').find({ js: function (el) {
  return el.firstChild;
}});

You can add custom locators via the addLocator() method.

Filters

When locating elements FWD also provides a way to filter the results. By default the following filters are supported:

fwd.find('.button', { visible: true });
fwd.find('.button', { text: 'click me' });
fwd.find('.button', { text: /click/ });

You can add custom filters via the addFilter() method.

Conditions

You can use FWD to wait for certain conditions to be met. The following conditions are supported by default:

fwd.wait({ url: '/welcome' }, 2000);
fwd.wait({ title: /friendly/ }, 2000);
fwd.wait(() => fwd.find('.foo').find('.bar'), 2000);
fwd.wait({ stale: fwd.find('body') }, 2000);

You can add custom conditions via the addCondition() method.

API

The top-level webdriver() function is a factory for FriendlyWebDriver instances which are thin wrappers around Selenium WebDriver objects.

FriendlyWebDriver

In addition to the WebDriver API, FriendlyWebDriver 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 FwdElementPromise for the first matching DOM element.

find(locator, [filter], [timeout])

  • locator: The locator to use
  • filter: An optional filter
  • timeout: 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 for
  • timeout: Optional timeout in milliseconds to wait
  • message: 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 FWD 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 to name
  • values: The values to be filled in
fwd.fill({
  user: 'John',
  email: '[email protected]'
})

// shortcut for:
fwd.find('[name="user"]').type('John');
fwd.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 be browser, client, driver, performance or server.
// activate logging
var fwd = webdriver({
  logging: {
    browser: 'severe',
    driver: 'debug'
  }
});

// fetch browser logs
fwd.getLogEntries('browser').then(entries => {
  console.log(entries.map(e => e.message));
});

addLocator

Registers a custom locator.

addLocator(fn)

  • fn A function that takes an arbitrary query 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:

fwd.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:
fwd.find({jQuery: 'div:animated' });

addFilter

Registers a custom filter.

addFilter(fn)

  • fn A function that takes an arbitrary filter 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:

fwd.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:
fwd.find('img', { minWidth: 200 });

addCondition

Registers a custom condition.

addCondition(fn)

  • fn A function that takes an arbitrary until object as argument and returns a webdriver.Contition if it wants to handle the given object.

use

Registers a plugin.

use(plugin)

FwdElement

FwdElement 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 fwd.find() that only takes the element's descendants into account.

findAll

findAll(selector, [filter], [timeout]) – Scoped version of fwd.findAll() that only takes the element's descendants into account.

fill

fill([attribute], values) – Scoped version of fwd.fill() that only takes the element's descendants into account.

parent

parent() – Returns a FwdElementPromise 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 FwdElement or {x: number, y: number} or a promise for either of both.

FwdElementPromise

FwdElementPromise mixes both FwdElement and an A+ compatible promise interface. This allows calls to the FwdElement 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 fwd = webdriver({
  base: 'https://www.example.com/',
  auth: {
    user: 'user',
    pass: 'secret'
  }
});

fwd.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 FWD inherit from their official counterparts, hence checks like se instanceof webdriver.WebDriver will still pass and you can use FWD as drop-in replacement inside your existing code.

Test runners

FWD 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 webdriver = require('friendly-webdriver');
var expect = require('unexpected').clone();

expect.use(require('unexpected-webdriver')());

describe('Google', function () {

  this.timeout(60000); // don't timeout too quickly

  it('should go to the FWD npm page', function () {
    var se = FWD();
    fwd.goto('https://www.google.com/');
    fwd.fill({ q: 'friendly-webdriver npm' });
    fwd.click('[jsaction=sf.lck]');
    fwd.wait({ url: 'https://www.npmjs.com/package/friendly-webdriver' });

    var name = fwd.find('.package-name');
    return expect(name, 'to contain text', 'FWD');
  });
});

History

This project was originally released under the name Selene and was later renamed to avoid confusion with the Selene Python library and its Jselene counterpart.

License

MIT