acvetkov / sinon-chrome

Testing chrome extensions with Node.js
ISC License
439 stars 48 forks source link

Using Sinon-Chrome #56

Open mabumusa1 opened 7 years ago

mabumusa1 commented 7 years ago

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.

acvetkov commented 7 years ago

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

Do you run tests under browser?

mabumusa1 commented 7 years ago

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: 
  }
} 
acvetkov commented 7 years ago

Did you patch window object with sinon-chrome?

import chrome from 'sinon-chrome';

window.chrome = chrome
mabumusa1 commented 7 years ago

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
acvetkov commented 7 years ago

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.

acvetkov commented 7 years ago

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

mabumusa1 commented 7 years ago

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

acvetkov commented 7 years ago

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
mabumusa1 commented 7 years ago

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

acvetkov commented 7 years ago

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();
mabumusa1 commented 7 years ago

I am developing a Chrome extension, so all the API calls I am using are from the extensions namespace.

The reason of app existing is a vendor library called OneSignal which check if the environment is an app, so the test fails on that.

I will check again based on your response today and get back to you.

Hopefully it will work this time

On Fri, May 5, 2017, 2:31 PM Aleksey Tsvetkov notifications@github.com wrote:

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 http://sinonjs.org/releases/v2.1.0/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();

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/acvetkov/sinon-chrome/issues/56#issuecomment-299441957, or mute the thread https://github.com/notifications/unsubscribe-auth/AMCuyozrcYl6xxwpZeLYXEEWPRxNz8xLks5r2wisgaJpZM4NQqUu .