Code Monkey home page Code Monkey logo

sinon-chrome's Introduction

Build Status npm version

Sinon-chrome

Sinon-chrome is helper tool for unit-testing chromium and Firefox extensions and apps. It mocks all extensions api with sinon stubs that allows you to run tests in Node.js without actual browser.

Schema support

API mocks are generated using official chromium extensions API (Firefox webextensions) schemas that ensures consistency with real API. Actual schemas are taken from Chrome 53 and Firefox 49.

How it works

Sinon-chrome mocks all chrome api, replaced methods by sinon stubs with some sugar. Chrome events replaced by classes with same behavior, so you can test your event handlers with manual triggering chrome events. All properties has values from chrome schema files.

Install

We recommend use sinon-chrome on Node.js platform.

npm install sinon-chrome --save-dev

But, if you want...

You can download sinon-chrome bundle from release page and include it on your page

<script src="/path/to/sinon-chrome.min.js">

or

<script src="/path/to/sinon-chrome-apps.min.js">

Usage

For mock extensions Api

const chrome = require('sinon-chrome');

// or

const chrome = require('sinon-chrome/extensions');

For mock apps Api

const chrome = require('sinon-chrome/apps'); // stable apps api

Examples

Let's write small navigation helper, which use chrome api methods.

export const navigationTarget = {
    NEW_WINDOW: 'new-window',
    NEW_TAB: 'new-tab',
    CURRENT_TAB: 'current-tab',
};

/**
 * Navigate user
 * @param {String} url
 * @param {String} [target]
 * @returns {*}
 */
export function navigate(url, target = navigationTarget.NEW_TAB) {
    switch (target) {
        case navigationTarget.NEW_WINDOW:
            return chrome.windows.create({url: url, focused: true, type: 'normal'});
        case navigationTarget.CURRENT_TAB:
            return chrome.tabs.update({url: url, active: true});
        default:
            return chrome.tabs.create({url: url, active: true});
    }
}

Test it

import chrome from '../src'; // from 'sinon-chrome'
import {assert} from 'chai';
import {navigate, navigationTarget} from './navigate';

describe('navigate.js', function () {

    const url = 'http://my-domain.com';

    before(function () {
        global.chrome = chrome;
    });

    it('should navigate to new window', function () {
        assert.ok(chrome.windows.create.notCalled, 'windows.create should not be called');
        navigate(url, navigationTarget.NEW_WINDOW);
        assert.ok(chrome.windows.create.calledOnce, 'windows.create should be called');
        assert.ok(
            chrome.windows.create.withArgs({url, focused: true, type: 'normal'}).calledOnce,
            'windows.create should be called with specified args'
        );
    });
});

You can run this example by command

npm run test-navigate

More tests in examples dir.

stubs api

With original sinon stubs api we add flush method, which reset stub behavior. Sinon stub has same method resetBehavior, but it has some issues.

Example

chrome.cookie.getAll.withArgs({name: 'my_cookie'}).yields([1, 2]);
chrome.cookie.getAll.withArgs({}).yields([3, 4]);

chrome.cookie.getAll({}, list => console.log(list)); // [3, 4]
chrome.cookie.getAll({name: 'my_cookie'}, list => console.log(list)); // [1, 2]
chrome.cookie.getAll.flush();
chrome.cookie.getAll({name: 'my_cookie'}, list => console.log(list)); // not called
chrome.cookie.getAll({}, list => console.log(list)); // not called

events

Let's write module, which depends on chrome events

export default class EventsModule {
    constructor() {
        this.observe();
    }

    observe() {
        chrome.tabs.onUpdated.addListener(tab => this.handleEvent(tab));
    }

    handleEvent(tab) {
        chrome.runtime.sendMessage(tab.url);
    }
}

And test it

import chrome from '../src'; // from 'sinon-chrome'
import {assert} from 'chai';
import EventsModule from './events';

describe('events.js', function () {

    before(function () {
        global.chrome = chrome;
        this.events = new EventsModule();
    });

    beforeEach(function () {
        chrome.runtime.sendMessage.flush();
    });

    it('should subscribe on chrome.tabs.onUpdated', function () {
        assert.ok(chrome.tabs.onUpdated.addListener.calledOnce);
    });

    it('should send correct url on tabs updated event', function () {
        assert.ok(chrome.runtime.sendMessage.notCalled);
        chrome.tabs.onUpdated.dispatch({url: 'my-url'});
        assert.ok(chrome.runtime.sendMessage.calledOnce);
        assert.ok(chrome.runtime.sendMessage.withArgs('my-url').calledOnce);
    });

    after(function () {
        chrome.flush();
        delete global.chrome;
    });
});

You can run this test via

npm run test-events

properties

You can set property values. chrome.flush reset properties to default values (null or specified by schema). Let's create module, which wraps chrome api with Promise. If chrome.runtime.lastError is set, promise will be rejected.

export const api = {
    tabs: {
        /**
         * Wrapper for chrome.tabs.query
         * @param {Object} criteria
         * @returns {Promise}
         */
        query(criteria) {
            return new Promise((resolve, reject) => {
                chrome.tabs.query(criteria, tabs => {
                    if (chrome.runtime.lastError) {
                        reject(chrome.runtime.lastError);
                    } else {
                        resolve(tabs);
                    }
                });
            });
        }
    }
};

And our tests

import chrome from '../src'; // from 'sinon-chrome'
import chai from 'chai';
import {api} from './then-chrome';
import chaiAsPromised from 'chai-as-promised';

chai.use(chaiAsPromised);
const assert = chai.assert;

describe('then-chrome.js', function () {

    before(function () {
        global.chrome = chrome;
    });

    beforeEach(function () {
        chrome.flush();
    });

    it('should reject promise', function () {
        chrome.tabs.query.yields([1, 2]);
        chrome.runtime.lastError = {message: 'Error'};
        return assert.isRejected(api.tabs.query({}));
    });

    it('should resolve promise', function () {
        chrome.runtime.lastError = null;
        chrome.tabs.query.yields([1, 2]);
        return assert.eventually.deepEqual(api.tabs.query({}), [1, 2]);
    });

    after(function () {
        chrome.flush();
        delete global.chrome;
    });
});

You can run this test via

npm run test-then

Plugins

Sinon chrome module supports plugins, that emulates browser behavior. More info on example page.

const chrome = require('sinon-chrome/extensions');
const CookiePlugin = require('sinon-chrome/plugins').CookiePlugin;

chrome.registerPlugin(new CookiePlugin());

Extension namespaces

Apps namespaces

Webextensions API

Any questions?

Feel free to open issue.

Useful resources

Awesome Browser Extensions And Apps - a curated list of awesome resources for building browser extensions and apps.

sinon-chrome's People

Contributors

acvetkov avatar aleksandr-oleynikov avatar crimx avatar dependabot[bot] avatar dinomite avatar freaktechnik avatar masgari avatar matcarlson avatar pimterry avatar sokratisvidros avatar standard8 avatar stoically avatar sxderp avatar tfoxy avatar thgaskell avatar vitalets 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sinon-chrome's Issues

Please consider supporting Firefox's WebExtensions

Firefox is currently implementing web extensions - these are pretty much the same as the chrome.*, although there are a few differences.

Firefox has created the browser.* namespace which is virtually the same as the chrome.* space. There are two differences:

  • browser.* will gain Firefox specific extensions.
  • browser.* uses promises rather the callbacks.

More information:

License?

First, thanks so much for making this! I was in the process of mocking all the chrome.* bits that I needed, but it looks like you've already done that! Much thanks. 😸

Second, I wondered if you could add a LICENSE file and/or license declaration in the package.json file. Without that--at least in the US--this library is essentially All Rights Reserved.

MIT, BSD, or Apache are pretty fabulous for this sort of project. sinon itself is BSD-3-clause. karma-sinon-chrome (which wraps this library) is MIT. It'd be super helpful to have one selected for this library also (see "none license" https://www.npmjs.com/package/sinon-chrome)

It looks like you've used the MIT before, so I'll assume (for now) that's where this is headed. 😉 But a license statement would help for sure. 😄

Thanks! 🎩

Add support for chrome.runtime.lastError

Currently there's no support for chrome.runtime.lastError.

After importing sinon-chrome what I'm doing is the following:

chrome.runtime.lastErrorStub = sinon.stub();
Object.defineProperty(
  chrome.runtime, 'lastError',
  {
    get: chrome.runtime.lastErrorStub,
    set: function(value) {
      // TODO: maybe make the stub return this value
    }
  }
);

var originalChromeReset = chrome.reset;
chrome.reset = function() {
  originalChromeReset();
  chrome.runtime.lastErrorStub.reset();
  chrome.runtime.lastErrorStub.resetBehavior();
}

Feel free to grab that code.

Promises?

Hi!

I'm going to rewrite a library I wrote using sinon-chrome. It will make the tests so much better! However I was also hoping to use your chrome promise library 'then-chrome' - Would that be possible?

how to handle the global variable in typescript?

if I use

global.chrome = chrome

typescript gives me error error TS2304: Cannot find name 'global'.

but if I declare global like this

let global = {};
global.chrome = chrome

My tests will fail, because chrome is not defined on the separate file that I am writing tests for.

Link to built sinon-chrome.js in README is invalid

The link to the pre-built sinon-chrome.js in the README is no longer valid.

Would it be possible to include a final sinon-chrome.js file in each published npm module? Right now I'm finding it tricky to have consistency with sinon-chrome.js in both my Node.js and and PhantomJS tests. It would be very convenient to use the same npm install for both environments without having to run webpack on my own.

How to mock test user authentication (hello.js and google+)

Hi,

I am working on a chrome extension that requires user to sign in and using hello.js to handle the authentication with different oauth services. So far I am only using google+ authentication because this is a chrome extension and user should have a google account.

I want to 'mock' the google+ authentication service using sinon-chrome, so the extension does not have to communicate with google+ during the test. I can see two problems:

  1. The authentication communication is meant to be secure, so I am not sure how to capture the response from google+ oauth service and use it in the test;
  2. Currently I treat hello.js as a black box, and would like to keep it that way for the testing: ideally no change of the code is needed for anything inside hello.js.

Thanks,

Using Sinon-Chrome

I am writing this issue out of desperation, I talked to seven developers to help with my goal testing a screen recording I am writing and none manged to help me with this. so here is the case.

I am building a screen recording and I am using Karma, Mocha, Sinon, and Sinon-Chrome to test it, I use Chrome APIs heavily desktopShare, Storage, gcm. I followed the instructions of installation but I still get the following error:

 Array
    ✗ should return -1 when the value is not present
	undefined is not an object (evaluating 'chrome.app.runtime')
	init@app/vendor/OneSignal.js:325:19
	GT_internal_init@app/vendor/OneSignal.js:130:31
	init@app/vendor/OneSignal.js:144:31
	/tmp/app/scripts.babel/background.js:11:15 <- /tmp/d6dd7cd3b93245575010c03528a48fe4.browserify:28:15

Here is my test code

import sinon from 'sinon'
import chrome from 'sinon-chrome'

describe('Array', function() {
    it('should return -1 when the value is not present', function() {
      var background = require('../app/scripts.babel/background')
      expect(background).to.exist
    });
});

I made sure the karma-sinon-chrome uses the latest version of sinon-chrome, but I still get this error.

One strange thing I noticed that all the objects I get are empty objects inside chrome namespace. I am so confused and I would really appreciate the help.

How to set permisssionLevel for notifications?

Hi! How to set permisssionLevel for notifications?
I need to test this method:

export const notifyMessage = (payload): void => {
    const { hashId, date, messageId } = payload;

    chrome.notifications.getPermissionLevel((permissionLevel) => {
        console.log('permissionLevel: ', permissionLevel);
        if (permissionLevel === 'granted') {
            chrome.notifications.create(`${hashId}`, options, (id) => {
                console.log('notificationId: ', id);
            });
        }
    });
};

and test:

describe('Notifications tests', () => {
    beforeAll(() => {
        global.chrome = chrome;
    });

    it('notifyMessage', () => {
        notifyMessage(mockEmail);

        expect(chrome.notifications.getPermissionLevel.calledOnce).toBeTruthy();
    });

    afterAll(() => {
        delete global.chrome;
    });
});

I don`t understand why getPermissionLevel return 'denied'(((
Plss help!

How to remove the registred plugin ?

I'm writing some unit test for a chrome-extension and I was testing the function that uses chrome.i18n.getMessage(). Each test passes smoothly at first but I have to skip the second test on the specific function.
image.

But after testing my function by removing the skip at line 57, that happens :

image

According to the Wiki about Cookie plugin, I assume the registered plugin must be removed after the test in order to restore the sinon stubs.
I tried the chrome.flush() and delete chrome, both didn't work.

** ❓ Does somebody know to do this ❓ **

Usage example: second test fails

I forked the project and tried to run the usage example but the second test is failed at assert.calledOnce(chrome.browserAction.setBadgeText);. The callback in chrome.tabs.query(params, callback); isn't invoked.

Here is my fork: https://github.com/phongvis/sinon-chrome with the following changes:

  • package.json: add test-background option for npm-run and install jsdom
  • the extension files are placed at examples/src
  • the test file is examples/background.test.js also in examples

Could you have a check? Thanks.

Stub not working with sinon.match

I'm having trouble making sinon-chrome stubs work with sinon.match.

I use webpack, karma, mocha and sinon-chai. The spec below may reproduce the error.

It looks like sinon-chrome's stub doesn't recognize sinon.match and treats it like a normal object. The error is "AssertionError: expected stub to have been called with arguments hasOwn("key")%D", while it should be "arguments matching hasOwn("key")%D".

Any idea what went wrong?

describe('Sinon Native Stub', function () {
  it('calledWithMatch + match', function () {
    const stub = sinon.stub()
    stub({key: 'value'})
    stub.should.be.calledWithMatch(sinon.match.hasOwn('key'))
  })
  it('calledWith + match', function () {
    const stub = sinon.stub()
    stub({key: 'value'})
    stub.should.be.calledWith(sinon.match.hasOwn('key'))
  })
  it('withArgs + match', function () {
    const stub = sinon.stub()
    stub({key: 'value'})
    stub.withArgs(sinon.match.hasOwn('key')).should.be.calledOnce
  })
})

describe('Sinon Chrome Stub', function () {
  it('calledWithMatch + match', function () {
    chrome.storage.local.set({key: 'value'})
    chrome.storage.local.set.should.be.calledWithMatch(sinon.match.hasOwn('key'))
  })
  it('calledWith + match', function () {
    chrome.storage.local.set({key: 'value'})
    chrome.storage.local.set.should.be.calledWith(sinon.match.hasOwn('key'))
  })
  it('withArgs + match', function () {
    chrome.storage.sync.set({key: 'value'})
    chrome.storage.sync.set.withArgs(sinon.match.hasOwn('key')).should.be.calledOnce
  })
})

Results:

  Sinon Native Stub
    √calledWithMatch + match
    √calledWith + match
    √withArgs + match

  Sinon Chrome Stub
    √calledWithMatch + match
    ×calledWith + match
        AssertionError: expected stub to have been called with arguments hasOwn("key")%D
    ×withArgs + match
        AssertionError: expected stub to have been called exactly once, but it was called 0 times

Chrome Application runtime

Hello. I'm using your awesome module to mock chrome application object in my tests and I've got an issue. Method chrome.runtime.getManifest() returns undefined, but there is something like this in my code: chrome.runtime.getManifest().version. I've tried to redefine getManifest method but It's not possible because of the descriptor of this property is:

Object.getOwnPropertyDescriptor(chrome.runtime, 'getManifest')
Object {set: undefined, enumerable: false, configurable: false, get: function}
configurable:false
enumerable:false
get:function get()
set:undefined
__proto__:Object

Is there a way to compel getManifest() return some objects?
Thanks in advance.

chrome.storage.local.get never executes

I'm testing with Jest 19.0.2, and when I try to use:

chrome.storage.local.get(key, val => console.log(val))

It will never actually execute the anonymous function and log anything. Is this expected behavior?

Phantom JS 2.0.0 seems to ignore AssertionError

Hi,
I've been using sinon-chrome 0.2.1 with PhantomJS 2.0.0 on node v0.12.6 and I got stuck on an issue where PhantomJS silently ignored chai's assertion errors, i.e. tests were always successful...
Fortunately, I've been able to fix it by overriding a function evaluate in a webpage object created by phantomjs but I'm not sure if it is a right resolution.

Please review my workaround when you have time. Any suggestion is appreciated.

The gist is the workaround (deriving from beforeeach.js), adding code snip from line 8 to 43. I modified the function wrapper string at line 42. You can find the original code in phantomjs webpage.js.

Other test-use dependencies are:

  • sinon 1.10.0
  • chai 3.3.0
  • mocha 2.0.1

sinon-chrome with then-chrome

I can't get the sinon-chrome stubs to return when using promises. Any ideas??

The test itself:

    var mocha = require('mocha');
    var chai = require('chai');
    var sinon = require('sinon');
    var CookieFlip = require('../index');

    describe("Cookieflip", function() {

        beforeEach(function() {
            global.chrome = require('sinon-chrome');
        });

        afterEach(function() {
            global.chrome = {};
        });

        describe('cookieflip flow', function() {

            it('Load the active tab', function(done) {

                var dummyTabs = [];

                dummyTabs.push({url: 'testurl1'});
                dummyTabs.push({url: 'testurl2'});
                dummyTabs.push({url: 'testurl3'});

                chrome.tabs.query.withArgs({active: true, currentWindow:true}).yields(dummyTabs);

                var cookieFlip = new CookieFlip();

                cookieFlip.getCurrentTab()
                    .then(function(data) {
                        console.log('verify the new promise');
                        expect(data.url).to.equal('testurl');

                        done();
                });

                console.log('wait for the promise..');
            });
        });
    });

Code to test:

var thenChrome = require('then-chrome');

function CookieFlip() {};

CookieFlip.prototype.getCurrentTab = function() {

    return thenChrome.tabs.query({active: true, currentWindow: true})
        .then(function(tabs){
            return tabs[0];
    });
};

module.exports = CookieFlip;

v2.2.2 is broken

It seems like the Github project is published to NPM. require('sinon-chrome/extensions') and require('sinon-chrome/webextensions') break on v2.2.2.

And #69 is not correctly built.

Spy callbacks

Couldn't find anything related in the examples, but how can I overcome this situation?

chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
    // [..]
    chrome.runtime.sendMessage({
           // [...]
    });
});

I want to spy chrome.tabs.query callback.

Sinon-chrome with Jasmine?

I am fairly new to (Chrome extension) testing and started with Jasmine (https://jasmine.github.io/). Then I realised that I need to 'stub' out many Chrome API functions for testing and found sinon-chrome, which is great. Is it possible to use sinon-chrome together with jasmine (and how?), or you have to choose one between sinon and jasmine?

Remove phantomjs dependency

Hello. First of all, thank you for a great util for testing chrome API related code.

But I don't use phantom in my tests. That's why I ask you to remove PhandomJS peer dependency from your package.json - I don't want to store it.

chrome.storage.local.get === chrome.storage.sync.get

const chrome = require( 'sinon-chrome' );
const chai = require( 'chai' );
chai.use( require( 'sinon-chai' ) );
const expect = chai.expect;

expect( chrome.storage.sync.get ).not.to.equal( chrome.storage.local.get ); // fails, these are equal

expect( chrome.storage.sync.get ).not.to.have.been.called;
expect( chrome.storage.local.get ).not.to.have.been.called;
chrome.storage.sync.get( 'test' );
expect( chrome.storage.sync.get ).to.have.been.called;
expect( chrome.storage.local.get ).not.to.have.been.called; // fails

I found this after hours of trying to figure out why my test was failing where I was testing that my extension loads from sync storage and not from local storage.

Append reset and resetBehavior methods to namespace scope

It will be very conveniently If I can reset/resetBehavior all stubs in namespace scope.

For example

chrome.windows.reset(); 

is equals to

chrome.windows.get.reset();
chrome.windows.getCurrent.reset();
chrome.windows.getLastFocused.reset();
chrome.windows.getAll.reset();
chrome.windows.create.reset();
chrome.windows.update.reset();
chrome.windows.remove.reset();

and

chrome.windows.resetBehavior();

is equals to

chrome.windows.get.resetBehavior();
chrome.windows.getCurrent.resetBehavior();
chrome.windows.getLastFocused.resetBehavior();
chrome.windows.getAll.resetBehavior();
chrome.windows.create.resetBehavior();
chrome.windows.update.resetBehavior();
chrome.windows.remove.resetBehavior();

Examples not clean

Hi,
I am unable to understand run the examples in wiki page at it uses jsdom.env function, which is not supported by jsdom any more. Also, the wiki page example looks different from usage example, in readme.
Also how to test a plugin developed using https://github.com/HaNdTriX/webextension-toolbox ?
Can you please provide some suggestion ?

Question: How to send response messages, if my code sends messages

Hey,

I'm trying to test my extension's popup code. The popup has a init function which catches data from the background page. In order to do that the popup sends a request to the background page via chrome.runtime.sendMessage( ... ).

Now I tried different approaches how to 'fake' the response from the background page, currently I'm trying this approach.

But the the listener doesn't get called, once the Popup sends the request...
What am I doing wrong?

How to test event listeners on the port object?

Hey,
thanks a lot for this repo. It really helps.
One question: Is there actually a way to test event listeners on the port object. How would one test the following code?

Chrome.runtime.onConnect.addListener(function(port) {
  port.onDisconnect(function() { console.log('Port disconnected'); });
});

Cheers.
Clemens

How to override stubbed methods?

Hi, thank you for awesome module!

I have been trying to override a stubbed method provided by sinon-chrome with my implementation to simulate chrome.runtime.lastError set inside the method.

Is there any officially-recommended way to override stubbed methods?


What I have tried

I'll write down here what I have tried for overriding methods just FYI.

NG: Assigning a function

First, I tried simply overwrite the method with a function by assignment as mentioned in README, but it seemed not working at all.

chrome.runtime.id = 'my_test_id';
chrome.runtime.getURL = function (path) {
   return 'chrome-extension://' + chrome.runtime.id + '/' + path;
};

// FAIL (getURL() returns undefined)
assert.equal(chrome.runtime.getURL("test"), "chrome-extension://my_test_id/test");

This is because stubbed methods are defined by Object.defineProperty with writable: false.

NG: sinon.stub()

Second, I tried sinon.stub() to stub a stubbed method, which sounds complicated. :P

chrome.runtime.id = 'my_test_id';
sinon.stub(chrome.runtime, "getURL", function (path) {
   return 'chrome-extension://' + chrome.runtime.id + '/' + path;
});

// Error
assert.equal(chrome.runtime.getURL("test"), "chrome-extension://my_test_id/test");

And sinon output error:

TypeError: Attempted to wrap undefined property getURL as function

OK but weird: Replace entire namespace object

Third, I tried replacing entire chrome.runtime namespace object with another one.

chrome.runtime = {
  id: "my_test_id",
  getURL: function (path) {
     return 'chrome-extension://' + chrome.runtime.id + '/' + path;
  }
};

// PASS
assert.equal(chrome.runtime.getURL("test"), "chrome-extension://my_test_id/test");

Although this one could mange to override a namespace including target method, it looks weird and has too much side-effect which is not related to the test.

I don't think this one is ideal, and there should be another way to override stubbed methods easily IMHO.

chrome.js is missing?

It seems chrome.js not in sinon-chrome folder, where I can find this file?

page.injectJs(node_modules + 'sinon-chrome/chrome.js');

I need it to test chrome extension. Thanks for any help~

How to test DOM update without displaying the page?

Hi,

I am using Sinon-Chrome to test a Chrome extension. One of the functions is that user can highlight text on a web page. This is how I think I can test this with Sinon-Chrome:

  1. Send the extension an text selection event (using Sinon-Chrome);
  2. The extension updates the DOM to change the text background;
  3. Check if the background of the selected text is changed, such as marked with <mark>.

I think I can do step 1, but don't know how to do step 2 or 3 if I don't want to open a real URL in browser. How to give the Chrome extension a DOM to update (and then check) without open an actual website?

Ideally such a (virtual?) DOM can be loaded from a pre-saved file, so 1) it will be exactly the same every time, and 2) it makes the test synchronous (open a real URL will be asynchronous).

How to test chrome.browserAction functions?

I've read the examples and everything else but I'm still confused on how to use sinon-chrome.
The extension I'm building has a lot of communication between content and background scripts.

For example, when a user access some webpage (defined in my match rules), the extension icon must become active. Moreover, if the icon is clicked, a sidebar should open.

Currently, I'm trying to do something like this:

global.chrome = require('sinon-chrome');

describe('chrome.*', function () {
  global.chrome = {
    browserAction: {
      setIcon: {
        get: jasmine.createSpy('get'),
        set: jasmine.createSpy('set')
      }
    }
  };

  beforeEach(function () {
    spyOn(chrome.browserAction, 'setIcon');
    chrome.tabs.create({ url: 'http://www.customdomain.com' });
  });

  it('tracks chrome.tabs.onUpdated events', function () {
    expect(chrome.browserAction.setIcon).toHaveBeenCalled();
    console.log(chrome.browserAction.setIcon.callCount);
  })
});

Am I misunderstanding something?

question(s): how to trigger client connection

I am not sure exactly how to actually trigger a connection using sinon-chrome.
Here is my test:

const browser = require('sinon-chrome/webextensions');
const sinon = require('sinon');

it("multiple content scripts can connect to the same background script", () => {
    let onConnect = sinon.spy();

    browser.runtime.onConnect.addListener(onConnect);

    browser.runtime.connect(null, { name: 'one' });
    browser.runtime.connect(null, { name: 'two' });

    browser.runtime.onConnect.trigger();
    browser.runtime.onConnect.trigger();

    assert(onConnect.callCount === 2, 'on connect has been called 2 times');
});

This seems to work but I am not sure why. I was expecting that I don't need to call browser.runtime.onConnect.trigger();

What is the best way to improve documentation? I have a hard time using sinon-chrome as a new user.

npm installation problem

Version 0.1.1 from npmjs is broken. It throws the following error when it is executed:

module.js:338
    throw err;
          ^
Error: Cannot find module './chrome-event'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at /path/to/package/node_modules/sinon-chrome/chrome.js:91:23
    at Object.<anonymous> (/path/to/package/node_modules/sinon-chrome/chrome.js:865:2)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)

The package.json is different from the one here in github. The main property value is "chrome.js" instead of "src/chrome.js". This file exists. But, as the error shows, it doesn't find the chrome-event module in the root folder. The chrome-event module is inside the src folder.

Version 0.1.0 doesn't have this problem.

Cannot import sinon-chrome with webpack

When I try to require the new version 1.0.0 with webpack I get the following error.

TypeError: 'undefined' is not an object (evaluating 'modules[moduleId].call')

Example no longer works

I ran it on my machine after an npm install and got this

$ npm test

> [email protected] test /Users/Stephen/Desktop/sinon-chrome/example
> phantomjs test/run.js

TypeError: 'undefined' is not a function (evaluating 'self.epilogue.bind(self)')

  ../node_modules/mocha/mocha.js:3980 in Spec
  ../node_modules/mocha/mocha.js:1822
  ../node_modules/mocha/mocha.js:6087
  test/run.js:21

browser.runtime.connect returns undefined making testing difficults

Hello,

I'm not sure to understand everything yet but I'm currently trying to write test on my webextension using sinon-chrome and I got difficulties because it stubs browser.runtime.connect() to undefined where browser.runtime.connect() returns a runtime.Port which contains event and methods.

A workaround like this is working for a test file when I'm testing if thing arrives on the event

import sinon from 'sinon';
import ChromeEvent from 'sinon-chrome/events';


export default () => {
    return {
        onMessage: new ChromeEvent(),
        onDisconnect: new ChromeEvent(),
        postMessage: sinon.stub()
    };
};
describe("sidebar", function() {
      it("should register a listener on sidebar", function() {
        let port = Port();
        port.name = "sidebar";
        port.onMessage.addListener = sinon.stub();
        onConnectListener(port);
        sinon.assert.calledOnce(port.onMessage.addListener);
      });
    });

But on another test file I don't manage to get it to work and I don't really understand how I'm supposed to do.

Here is my method

const port = browser.runtime.connect({name: "worker"});

export function runTest(testObj, id, rootElement) {
  // Only run the test suite if there's a root element
  // (e.g. when in source view there's no root element set)
  if (rootElement) {
    let contentTest = testObj.check(rootElement);
    testObj.errors = contentTest;

    /* TODO: Follow progress on bug https://bugzilla.mozilla.org/show_bug.cgi?id=1370884 */

    port.postMessage({
      type: "processTestResult",
      test: JSON.stringify(testObj),
      id
    });
  }
}

And here is my "test"

  describe("runTest", function(){
    it("should postMessage on port", function(){
      let port = Port();
      let testObj = {
        check: (any) => true,
        errors: "There is no errors"
      }

      runTest(testObj, 0, true);
      sinon.assert.calledOnce(port.postMessage);
    });
  });

Javascript wise, I'm pretty sure there is an issue with scope but I don't really have any idea about how to do in an other way.

My experience with testing is very limited (and considering how important it is, I'm kinda sad about it), sorry if it's a very simple question.

Karma configuration

I know that there is a karma sinon-chrome wrap, but I could not find any reliable solution to what I'm looking for. I'm getting this error:

Chrome 52.0.2743 (Linux 0.0.0) ERROR
  Uncaught TypeError: Cannot read property 'create' of undefined
  at node_modules/sinon-chrome/dist/sinon-chrome.latest.js:7537

My spec file

  describe('chrome.*', function () {
    beforeEach(function () {
      console.log(chrome);
    });

    it('should open a new tab', function () {
      // chrome.tabs.create({});
      // expect(chrome.tabs.create.called).toBe(true);
    });
  });

I installed sinon-chrome and karma-sinon-chrome via npm. My karma config file:

  frameworks: [
      'jasmine',
      'requirejs',
      'sinon-chrome'
    ],

    // list of files / patterns to load in the browser
    files: [
      { pattern: 'src/**/*.js', included: false },
      { pattern: 'test/*-spec.js', included: false },
      'require.conf.js'
    ],

    // list of files to exclude
    exclude: [
      'src/vendor/**/*.js'
    ],

    // plugins
    plugins: [
      'karma-jasmine',
      'karma-chrome-launcher',
      'karma-requirejs',
      'karma-sinon-chrome'
      //'karma-coverage'
    ],

chrome.flush does not flush withArgs stubs

var chrome = require('sinon-chrome');

chrome.runtime.getUrl.returns('my-domain.com');
chrome.runtime.getUrl(); // my-domain.com

chrome.flush()
chrome.runtime.getUrl(); // undefined

But if we flush stubs, which has withArgs behavior, chrome.flush does not work.

var chrome = require('sinon-chrome');

chrome.runtime.getUrl.withArgs(1).returns('my-domain1.com');
chrome.runtime.getUrl.withArgs(2).returns('my-domain2.com');
chrome.runtime.getUrl(1); // my-domain1.com
chrome.runtime.getUrl(2); // my-domain2.com

chrome.flush()
chrome.runtime.getUrl(1); // my-domain1.com
chrome.runtime.getUrl(2); // my-domain2.com

How to run the example code?

Hi, I am new to Javascript testing. I have a chrome plugin and I want to write some unit tests to it.

I tried your "Usage example" but couldn't get the testing part to work. I don't know where to put the reference to the "sinon-chrome.latest.js". Also, how do I know if the tests are run? I am very confused on how sinon knows that it needs to run background.test.js and popup.test.js. And where can I see the test result?

Thanks

Example in Readme.md to test messaging

It would be good, if we have an example to test message passing. Most of the plugins have a postMessage and onMessage callbacks.
eg.
chromeobject.postMessage("hello")
chromeobject.onMessage.AddListener(function(data){
console.log(data)
})
A call to postMessage or message needs to trigger callback registered via onMessage.AddListener.

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.