webextensions-jsdom v1.4.1
WebExtensions JSDOM
When testing WebExtensions you might want to test your browser_action/page_action default_popup, sidebar_action default_panel or background page/scripts inside JSDOM. This package lets you do that based on the manifest.json. It will automatically stub window.browser with webextensions-api-mock.
Installation
npm install --save-dev webextensions-jsdom sinonImportant: sinon is a peer dependency, so you have to install it yourself. That's because it can otherwise lead to unexpected assertion behavior when sinon does instanceof checks internally. It also allows to upgrade sinon without the need to bump the version in webextensions-api-mock.
Usage
const webExtensionsJSDOM = require("webextensions-jsdom");
const webExtension = await webExtensionsJSDOM.fromManifest(
"/absolute/path/to/manifest.json"
);Based on what's given in your manifest.json this will create JSDOM instances with stubbed browser and load popup/sidebar/background in it.
The resolved return value is an <object> with several properties:
- background
<object>, with propertiesdom,window,document,browseranddestroy(If background page or scripts are defined in the manifest) - popup
<object>, with propertiesdom,window,document,browseranddestroy(If browser_action with default_popup is defined in the manifest) - pageActionPopup
<object>, with propertiesdom,window,document,browseranddestroy(If page_action with default_popup is defined in the manifest) - sidebar
<object>, with propertiesdom,window,document,browseranddestroy(If sidebar_action with default_panel is defined in the manifest) - destroy
<function>, shortcut tobackground.destroy,popup.destroyandsidebar.destroy
dom is a new JSDOM instance. window is a shortcut to dom.window. document is a shortcut to dom.window.document. browser is a new webextensions-api-mock instance that is also exposed on dom.window.browser. And destroy is a function to clean up. More infos in the API docs.
If you expose variables in your code on window, you can access them now, or trigger registered listeners by e.g. browser.webRequest.onBeforeRequest.addListener.yield([arguments]).
Automatic wiring
If popup/sidebar and background are defined and loaded then runtime.sendMessage in the popup is automatically wired with runtime.onMessage in the background if you pass the wiring: true option. That makes it possible to e.g. "click" elements in the popup and then check if the background was called accordingly, making it ideal for feature-testing.
await webExtensionsJSDOM.fromManifest("/absolute/path/to/manifest.json", {
wiring: true
});API Fake
Passing apiFake: true in the options to fromManifest automatically applies webextensions-api-fake to the browser stubs. It will imitate some of the WebExtensions API behavior (like an in-memory storage), so you don't have to manually define behavior. This is especially useful when feature-testing.
await webExtensionsJSDOM.fromManifest("/absolute/path/to/manifest.json", {
apiFake: true
});Code Coverage
Code coverage with nyc / istanbul is supported if you execute the test using webextensions-jsdom with nyc. To get coverage-output you need to call the exposed destroy function after the background, popup and/or sidebar are no longer needed. This should ideally be after each test.
If you want to know how that's possible you can check out this excellent article by @freaktechnik.
Chrome Extensions
Not supported, but you could use webextension-polyfill.
Example
In your manifest.json you have default_popup and background page defined:
{
"browser_action": {
"default_popup": "popup.html"
},
"background": {
"page": "background.html"
}
}Note: "scripts" are supported too.
In your popup.js loaded from popup.html you have something like this:
document.getElementById("doSomethingUseful").addEventListener("click", () => {
browser.runtime.sendMessage({
method: "usefulMessage"
});
});and in your background.js loaded from the background.html
browser.runtime.onMessage.addListener(message => {
// do something useful with the message
});To test this with webextensions-jsdom you can do (using mocha, chai and sinon-chai in this case):
const path = require("path");
const sinonChai = require("sinon-chai");
const chai = require("chai");
const expect = chai.expect;
chai.use(sinonChai);
const webExtensionsJSDOM = require("webextensions-jsdom");
const manifestPath = path.resolve(
path.join(__dirname, "path/to/manifest.json")
);
describe("Example", () => {
let webExtension;
beforeEach(async () => {
webExtension = await webExtensionsJSDOM.fromManifest(manifestPath, {
wiring: true
});
});
describe("Clicking in the popup", () => {
beforeEach(async () => {
await webExtension.popup.document
.getElementById("doSomethingUseful")
.click();
});
it("should call the background", async () => {
expect(
webExtension.background.browser.onMessage.addListener
).to.have.been.calledWithMatch({
method: "usefulMessage"
});
});
});
afterEach(async () => {
await webExtension.destroy();
});
});There's a fully functional example in examples/random-container-tab.
API
Exported function fromManifest(path, options)
- path
<string>, required, absolute path to themanifest.jsonfile - options
<object>, optional- background
<object|false>optional, iffalseis given background wont be loaded- jsdom
<object>, optional, this will set all given properties as options for the JSDOM constructor, an useful example might bebeforeParse(window). Note: SettingresourcesorrunScriptsmight lead to unexpected behavior. - afterBuild(background)
<function>optional, executed directly after the background dom is build (might be useful to do things before the popup dom starts building). If a Promise is returned it will be resolved before continuing.
- jsdom
- popup
<object|false>optional, iffalseis given popup wont be loaded- jsdom
<object>, optional, this will set all given properties as options for the JSDOM constructor, an useful example might bebeforeParse(window). Note: SettingresourcesorrunScriptsmight lead to unexpected behavior.
- jsdom
- pageActionPopup
<object|false>optional, iffalseis given popup wont be loaded- jsdom
<object>, optional, this will set all given properties as options for the JSDOM constructor, an useful example might bebeforeParse(window). Note: SettingresourcesorrunScriptsmight lead to unexpected behavior. - afterBuild(popup)
<function>optional, executed after the popup dom is build. If a Promise is returned it will be resolved before continuing.
- jsdom
- sidebar
<object|false>optional, iffalseis given sidebar wont be loaded- jsdom
<object>, optional, this will set all given properties as options for the JSDOM constructor, an useful example might bebeforeParse(window). Note: SettingresourcesorrunScriptsmight lead to unexpected behavior. - afterBuild(sidebar)
<function>optional, executed after the sidebar dom is build. If a Promise is returned it will be resolved before continuing.
- jsdom
- autoload
<boolean>optional, iffalsewill not automatically load background/popup/sidebar (might be useful forloadURL) - apiFake
<boolean>optional, iftrueautomatically applies API fakes to thebrowserusingwebextensions-api-fakeand ifpath/_localesis present its content will get passed down to api-fake. - wiring
<boolean>optional, iftruethe automatic wiring is enabled
- background
Returns a Promise that resolves an <object> with the following properties in case of success:
background
<object>- dom
<object>the JSDOM object - window
<object>shortcut todom.window - document
<object>shortcut todom.window.document - browser
<object>stubbedbrowserusingwebextensions-api-mock - destroy
<function>destroy thedomand potentially write coverage data if executed withnyc. Returns a Promise that resolves if destroying is done.
- dom
popup
<object>- dom
<object>the JSDOM object - window
<object>shortcut todom.window - document
<object>shortcut todom.window.document - browser
<object>stubbedbrowserusingwebextensions-api-mock - destroy
<function>destroy thedomand potentially write coverage data if executed withnyc. Returns a Promise that resolves if destroying is done. - helper
<object>- clickElementById(id)
<function>shortcut fordom.window.document.getElementById(id).click();, returns a promise
- clickElementById(id)
- dom
pageActionPopup
<object>- dom
<object>the JSDOM object - window
<object>shortcut todom.window - document
<object>shortcut todom.window.document - browser
<object>stubbedbrowserusingwebextensions-api-mock - destroy
<function>destroy thedomand potentially write coverage data if executed withnyc. Returns a Promise that resolves if destroying is done. - helper
<object>- clickElementById(id)
<function>shortcut fordom.window.document.getElementById(id).click();, returns a promise
- clickElementById(id)
- dom
sidebar
<object>- dom
<object>the JSDOM object - window
<object>shortcut todom.window - document
<object>shortcut todom.window.document - browser
<object>stubbedbrowserusingwebextensions-api-mock - destroy
<function>destroy thedomand potentially write coverage data if executed withnyc. Returns a Promise that resolves if destroying is done. - helper
<object>- clickElementById(id)
<function>shortcut fordom.window.document.getElementById(id).click();, returns a promise
- clickElementById(id)
- dom
destroy
<function>, shortcut to callbackground.destroy,popup.destroyandsidebar.destroy. Returns a Promise that resolves if destroying is done.
Exported function fromFile(path, options)
Load an arbitrary .html file, accepts the following parameters:
- path
<string>, required, absolute path to the html file that should be loaded - options
<object>, optional, accepts the following parameters- apiFake
<boolean>optional, iftrueautomatically applies API fakes to thebrowserusingwebextensions-api-fake - jsdom
<object>, optional, this will set all given properties as options for the JSDOM constructor, an useful example might bebeforeParse(window). Note: SettingresourcesorrunScripts. might lead to unexpected behavior
- apiFake
Returns a Promise that resolves an <object> with the following properties in case of success:
- dom
<object>the JSDOM object - window
<object>shortcut todom.window - document
<object>shortcut todom.window.document - browser
<object>stubbedbrowserusingwebextensions-api-mock - destroy
<function>destroy thedomand potentially write coverage data if executed withnyc. Returns a Promise that resolves if destroying is done.
GeckoDriver
If you're looking for a way to do functional testing with GeckoDriver then webextensions-geckodriver might be for you.
Sinon useFakeTimers
sinon.useFakeTimers({
toFake: ["setTimeout", "clearTimeout", "setInterval", "clearInterval"]
});6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago