automation-extra-plugin v4.2.1-next.587
automation-extra-plugin

Base class to develop plugins for automation-extra.
Installation
yarn add automation-extra-plugin
- 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
- .onPluginRegistered()
- .beforeLaunch(options)
- .afterLaunch(browser, launchContext)
- .beforeConnect(options)
- .afterConnect(browser, launchContext)
- .onBrowser(browser, launchContext)
- .beforeContext(options, browser)
- .onContextCreated(context, options)
- .onPageCreated(page)
- .onPageClose(page)
- .onContextClose(context)
- .onDisconnected(browser)
- class: AutomationExtraPlugin
- class: TypeGuards
- class: LauncherEnv
- class: PageShim
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 Thepuppeteer
orplaywright
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 Thepuppeteer
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 Thepuppeteer
orplaywright
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 optionsbrowser
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 contextoptions
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)
}
- See: https://playwright.dev/#version=v1.3.0&path=docs%2Fapi.md&q=event-page
- See: https://pptr.dev/#?product=Puppeteer&version=main&show=api-event-targetcreated
.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 Thepuppeteer
orplaywright
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.
- If the plugin only supports locally created browser instances (no
headful
- If the plugin doesn't work in
headless: true
mode, will output a warning to the user.
- If the plugin doesn't work in
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.
4 years ago
4 years ago
4 years ago
4 years ago