automation-extra-plugin
TypeScript icon, indicating that this package has built-in type declarations

4.2.1-next.587 • Public • Published

automation-extra-plugin GitHub Workflow Status Discord npm

Base class to develop plugins for automation-extra.

Installation

yarn add automation-extra-plugin
Changelog
  • v4.1
    • Initial public release

Features

  • Supports playwright-extra as well as puppeteer-extra
  • Uses lifecycle events to hook into Puppeteer & Playwright execution
  • Ships with this.env and type guards to make multi-browser, multi-driver plugin development a breeze
  • Written in TypeScript (which means helpful auto-complete even if you're writing your plugins in JS)
  • Successor to puppeteer-extra-plugin, which only supports Puppeteer

Example

const { AutomationExtraPlugin } = require('automation-extra-plugin')

class DemoPlugin extends AutomationExtraPlugin {
  constructor(opts = {}) {
    super(opts)
  }

  static get id() {
    return 'demo'
  }

  async beforeLaunch(options) {
    // Modify launch options
    options.headless = false
  }

  async onBrowser(browser) {
    // Become aware of browser launch/connect
    console.log('onBrowser:', {
      driverName: this.env.driverName,
      browserName: this.env.browserName,
    })
  }

  async onPageCreated(page) {
    // Hook into page events
    console.log('Page created:', page.url())
    page.on('load', () => {
      console.log('Page loaded', page.url())
    })
    // Use a shim which unifies page.evaluateOnNewDocument and page.addInitScript
    this.shim(page).addScript(() => {
      navigator.alice = 'bob'
    })
  }
}

const demo = new DemoPlugin()

Use the plugin with Puppeteer:

const puppeteer = require('puppeteer-extra')

puppeteer.use(demo) // that's it :-)
puppeteer.launch({ headless: true }).then(async (browser) => {
  const page = await browser.newPage()
  await page.goto('https://example.com', { waitUntil: 'load' })
  const alice = await page.evaluate(() => navigator.alice)
  console.log(alice) // ==> bob
  await browser.close()
})

Use the same plugin with Playwright (chromium and webkit are supported as well):

const { firefox } = require('playwright-extra')

firefox.use(demo) // that's it :-)
firefox.launch({ headless: true }).then(async (browser) => {
  // ... same code as above
})

Contributing

If you're interested in releasing your plugin under the @extra organization please reach out to us through an issue or on our discord server. :-)

API

Table of Contents

class: PluginLifecycleMethods

Plugin lifecycle methods used by AutomationExtraPlugin.

These are hooking into Playwright/Puppeteer events and are meant to be overriden on a per-need basis in your own plugin extending AutomationExtraPlugin.


.onPluginRegistered()

Returns: Promise<void>

After the plugin has been registered, called early in the life-cycle (once the plugin has been added).


.beforeLaunch(options)

  • options LaunchOptions Puppeteer/Playwright launch options

Returns: Promise<(LaunchOptions | void)>

Before a new browser instance is created/launched.

Can be used to modify the puppeteer/playwright launch options by modifying or returning them.

Plugins using this method will be called in sequence to each be able to update the launch options.

Example:

async beforeLaunch (options) {
  if (this.opts.flashPluginPath) {
    options.args = options.args || []
    options.args.push(`--ppapi-flash-path=${this.opts.flashPluginPath}`)
  }
}

.afterLaunch(browser, launchContext)

  • browser Browser The puppeteer or playwright browser instance.
  • launchContext LaunchContext

After the browser has launched.

Note: Don't assume that there will only be a single browser instance during the lifecycle of a plugin. It's possible that pupeeteer.launch will be called multiple times and more than one browser created. In order to make the plugins as stateless as possible don't store a reference to the browser instance in the plugin but rather consider alternatives.

E.g. when using onPageCreated you can get a browser reference by using page.browser().

Alternatively you could expose a class method that takes a browser instance as a parameter to work with:

const fancyPlugin = require('puppeteer-extra-plugin-fancy')()
puppeteer.use(fancyPlugin)
const browser = await puppeteer.launch()
await fancyPlugin.killBrowser(browser)

Example:

async afterLaunch (browser, opts) {
  this.debug('browser has been launched', opts.options)
}

.beforeConnect(options)

  • options ConnectOptions Puppeteer/playwright connect options

Returns: Promise<(ConnectOptions | void)>

Before connecting to an existing browser instance.

Can be used to modify the puppeteer/playwright connect options by modifying or returning them.

Plugins using this method will be called in sequence to each be able to update the launch options.


.afterConnect(browser, launchContext)

  • browser Browser The puppeteer or playwright browser instance.
  • launchContext LaunchContext

After connecting to an existing browser instance.

Note: Don't assume that there will only be a single browser instance during the lifecycle of a plugin.


.onBrowser(browser, launchContext)

  • browser Browser The puppeteer or playwright browser instance.
  • launchContext LaunchContext

Called when a browser instance is available.

This applies to both launch and connect.

Convenience method created for plugins that need access to a browser instance and don't mind if it has been created through launch or connect.

Note: Don't assume that there will only be a single browser instance during the lifecycle of a plugin.


.beforeContext(options, browser)

  • options Playwright.BrowserContextOptions Playwright browser context options
  • browser Playwright.Browser Playwright browser

Returns: Promise<(Playwright.BrowserContextOptions | void)>

Before a new browser context is created.

Note: Currently only triggered by playwright, as puppeteer's usage of context is very lackluster.

Plugins using this method will be called in sequence to each be able to update the context options.


.onContextCreated(context, options)

  • context Playwright.BrowserContext Playwright browser context
  • options Playwright.BrowserContextOptions Playwright browser context options

After a new browser context has been created.

Note: playwright specific.


.onPageCreated(page)

  • page (Puppeteer.Page | Playwright.Page)

Called when a page has been created.

The event will also fire for popup pages.

Example:

async onPageCreated (page) {
  let ua = await page.browser().userAgent()
  if (this.opts.stripHeadless) {
    ua = ua.replace('HeadlessChrome/', 'Chrome/')
  }
  this.debug('new ua', ua)
  await page.setUserAgent(ua)
}

.onPageClose(page)

  • page Page

Called when a page has been closed.


.onContextClose(context)

  • context Playwright.BrowserContext

Called when a browser context has been closed.

Note: playwright specific.


.onDisconnected(browser)

  • browser Browser The puppeteer or playwright browser instance.

Called when the browser got disconnected.

This might happen because of one of the following:

  • The browser is closed or crashed
  • The browser.disconnect method was called

class: AutomationExtraPlugin

Extends: PluginLifecycleMethods

AutomationExtraPlugin - Meant to be used as a base class and it's methods overridden.

Implements all PluginLifecycleMethods.

Example:

class Plugin extends AutomationExtraPlugin {
  static id = 'foobar'
  constructor(opts = {}) {
    super(opts)
  }

  async beforeLaunch(options) {
    options.headless = false
    return options
  }
}

.env

Type: LauncherEnv

Contains info regarding the launcher environment the plugin runs in

  • See: LauncherEnv

.shim(page)

  • page Page

Returns: PageShim

Unified Page methods for Playwright & Puppeteer


.defaults

Type: PluginOptions

Plugin defaults (optional).

If defined will be (deep-)merged with the (optional) user supplied options (supplied during plugin instantiation).

The result of merging defaults with user supplied options can be accessed through this.opts.

Example:

get defaults () {
  return {
    stripHeadless: true,
    makeWindows: true,
    customFn: null
  }
}

// Users can overwrite plugin defaults during instantiation:
puppeteer.use(require('puppeteer-extra-plugin-foobar')({ makeWindows: false }))
  • See: [[opts]]

.requirements

Type: PluginRequirements

Plugin requirements (optional).

Signal certain plugin requirements to the base class and the user.

Currently supported:

  • launch
    • If the plugin only supports locally created browser instances (no puppeteer.connect()), will output a warning to the user.
  • headful
    • If the plugin doesn't work in headless: true mode, will output a warning to the user.
  • runLast
    • In case the plugin prefers to run after the others. Useful when the plugin needs data from others.

Example:

get requirements () {
  return new Set(['runLast', 'dataFromPlugins'])
}

.filter

Type: (Filter | undefined)

Plugin filter statements (optional).

Filter this plugin from being called depending on the environment.

Example:

get filter() {
  return {
    include: ['playwright:chromium', 'puppeteer:chromium']
  }
}

.dependencies

Type: PluginDependencies

Plugin dependencies (optional).

Missing plugins will be required() by automation-extra.

Example:

// Will ensure the 'puppeteer-extra-plugin-user-preferences' plugin is loaded.
get dependencies () {
  return new Set(['user-preferences'])
}

// Will load `user-preferences` plugin and pass `{ beCool: true }` as opts
get dependencies () {
  return new Map([['user-preferences', { beCool: true }]])
}

.plugins

Type: Array<AutomationExtraPluginInstance>

Add additional plugins (optional).

Expects an array of AutomationExtraPlugin instances, not classes. This is intended to be used by "meta" plugins that use other plugins behind the scenes.

The benefit over using dependencies is that this doesn't use the framework for dynamic imports, but requires explicit imports which bundlers like webkit handle much better.

Missing plugins listed here will be added at the start of launch or connect events.


.opts

Type: PluginOptions

Access the plugin options (usually the defaults merged with user defined options)

To skip the auto-merging of defaults with user supplied opts don't define a defaults property and set the this._opts Object in your plugin constructor directly.

Example:

get defaults () { return { foo: "bar" } }

async onPageCreated (page) {
  this.debug(this.opts.foo) // => bar
}
  • See: [[defaults]]

.debug

Type: Debugger

Convenience debug logger based on the debug module. Will automatically namespace the logging output to the plugin package name.

# toggle output using environment variables
DEBUG=automation-extra-plugin:<plugin_id> node foo.js
# to debug all the things:
DEBUG=automation-extra,automation-extra-plugin:* node foo.js

Example:

this.debug('hello world')
// will output e.g. 'automation-extra-plugin:anonymize-ua hello world'

.id

Plugin id/name (required)

Convention:

  • Package: automation-extra-plugin-anonymize-ua
  • Name: anonymize-ua

Example:

static id = 'anonymize-ua';
// or
static get id() {
  return 'anonymize-ua'
}

class: TypeGuards

TypeGuards: They allow differentiating between different objects and types.

Type guards work by discriminating against properties only found in that specific type. This is especially useful when used with TypeScript as it improves type safety.


.isPage(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isBrowser(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isPuppeteerPage(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isPuppeteerBrowser(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isPuppeteerBrowserContext(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isPlaywrightPage(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isPlaywrightBrowser(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


.isPlaywrightBrowserContext(obj)

  • obj any The object to test

Returns: boolean

Type guard, will make TypeScript understand which type we're working with.


class: LauncherEnv

Extends: TypeGuards

Stores environment specific info, populated by the launcher. This allows sane plugin development in a multi-browser, multi-driver environment.


.driverName

The name of the driver currently in use: "playwright" | "puppeteer".


.browserName

The name of the browser engine currently in use: "chromium" | "firefox" | "webkit" | "unknown".

Note: With puppeteer the browser will only be known once a browser object is available (after launching or connecting), as they support defining the browser during .launch().


.isPuppeteer

Check if current driver is puppeteer


.isPlaywright

Check if current driver is playwright


.isChromium

Check if current browser is chrome or chromium


.isFirefox

Check if current browser is firefox


.isWebkit

Check if current browser is webkit


.isBrowserKnown

Check if current browser is known


class: PageShim

Unified Page methods for Playwright & Puppeteer. They support common actions through a single API.


.addScript(script, arg?)

Adds a script which would be evaluated in one of the following scenarios:

Whenever the page is navigated. Whenever the child frame is attached or navigated. In this case, the script is evaluated in the context of the newly attached frame.

The script is evaluated after the document was created but before any of its scripts were run.

  • See: Playwright: addInitScript Puppeteer: evaluateOnNewDocument

License

Copyright © 2018 - 2021, berstend̡̲̫̹̠̖͚͓̔̄̓̐̄͛̀͘. Released under the MIT License.

Package Sidebar

Install

npm i automation-extra-plugin

Weekly Downloads

592

Version

4.2.1-next.587

License

MIT

Unpacked Size

169 kB

Total Files

16

Last publish

Collaborators

  • berstend