Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

WebExtensions API Fake

When testing WebExtensions you might want a working fake implementation of the API in-memory available without spawning a complete browser.

This package depends on sinon and webextensions-api-mock to have the whole browser WebExtension API available as sinon stubs. You can pass in your own stubbed version of the browser.

Installation

npm install --save-dev webextensions-api-fake sinon

Important: 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-fake.

Usage

importbrowserFakefrom'webextensions-api-fake';// or// const { default: browserFake } = require('webextensions-api-fake');constbrowser=browserFake();

browser is now a webextensions-api-mock with faked api.

API Fake

Currently supported API fake implementations based on Firefox57+:

  • alarms

    • create
    • get
    • getAll
    • clear
    • clearAll
  • contextualIdentities

    • create
      • triggers: onCreated
    • get
    • remove
      • triggers: onRemoved
    • query
    • update
      • triggers: onUpdated
  • cookies

    • get
    • getAll
    • getAllCookieStores
    • remove
    • set
  • i18n

    • getAcceptLanguages
      • Returns ['en-US'] by default, can be overwritten by _setAcceptLanguages
    • getMessage
      • Returns results based on the locales and default_locale passed as options
    • getUILanguage
      • Returns en-US by default, can be overwritten by _setUILanguage
    • detectLanguage
      • Returns a Promise that resolves to the result of getUILanguage
  • tabs

    • create
      • You can pass in any parameter you want to overwrite
      • triggers: onCreated. If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • update
      • triggers: If url is given that doesn't start with about: or moz-ext:: webRequest.onBeforeRequest, webRequest.onCompleted, onUpdated
    • get
    • query
    • remove
      • triggers: onRemoved
  • storage

    • local
      • get
      • remove
      • set
      • clean
    • sync
      • get
      • remove
      • set
      • clean
    • managed
      • get
      • remove
      • set
      • clean

Faked API methods are also directly available with underscore prefix. E.g. browser.tabs._create exposes the browser.tabs.create fake. This can be useful to trigger fake behavior from tests without polluting its sinon call history.

Special Fake Methods

  • i18n

    • _setAcceptLanguages
      • Overwrite the default for getAcceptLanguages
    • _setUILanguage
      • Overwrite the default for getUILanguage
  • tabs

    • _create - helper method, same as create, but takes a special fake object that you can pass as second parameter with the following properties
      • options<object>, optional
        • webRequest<object>, optional, lets you overwrite the object properties for the request that triggers webRequest.onBeforeRequest, e.g. requestId
        • webRequestRedirects<array>, optional, triggers webRequest.onBeforeRequest again with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
        • webRequestDontYield<array>, optional, given listeners are not triggered, e.g. onCompleted
        • webRequestError<boolean>, optional, if true is given onErrorOccurred will be triggered instead of onCompleted
        • instantRedirects<boolean>, optional, if true is given redirects will not await the onBeforeRequest promise
      • responses<object>, optional, will get filled with the following structure if given
        • webRequest<object>, contains results of the call (yield) from onBeforeRequest and onCompleted as properties. Also contains the request property which is the object passed into the onBeforeRequest call.
        • tabs<object>, contains results of the call (yield) from onCreated and onUpdated as properties
        • promises<array>, contains return values of all calls, useful to await Promise.all
    • _navigate - helper method to trigger onBeforeRequest
      • tabId<integer>, required, id of the tab
      • url<string>, required, url to navigate to, will mutate the tabs url
      • webRequest<object>, optional, lets you overwrite request parameters
    • _redirect - helper method to trigger onBeforeRequest for a tab with already used request, imitating a redirect. Will automatically use the last request seen for this tab if not overwritten by webRequest. Will mutate the stored tabs url to the last url in the array. Takes the parameters:
      • tabId<integer>, required, id of the tab
      • redirects<array>, required, triggers webRequest.onBeforeRequest with the given URLs in the array in the order they are listed. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
      • webRequest<object>, optional, lets you overwrite request parameters
    • _registerRedirects - helper method to register triggering onBeforeRequest for the given redirect urls if the registered url is seen in a tabs.create or tabs.update. Will mutate the tabs url to the last redirect url. Has higher precedence than webRequestRedirects
      • targetUrl<string>, required, the target url
      • redirectUrls<array>, required, the urls for which follow-up onBeforeRequest calls are made. Instead of an URL string its possible to pass an object with properties url (the url to redirect) and webRequest (overwrite request parameters)
    • _unregisterRedirects - helper method to remove registered redirects for the given target url
      • targetUrl<string>, required, the target url
    • _lastRequestId - helper method to return the last used requestId

NodeJS Example

Given the following production code for your WebExtension:

example.js

browser.tabs.onCreated.addListener(asynctab=>{awaitbrowser.storage.local.set({lastCreatedTab: tab,});});constfirstWeDoThis=async()=>{constcontainer=awaitbrowser.contextualIdentities.create({name: 'My Container',color: 'blue',icon: 'fingerprint',});awaitbrowser.storage.local.set({lastCreatedContainer: container.cookieStoreId,});};constthenWeDoThat=async()=>{const{ lastCreatedContainer }=awaitbrowser.storage.local.get('lastCreatedContainer');awaitbrowser.tabs.create({cookieStoreId: lastCreatedContainer,});};constmyFancyFeature=async()=>{awaitfirstWeDoThis();awaitthenWeDoThat();};myFancyFeature();

You could have a test that looks like this (using mocha, sinon-chai, chai.should and require-reload in this case):

example.test.js

const{default: browserFake}=require('webextensions-api-fake');constreload=require('require-reload')(require);constsinon=require('sinon');constsinonChai=require('sinon-chai');constchai=require('chai');chai.should();chai.use(sinonChai);describe('Useful WebExtension',()=>{beforeEach(async()=>{// fake the browserglobal.browser=browserFake();// execute the production codereload('./example.js');// wait a tick to give the production code the chance to executereturnnewPromise(resolve=>process.nextTick(resolve));// instead of doing a require and then waiting for the next tick// it would also be possible to set e.g. `global._testEnv = true;` in the test// and in the production code something like// if (!_testEnv) {// myFancyFeature();// } else {// module.exports = myFancyFeature;// }//// that would make it possible to get the actual function when doing require});describe('My Fancy Feature which is executed on load',()=>{it('should work',async()=>{browser.tabs.create.should.have.been.calledWithMatch({cookieStoreId: sinon.match.string,});consttabs=awaitbrowser.tabs.query({});tabs.length.should.equal(1);});});describe('Triggering listeners after loading the production code',()=>{it('should work as well',async()=>{constcreatedTab=awaitbrowser.tabs.create({});const{ lastCreatedTab }=awaitbrowser.storage.local.get('lastCreatedTab');lastCreatedTab.id.should.equal(createdTab.id);});});});

You can find the example in the examples directory and also execute it:

npm install
npm run example

JSDOM

If you want to execute your WebExtensions tests using JSDOM, then webextensions-jsdom might be for you.

API

Exported default function([options])

  • options<object>, optional
    • browser<object>, optional, stubbed version of the WebExtensions API. Defaults to webextensions-api-mock if not given
    • locales<object>, optional, used for the i18n.getMessage fake. Format is {locale: messages}. E.g.: {'en': {'translated': {'message': 'hello world'}}}
    • default_locale<string>, optional, used for the i18n.getMessage fake

Returns a new stubbed browser with newly created and applied fakes.

Exported Class: WebExtensionsApiFake

Constructor: new WebExtensionsApiFake
webExtensionsApiFake.createBrowser([options])

Returns a new stubbed browser without applied fakes.

webExtensionsApiFake.fakeApi(browser)
  • browser<object>, required, Stubbed version of the WebExtensions API

Applies the API fakes to the given browser object. Can be called multiple times with different browser stubs and applies the same fakes (with the same in-memory data) in that case.

About

In-memory WebExtensions API Fake Implementation for testing purposes

Resources

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages