acvetkov / sinon-chrome

Testing chrome extensions with Node.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using Sinon-Chrome

mabumusa1 opened this issue · comments

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.

Hi, @mabumusa1 !
Can you show app/scripts.babel/background script?

Do you run tests under browser?

Thanks @acvetkov, here is the code I am working on

https://github.com/mabumusa1/chrome-ext/blob/testing/app/scripts.babel/background.js

I run tests using Karma, under phantomJS

To give you a quick overview of this code, we mount the file system, allow OneSingal notifications, then prepare the events page to listen to events.

Here is a list of how the test script should work.

1- Browserify, compile test/test.spec.js which in turn require('../app/scripts.babel/background.js')
2- Before the test starts include three dependencies

3- background.js initiate OneSignal, mount the file system, and get ready to receive calls.

Below an object of the required apis we use.

{
  app:{
    runtime:{
      onLaunched: {
        addListener: 
      },
    }
  },
  runtime:{
    id: ,
    onConnect: {
      addListener: 
    },
    connect: function(config){
        return{
            onMessage: {
                addListener: 
              }
          }
    }
  },
  windows:{
    onCreated: {
      addListener: 
    },
    onRemoved: {
      addListener: 
    },
    onFocusChanged: {
      addListener: 
    },
  },
  tabs: {
    onCreated: {
      addListener: 
    },
  },
  storage: {
    local: {
      get: ,
      set: 
    }
  },
  gcm:{
    onMessage: {
      addListener: 
    },
    register: 
  },
  notifications:{
    onClicked: {
      addListener: 
    },
    onButtonClicked: {
      addListener: 
    },
  }
}
port:{
  onMessage: {
    addListener: 
  }
} 

Did you patch window object with sinon-chrome?

import chrome from 'sinon-chrome';

window.chrome = chrome

I just did, before I used to do global.chrome = chrome. now I did as you suggested window.chrome=chrome

Nothing changed, the same error still occur.

What I find strange, that the objects are empty when I do console.log(window.chrome). here is what I get.

LOG LOG: Object{registerPlugin: function registerPlugin(plugin) { ... }, alarms: Object{}, bookmarks: Object{}, browserAction: Object{}, browsingData: Object{}, commands: Object{}, contentSettings: Object{}, contextMenus: Object{}, cookies: Object{}, debugger: Object{}, declarativeContent: Object{}, desktopCapture: Object{}, devtools: Object{inspectedWindow: Object{}, network: Object{}, panels: Object{}}, dial: Object{}, downloads: Object{}, events: Object{}, extension: Object{}, extensionTypes: Object{}, fontSettings: Object{}, gcm: Object{}, history: Object{}, i18n: Object{}, identity: Object{}, idle: Object{}, instanceID: Object{}, management: Object{}, notifications: Object{}, omnibox: Object{}, pageAction: Object{}, pageCapture: Object{}, permissions: Object{}, power: Object{}, printerProvider: Object{}, privacy: Object{network: Object{}, services: Object{}, websites: Object{}}, proxy: Object{}, runtime: Object{}, sessions: Object{}, storage: Object{}, system: Object{cpu: Object{}, memory: Object{}, storage: Object{}}, tabCapture: Object{}, tabs: Object{}, topSites: Object{}, tts: Object{}, ttsEngine: Object{}, types: Object{}, webNavigation: Object{}, webRequest: Object{}, webstore: Object{}, windows: Object{}, accessibilityFeatures: Object{}, certificateProvider: Object{}, documentScan: Object{}, enterprise: Object{deviceAttributes: Object{}, platformKeys: Object{}}, fileBrowserHandler: Object{}, fileSystemProvider: Object{}, input: Object{ime: Object{}}, networking: Object{config: Object{}}, platformKeys: Object{}, vpnProvider: Object{}, wallpaper: Object{}, __stub__: StubsCache{stubs: Object{}}, __events__: EventsCache{events: Object{}, sandbox: Object{}}, __props__: PropsCache{props: Object{bookmarks.MAX_WRITE_OPERATIONS_PER_HOUR: ..., bookmarks.MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE: ..., contextMenus.ACTION_MENU_TOP_LEVEL_LIMIT: ..., devtools.inspectedWindow.tabId: ..., extension.lastError: ..., extension.inIncognitoContext: ..., gcm.MAX_MESSAGE_SIZE: ..., runtime.lastError: ..., runtime.id: ..., sessions.MAX_SESSION_RESULTS: ..., tabs.TAB_ID_NONE: ..., webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES: ..., windows.WINDOW_ID_NONE: ..., windows.WINDOW_ID_CURRENT: ...}}, reset: function () { ... }, flush: function () { ... }, plugins: Object{CookiePlugin: function ChromeCookies() { ... }, I18nPlugin: function ChromeI18n() { ... }}}

And the error is still the same

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/fa2ba9d872fcf8dc56feec80f16c1f52.browserify:28:15


Can you try include sinon-chrome/bunlde/sinon-chrome.min.js instead of

import chrome from 'sinon-chrome';

window.chrome = chrome

You should include this script in same context as your tests.

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

karma-sinon-chrome does not use the latest sinon-chrome version.
latest sinon-chrome - 2.2.1
latest karma-sinon-chrome depends on 1.1.2

I noticed that so I changed the karma-sinon-chrome to use the latest version 2.2.1 this the change I made in karma-sinon-chrome

var framework = function (files) {
  //, '../sinon-chrome/bundle/sinon-chrome.min.js'
  var sinonChromePath = require.resolve('../sinon-chrome/bundle/sinon-chrome.min.js')
  console.log(sinonChromePath)
  files.unshift(pattern(sinonChromePath))
}

I explicitly referenced the latest version

What I find strange, that the objects are empty when I do

It's valid behavior, because methods defined via getters.
https://github.com/acvetkov/sinon-chrome/blob/master/src/api/index.js#L79

undefined is not an object (evaluating 'chrome.app.runtime')

I find out reason of your problem. You test chrome app, not chrome extension.
You should include chrome app stub.
https://github.com/acvetkov/sinon-chrome#usage

For mock apps Api

const chrome = require('sinon-chrome/apps'); // stable apps api
window.chrome = chrome; // for browser context
global.chrome = chrome; // for nodeJS context

I tested with both sinon-chrome/apps and sinon-chrome/extensions, it worked with app on 'chrome.app.runtime' but it when the excution completes I got a another error chrome.windows.onCreated.

I have two questions
1- Do I have to create spys for each api call? or what is the best practice, since objects are returned empty.

2- for chrome.app.runtime, it is used inside a vendor libaray OneSignal which has the following statement

    if (chrome.app.runtime)
      chrome.app.runtime.onLaunched.addListener(OneSignal.appLaunched);

The code above works well inside chrome and executes as expected, I am a bit confuced why it works well inside chrome but not in tests.

Sorry for taking your time, but I've talked to seven different developers who did not manage to make it work for us. Maybe we can solve this issue together and understand how to do unit tests for chrome.

Thanks

Are you developing chrome app?
Why are you using chrome.windows, which should be used for chrome extensions?

it worked with app on 'chrome.app.runtime' but it when the excution completes I got a another error chrome.windows.onCreated.

You should use chrome.app.window.
Chrome apps api does not support chrome.windows.* namespace.

1- Do I have to create spys for each api call? or what is the best practice, since objects are returned empty.

No. sinon-chrome provides it by default.
All chrome api methods is sinon stubs, you should define behavior for it.

chrome.cookie.getAll.yields([3, 4]);
chrome.cookie.getAll({}, list => console.log(list)); // [3, 4]
chrome.cookie.getAll.flush();