Giter Site home page Giter Site logo

clarkbw / jest-webextension-mock Goto Github PK

View Code? Open in Web Editor NEW
92.0 5.0 36.0 1.02 MB

A module to mock WebExtensions in Jest

License: BSD 3-Clause "New" or "Revised" License

JavaScript 100.00%
jest jest-mocking webextension chrome-extensions firefox-extension

jest-webextension-mock's Introduction

npm npm Codecov Greenkeeper badge Twitter

💪 @RickyMarou is an official maintainer. This change was made on 2024-04-12 as @clarkbw has not been able to devote sufficient time necessary for this project.

Install

For npm:

npm i --save-dev jest-webextension-mock

For yarn:

yarn add --dev jest-webextension-mock

Setup

Require module directly

In your package.json under the jest section add the setupFiles attribute with this module name.

"jest": {
  "setupFiles": [
    "jest-webextension-mock"
  ]
}

Use setup file

Alternatively you can create a new setup file and require this module.

__setups__/chrome.js

require('jest-webextension-mock');

And add that file to your setupFiles:

"jest": {
  "setupFiles": [
    "./__setups__/chrome.js"
  ]
}

Usage

Use this module to check that API calls were made when expected.

describe('your function to test', () => {
  it('should have called a webextension API', () => {
    yourFunctionToTest();
    expect(chrome.tabs.update).toHaveBeenCalled();
  });
});

Check the API was called with certain parameters.

describe('your function to test', () => {
  it('should have called a webextension API', () => {
    yourFunctionToTest();
    expect(chrome.tabs.update).toHaveBeenCalledWith({
      url: 'https://example.com/'
    });
  });
});

And you can reset the API mocks to ensure APIs are only called when needed.

beforeEach(() => {
  browser.geckoProfiler.start.mockClear();
  browser.geckoProfiler.stop.mockClear();
});

it('should toggle the profiler on from stopped', () => {
  const store = mockStore(reducer(undefined, {}));
  const expectedActions = [
    { type: 'PROFILER_START', status: 'start' },
    { type: 'PROFILER_START', status: 'done' },
  ];
  return store.dispatch(actions.toggle()).then(() => {
    expect(browser.geckoProfiler.start).toHaveBeenCalledTimes(1);
    expect(store.getActions()).toEqual(expectedActions);
  });
});

Development

npm install
npm test

Publish

Publishing new releases is automated via the GitHub Action https://github.com/mikeal/merge-release tool.

To ensure your feature is properly released prefix your commit message with feat for any new feature. For example: feat: new API and this will bump the minor release number. All other changes will be assumed as patch releases unless you include the string BREAKING CHANGE in your commit message or description which will trigger a new major release. (do not do this unless absolutely required)

jest-webextension-mock's People

Contributors

8w9ag avatar allenfang avatar betree avatar clarkbw avatar crimx avatar dependabot[bot] avatar escapedcat avatar fregante avatar greenkeeper[bot] avatar greenkeeperio-bot avatar hankchiutw avatar hranum avatar jasonandmonte avatar kocal avatar m-sureshraj avatar therynamo avatar vitonsky avatar wuz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

jest-webextension-mock's Issues

Notifications API

Hey!
First let me thanks you for this great package 😄

I created this issue to let you know that I am actually working on an implementation of notifications API.

I will make a PR soon!

How to test that chrome.runtime.sendMessage is being called?

I have a function

export const send = async msg =>
  new Promise(resolve =>
    chrome.runtime.sendMessage(msg, response => resolve(response))
  );

and corresponding test

describe('send', () => {
  const message = 'some message'

  beforeAll(async () => {
    await send(message);
  })

  it('called chrome.runtime.sendMessage with the message', () => {
    expect(chrome.runtime.sendMessage).toHaveBeenCalledWith(message, expect.any(Function));
  })
})

but I get the error

 FAIL  tests/messaging/messageManager.test.js
  ● send › called chrome.runtime.sendMessage with the message

    TypeError: listener is not a function

      14 | export const send = async msg =>
      15 |   new Promise(resolve =>
    > 16 |     chrome.runtime.sendMessage(msg, response => resolve(response))
         |                    ^
      17 |   );
      18 | 

      at node_modules/jest-webextension-mock/dist/setup.js:123:14
          at Array.forEach (<anonymous>)
      at Object.<anonymous> (node_modules/jest-webextension-mock/dist/setup.js:122:24)
      at sendMessage (src/messaging/messageManager.js:16:20)

What am I doing wrong?

Calling callbacks as a default mock implementation

This is more for discussion than anything.

There are lots of callbacks (promises in FF) in the web-extension API. And I guess it would be fair to implement all functions with them so, that the callbacks/promises would be called immediately.

Now, one have to overwrite all mocked function, where callback call is needed, with new mock with implementation with callback call weather any other than call is needed or not.

Issues

What to pass as parameters which are often needed?

Testing onMessage listeners

I'm attempting to test my onMessage listeners, but I keep getting the error TypeError: sendResponse is not a function. It appears that the implementation of runtime.sendMessage accepts a callback as the second argument, but doesn't actually pass it to the listener.

I've also tried the MDN-recommended method of returning a promise from the onMessage listener and using .then() from runtime.sendMessage, but this doesn't work either.

My workaround thus far has been to export the listener function and hit it directly. This works but is less than ideal.

Help needed for gettting started.

Hi I am using Zest + Enzyme for testing my browser plugin. My plugin consists of background.js, contentscript.js and dev tools.

  chrome.extension.onConnect.addListener(function (incoming_port) {
    let port = incoming_port;
    if (port.name === '3pm-panel') {
      port.onMessage.addListener(function (message) {
        switch (message.type) {

          case 'reloadWithDebug':
            // This is called even for iframe is loaded
            chrome.tabs.query({
              active: true,
...

I am trying to test if the onConnect.addListener is called once, reloadWithDebug is called once . I am not sure how to proceed with the testing. Can you elaborate with the below example. Thank you in Advance for your help

describe('your function to test', () => {
  it('should have called a webextension API', () => {
    yourFunctionToTest();
    expect(chrome.tabs.update).toHaveBeenCalled();
  });

Add support for runtime.connectNative

Hi, do you have any plans to add support for the connectNative method (and what comes with it)?
Thanks

Edit: same would apply for chrome.webNavigation and chrome.webRequest or maybe it's not in the scope of this package?

Missing onChanged on chrome.storage.sync

Currently onChanged is only supported in chrome.storage. Need to add it for chrome.storage.sync as well. Should suffice to add onChanged: {
addListener: jest.fn(),
removeListener: jest.fn(),
hasListener: jest.fn(),
}, in storage.js

Vitest support

I'm looking to switch to Vitest, do you think it would be possible to look for the vi global as well as jest? I'm not sure if that's enough.

Current Vitest config

import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    setupFiles: [
      "jest-webextension-mock"
    ]
  },
})

Output

 FAIL  utils.test.ts [ utils.test.ts ]
ReferenceError: jest is not defined
 ❯ Object.<anonymous> node_modules/jest-webextension-mock/dist/setup.js:5:25

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

Related

Struggling with ts-jest

Hi! This seems to be a fantastic project.

Currently I'm trying to test chrome.storage.local.get and chrome.storage.local.set, but I'm struggling to have my typscript environment, more specifically ts-jest understand that mockClear actually exists as per:

beforeEach(() => {
  chrome.storage.local.get.mockClear(); // Property 'mockClear' does not exist on type ... ...
});

I've managed to setup a similar project called jest-fetch-mock, and if I dig into their source, I can see that they have types such as:

export interface FetchMock
    extends jest.MockInstance<Promise<Response>, [string | Request | undefined, RequestInit | undefined]> {
    (input?: string | Request, init?: RequestInit): Promise<Response>;

    // Response mocking
    mockResponse(fn: MockResponseInitFunction): FetchMock;
    mockResponse(response: string, responseInit?: MockParams): FetchMock;

    ...
}

defined in their project. And I can't seem to find something similar in this project.

Could it be that my project doesn't understand that the implementations has actually created functions such as mockClear? Perhaps this project doesn't support typescript testing? If so, maybe I could declare something for the functions I'm using. I just want to make sure I'm not misunderstanding something that you've documented somewhere.

Thanks in advance!

throw new Error('Wrong key given');

I'm getting this error if the keys is undefined, but why no compare witch undefined values too?

on: jest-webextension-mock/dist/setup.js:264

Question about Storage implementation

Hi, I got a question about chrome.storage.*.

So I have a class StorageManager which is a very simple wrapper of chrome.storage.sync's get and set methods.

This class works fine in a web extension context, but it doesn't in tests context.
Each time I try to get a value, it returns an empty string (https://github.com/clarkbw/jest-webextension-mock/blob/master/src/storage.js#L4).

So my question is, is this behaviour correct for you, or you didn't have the desire and/or time to write a better implementation?
If it's about desire/time, I can take on my own time to implement a more correct version of chrome.storage.sync 👍

Thanks

Selectively expose mocks via `browsers` or `chrome`?

I try to test an extension that uses separate codepaths for chrome and firefox depending on if 'browsers' is set. Currently, tests do only test the chrome paths.

So far, I wasn't able to make it work that: var browser = browser || chrome; evaluates as browser with undefined chrome. Any ideas how I can achieve this with the jest-webextension-mock?

in generic.js during test execution, chrome global.chrome and global.browser is defined but not browser. If I set chrome to null, chromeandglobal.chromeare set. I assume that this is due to the node context with globally scoped variables inglobalbut I do not get wherechrome` is set. Any hints?

Add support for browser.windows.* APIs

First of all thanks for making this library, it seems really promising!

I was trying to use this library in my extension and followed the instructions in the README but I keep getting this error when I run certain tests:

failing-test

I think the issue is just that there are no mocks for this part of the API yet (correct me if I am wrong). Let me know if this is actually an issue or I am just misusing the library.

If it is just a case of missing support, I think we can add it. If you want I can make a PR to add these mocks, I looked over the existing source code and I think I understand how this library works.

Thanks!

Wrong data getting when use browser.storage.local.get

HI @clarkbw firstly, thanks for this awesome repo, it help me a lots 👍

However, I encounter a bug maybe, following is my source code:

browser.storage.local.get({ apiErrReport: [] }, (result) => {
   console.log(result);
   const { apiErrReport } = result;
   // ...  
});

I pass an object to get function, this is work well as usual, but when I started to use jest-webextension-mock and wrote the test, I found the callback argument reuslt become as:
There's a hardcode id always in the root object

{ id: { apiErrReport: [] } }

But I think it is suppose to be a plain object without id

{ apiErrReport: [] } }

Any idea? BTW, I saw the source code and it always call the callback with { id }, but I think:

if object given, the argument should be

const args = { ...id }
cb(args);

if string given, the argument should be

const args = {};
args[id] = ...;
cb(args);

I'm not sure, please let me know your idea. thanks

Regards,
Allen

chrome.storage.local.get should return a deep copy of objects

Presently it's possible for test behavior and real code behavior to differ because changes to objects returned by chrome.storage.local.get can be "persistently" modified in functions that do not save the result to chrome.storage.

interface Thing {
  array: string[]
}

function saveThing(thing: Thing) {
  return new Promise((resolve) => {
    chrome.storage.local.set({ thing: thing }, () => {
      resolve(true)
    })
  })
}

function getThing(): Promise<Thing> {
  return new Promise((resolve) => {
    chrome.storage.local.get('thing', (data: { thing: Thing }) => {
      resolve(data.thing)
    })
  })
}

function doSomethingWithThing(thing: Thing) {
  thing.array = thing.array.map((string) => {
    return 'bar'
  })
}

describe('Thing should work', () => {
  it('thing works', async () => {
    await saveThing({ array: ['foo'] })
    let thing = await getThing()
    expect(thing.array[0]).toEqual('foo')

    doSomethingWithThing(thing)

    thing = await getThing()
    expect(thing.array[0]).toEqual('foo')
  })
})

Expected: "foo"
Received: "bar"

chrome.runtime.onMessage inconsistent callback parameters?

Hi!

I'm working with the code located in the tabs.js module of this project as per:

sendMessage: jest.fn((tabId, message, cb) => {
    onMessageListeners.forEach((listener) => listener(tabId, message));
    if (cb !== undefined) {
      return cb();
    }
    return Promise.resolve();
  }),

From what I can see in the official docs for onMessage, the structure is as follows:

(message: any, sender: [MessageSender](https://developer.chrome.com/docs/extensions/reference/runtime/#type-MessageSender), sendResponse: function) => boolean | undefined

In my test if I run chrome.tabs.sendMessage(tabId, Message), the tabId is the first parameter of the listener, which is inconsistent with the way chrome.runtime.sendMessage calls its listener.

For more info, the listener I'm testing is

private async onMessage(
    message: Message,
    _: chrome.runtime.MessageSender,
    sendResponse: (response?: any) => void
  ) {
    console.log(message);
  }

There's also the issue with the sendResponse, which I don't believe is covered here?

Any guidance would be greatly appreciated :)

Add support for browser.webRequest API

There is no implementation at all for the browser.webRequest API.

Also since this API will be/is going through changes with MV3 (see here), it'll be awesome to be able to configure the mocking to be MV2/MV3 so we can write unit tests for these different scenarios.

Need a push to npm

With some of the recent changes, we need a push to npm. I need the chrome.runtime.getURL changes specifically.
Thanks! 😄

Update: I would also be happy to help out with this repo however I can! I can look into setting up some CI/CD integration to deploy the extension and maybe fleshing out the documentation a bit? Would that be helpful?

Add contribution guidelines

While making the changes for #129 I felt that there may be a need for this.

By the looks of the commit log, the format of the messages varies quite a lot. I'm also wondering about what the preferred merge strategy is, like would it be appreciated if one was to keep their branch up to date with master through rebasing? I'm not sure if this is something that is valued here, it might just be me that is overly picky about such things.

Other than that, clarifying the goal with this mocking library might be beneficial. I mentioned in #129 that the changes only seem to relevant for Chrome extensions. Should such cases be supported by this project, or should anything outside of the actual WebExtensions API be omitted?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.