foxr
Version:
Node.js API to control Firefox
328 lines (224 loc) • 6.58 kB
Markdown
# foxr
[](https://www.npmjs.com/package/foxr) [](https://packagephobia.now.sh/result?p=foxr) [](https://travis-ci.org/deepsweet/foxr) [](https://codecov.io/github/deepsweet/foxr)
Node.js API to control Firefox.
<img src="logo.svg" width="110" height="110" align="right" alt="logo"/>
* uses a built-in [Marionette](https://vakila.github.io/blog/marionette-act-i-automation/) through [remote protocol](https://firefox-source-docs.mozilla.org/testing/marionette/marionette/index.html)
* no [Selenium WebDriver](https://github.com/SeleniumHQ/selenium/wiki/FirefoxDriver) is needed
* works with [Headless mode](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode)
* compatible subset of [Puppeteer](https://github.com/GoogleChrome/puppeteer) API
At this point Foxr is more a proof of concept, [work is pretty much in progress](https://github.com/deepsweet/foxr/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aenhancement).
## Example
Run a locally installed Firefox:
```sh
/path/to/firefox -headless -marionette -safe-mode
```
Or a [dockerized version](https://github.com/deepsweet/firefox-headless-remote):
```sh
docker run -it --rm --shm-size 2g -p 2828:2828 deepsweet/firefox-headless-remote:68
```
```js
import foxr from 'foxr'
// const foxr = require('foxr').default
(async () => {
try {
const browser = await foxr.connect()
const page = await browser.newPage()
await page.goto('https://example.com')
await page.screenshot({ path: 'example.png' })
await browser.close()
} catch (error) {
console.error(error)
}
})()
```
## Install
```sh
yarn add --dev foxr
# or
npm install --save-dev foxr
```
## API
### Foxr
#### `connect`
Connect to the Marionette endpoint.
```ts
type TConnectOptions = {
host?: string,
port?: number,
defaultViewport?: {
width?: number,
height?: number
}
}
foxr.connect(options?: TConnectOptions): Promise<Browser>
```
* `host` – `'localhost'` by default
* `port` – `2828` by default
* `defaultViewport`
* `width` – `800` by default
* `height` – `600` by default
#### `launch`
```ts
type TLaunchOptions = {
args?: string[],
dumpio?: boolean,
executablePath: string,
headless?: boolean
} & TConnectOptions
foxr.launch(options?: TLaunchOptions): Promise<Browser>
```
* `args` – array of additional args, `['-marionette', '-safe-mode', '-no-remote']` by default
* `dumpio` – print browser process stdout and stderr, `false` by default
* `executablePath` – path to Firefox executable, required
* `headless` – whether to run browser in headless mode, `true` by default
### Browser
#### `close`
```ts
browser.close(): Promise<void>
```
#### `disconnect`
```ts
browser.disconnect(): Promise<void>
```
#### `newPage`
```ts
browser.newPage(): Promise<Page>
```
#### `pages`
```ts
browser.pages(): Promise<Page[]>
```
#### `install`
```ts
browser.install(extensionPath: string, isTemporary: boolean): Promise<string | null>
```
#### `uninstall`
```ts
browser.install(extensionId: string): Promise<void>
```
#### `getPref`
```ts
browser.getPref(pref: string, defaultBranch: boolean = false): Promise<any>
```
#### `setPref`
```ts
browser.setPref(pref: string, value: string | number | boolean, defaultBranch: boolean = false): Promise<void>
```
### Page
#### `$`
```ts
page.$(selector: string): Promise<ElementHandle | null>
```
#### `$$`
```ts
page.$$(selector: string): Promise<ElementHandle[]>
```
#### `$eval`
```ts
page.$eval(selector: string, func: TSerializableFunction, ...args: TEvaluateArg[]): Promise<TJsonValue | void>
```
#### `$$eval`
```ts
page.$$eval(selector: string, func: TSerializableFunction, ...args: TEvaluateArg[]): Promise<Array<TJsonValue | void>>
```
#### `bringToFront`
```ts
page.bringToFront(): Promise<void>
```
#### `browser`
```ts
page.browser(): TBrowser
```
#### `close`
```ts
page.close(): Promise<void>
```
#### `content`
```ts
page.content(): Promise<string>
```
#### `evaluate`
```ts
page.evaluate(target: string): Promise<TJsonValue | void>
page.evaluate(target: TSerializableFunction, ...args: TEvaluateArg[]): Promise<TJsonValue | void>
```
#### `evaluateHandle`
```ts
page.evaluate(target: string): Promise<JSHandle>
page.evaluate(target: TSerializableFunction, ...args: TEvaluateArg[]): Promise<JSHandle>
```
#### `focus`
```ts
page.focus(selector: string): Promise<void>
```
#### `goto`
```ts
page.goto(url: string): Promise<void>
```
#### `screenshot`
```ts
page.screenshot(options?: { path?: string }): Promise<Buffer>
```
#### `setContent`
```ts
page.setContent(html: string): Promise<void>
```
#### `title`
```ts
page.title(): Promise<string>
```
#### `url`
```ts
page.url(): Promise<string>
```
#### `viewport`
```ts
page.viewport(): Promise<{ width: number, height: number }>
```
### JSHandle
…
### ElementHandle
#### `$`
```ts
elementHandle.$(selector: string): Promise<ElementHandle | null>
```
#### `$$`
```ts
elementHandle.$$(selector: string): Promise<ElementHandle[]>
```
#### `click`
```ts
type TOptions = {
button?: 'left' | 'middle' | 'right',
clickCount?: number
}
elementHandle.click(options?: TOptions): Promise<void>
```
#### `focus`
```ts
elementHandle.focus(): Promise<void>
```
#### `hover`
```ts
elementHandle.hover(): Promise<void>
```
#### `press`
```ts
elementHandle.press(key: string): Promise<void>
```
Where `key` is of the [possible keys](./src/keys.ts) or a single character.
#### `screenshot`
```ts
elementHandle.screenshot(options?: { path?: string }): Promise<Buffer>
```
#### `type`
```ts
elementHandle.type(text: string): Promise<void>
```
## Development
See [my Start task runner preset](https://github.com/deepsweet/_/tree/master/packages/start-preset-node-ts-lib) for details.
## References
* Python Client: [API](https://marionette-client.readthedocs.io/en/latest/reference.html), [source](https://searchfox.org/mozilla-central/source/testing/marionette/client/)
* Perl Client: [API](https://metacpan.org/pod/Firefox::Marionette), [source](https://metacpan.org/source/DDICK/Firefox-Marionette-0.57/lib/Firefox)
* Node.js client (outdated): [source](https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette/client)
* [Marionette Google Group](https://groups.google.com/forum/#!forum/mozilla.tools.marionette)