diff --git a/.eslintrc.json b/.eslintrc.json index 8e44a80..b5e3a37 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,12 +1,17 @@ { "env": { "browser": true, - "es6": true + "es6": true, + "mocha": true }, "globals": { "browser": true }, - "plugins": ["no-unsanitized"], + "plugins": [ + "json", + "mocha", + "no-unsanitized" + ], "extends": ["eslint:recommended"], "parserOptions": { "ecmaVersion": "2017" diff --git a/.gitignore b/.gitignore index 3c3629e..3abe3d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules +node_modules +npm-debug.log diff --git a/.travis.yml b/.travis.yml index c3785d3..034b8d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,12 @@ +addons: + firefox: latest +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + - sleep 3 language: node_js node_js: -- '6.1' + - '6.1' notifications: slack: secure: Y3VDGf3FSYXhgjvE9+RqIi+kMGpRuecp6r8rzFrhV11ktPzhb5aaFs0+qpBIL/Ptz99yTiM2kaRl/DTYqdcBzWD664tZR8g0OEsossvtu/U9ujlOPxz2QVsSbPI+JE21RwvQFq4wmK/2RUzyEjEp0SU9mxGngrBtkqMOHv7cSu5vy4NL8sTzcSTt4rZNAadzQHkhnrPu+dizLYD3a1MReRI2SuQ5qgVz0yuifKa26YXrjqmN+q1weycp8wQX1f0ITeZwCkhSERMOWRCjooGdbYsR6Oe0xQ+E7A1GxXCH5np43JHxKV+Te5YOnAzthiV1rdOnJdqx10+vHdU6NIZhZwER4wy0Tjsz/kiGwnvhWW641qS2NFPRC0aa8URDGBKzt2A9dOwiJYZ/f49dOe3q04cu3q8QH3cwkwHCNqO1f5T5iwldqU4pWRGyhwiiF6P5hVFWiyQ8b5ATG5rBIWFKq2uyD3xe5R4evw5Ay+3JwFG4Ddt2/C4KWv4OldbpT5pbT1WVOAkKKoXf4G/w0j3gDbWbxTqNJMP2L+jFmkYVEVrNd8Xcb/SR7I5398YicKEfJUiz8Y+p+nIvauJ6pdb/Ee6aBECOdhSDkcUwg8bPn7p4cS+wsoEIjz0SQGIe45wJ2Zy5nwMcgG8OPJy6EwuZ0s8eUW6Lbc44al3bOs/MS+k= diff --git a/js/background.js b/js/background.js index d8a6630..36e8c1c 100644 --- a/js/background.js +++ b/js/background.js @@ -1,27 +1,29 @@ -'use strict'; - -// When the user clicks browserAction icon in toolbar, run Lightbeam -browser.browserAction.onClicked.addListener(runLightbeam); - -capture.init(); - -async function runLightbeam() { - // Checks to see if Lightbeam is already open. Returns true if it is, false if not. - async function isOpen() { - const tabs = await browser.tabs.query({}); - const fullUrl = browser.extension.getURL('index.html'); - const lightbeamTabs = tabs.filter((tab) => { - return (tab.url === fullUrl); - }); - return lightbeamTabs[0] || false; - } - - const lightbeamTab = await isOpen(); - if (!lightbeamTab) { - // only open a new Lightbeam instance if one isn't already open. - browser.tabs.create({url: 'index.html'}); - } else if (!lightbeamTab.active) { - // re-focus Lightbeam if it is already open but lost focus - browser.tabs.update(lightbeamTab.id, {active: true}); - } -} +/* eslint no-undef: "off" */ +'use strict'; + +// When the user clicks browserAction icon in toolbar, run Lightbeam +browser.browserAction.onClicked.addListener(runLightbeam); + +capture.init(); + +async function runLightbeam() { + // Checks to see if Lightbeam is already open. + // Returns true if it is, false if not. + async function isOpen() { + const tabs = await browser.tabs.query({}); + const fullUrl = browser.extension.getURL('index.html'); + const lightbeamTabs = tabs.filter((tab) => { + return (tab.url === fullUrl); + }); + return lightbeamTabs[0] || false; + } + + const lightbeamTab = await isOpen(); + if (!lightbeamTab) { + // only open a new Lightbeam instance if one isn't already open. + browser.tabs.create({url: 'index.html'}); + } else if (!lightbeamTab.active) { + // re-focus Lightbeam if it is already open but lost focus + browser.tabs.update(lightbeamTab.id, {active: true}); + } +} diff --git a/js/capture.js b/js/capture.js index e233a4a..6ca1d13 100644 --- a/js/capture.js +++ b/js/capture.js @@ -1,3 +1,6 @@ +/* eslint no-console: "off" */ +/* eslint no-unused-vars: "off" */ + const capture = { init() { this.addListeners(); diff --git a/js/store.js b/js/store.js index efe5a59..b7c81b7 100644 --- a/js/store.js +++ b/js/store.js @@ -40,7 +40,6 @@ const store = { async set(websites) { /** - * @todo rewrite this method so that websites object is updated with additional hostnames * @todo code to be updated in next PR */ return await browser.storage.local.set({ websites }); @@ -51,31 +50,10 @@ const store = { }, addListeners() { - browser.storage.onChanged.addListener((changes, area) => { - /* - * @todo update the code - */ - }); + /* + * @todo update the code + */ } }; -store.getAll() - .then(websites => { - console.log('get all websites:', websites); - }); - -store.getFirstParty('a4.com') - .then(firtParty => { - console.log('firtParty:', firtParty); - }) - .catch(error => { - console.log('error from getFirstParty:', error.message); - }); - -store.getThirdParties('a4.com') - .then(thirdParties => { - console.log('thirdParties:', thirdParties); - }) - .catch(error => { - console.log('error from getThirdParties:', error.message); - }); \ No newline at end of file +store.init(); \ No newline at end of file diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..8701e58 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,21 @@ +/* eslint no-undef: "off" */ + +/* +* @todo resolve 'module' is undefined and turn 'on' the eslint rule +*/ + +module.exports = function(config) { + config.set({ + singleRun: true, + browsers: ['Firefox'], + frameworks: ['mocha', 'chai'], + files: [ + 'test/unit/*.test.js' + ], + plugins: [ + 'karma-chai', + 'karma-firefox-launcher', + 'karma-mocha' + ] + }); +}; diff --git a/package.json b/package.json index a2e5a39..a1dd02d 100644 --- a/package.json +++ b/package.json @@ -4,20 +4,46 @@ "description": "Lightbeam web extension", "main": "background.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "lint": "npm-run-all lint:*", + "lint:eslint": "eslint --ext=.js,.json .", + "lint:webext": "web-ext -s js lint", + "test": "npm-run-all test:*", + "test:lint": "npm run lint:eslint", + "test:karma": "karma start --browsers Firefox --single-run" }, "repository": { "type": "git", - "url": "git+https://github.com/pauljt/lightbeam-we.git" + "url": "https://github.com/electrolyfish/lightbeam-we.git" }, "author": "Mozilla", "license": "MPL-2.0", "bugs": { - "url": "https://github.com/pauljt/lightbeam-we/issues" + "url": "https://github.com/electrolyfish/lightbeam-we/issues" }, - "homepage": "https://github.com/pauljt/lightbeam-we#readme", + "homepage": "https://github.com/electrolyfish/lightbeam-we#readme", + "dependencies": {}, "devDependencies": { + "chai": "^3.5.0", "eslint": "^3.19.0", - "eslint-plugin-no-unsanitized": "^2.0.0" - } + "eslint-plugin-json": "^1.2.0", + "eslint-plugin-mocha": "^4.9.0", + "eslint-plugin-no-unsanitized": "^2.0.0", + "karma": "^1.7.0", + "karma-chai": "^0.1.0", + "karma-firefox-launcher": "^1.0.1", + "karma-mocha": "^1.3.0", + "mocha": "^3.4.1", + "npm-run-all": "^4.0.2", + "sinon": "^2.3.1", + "sinon-chrome": "^2.2.1" + }, + "directories": { + "test": "test" + }, + "keywords": [ + "webextension", + "mozilla", + "web", + "security" + ] } diff --git a/test/unit/store.test.js b/test/unit/store.test.js new file mode 100644 index 0000000..3ee497b --- /dev/null +++ b/test/unit/store.test.js @@ -0,0 +1,96 @@ +/* eslint no-undef: "off" */ +/* eslint no-console: "off" */ + +/* +* @todo resolve 'expect' is undefined and turn 'on' the above eslint rules +* @todo use eslint-plugin-chai to resolve 'expect' +*/ + +'use strict'; + +describe('store.js', () => { + describe('store get method', () => { + const mockStore = { + _websites: { + 'a1.com': { + faviconUrl: '/link/to/url', + firstAccess: 1234, + lastAccess: 5678, + thirdPartyRequests: { + 'a.com': { + requestTime: 9012, + cookies: [] + }, + 'b.com': {}, + 'c.com': {} + } + }, + 'a2.com': {} + }, + + getAll() { + return Promise.resolve(this._websites); + }, + + getFirstParty(hostname) { + if (!hostname) { + throw new Error('getFirstParty requires a valid hostname argument'); + } + + return Promise.resolve(this._websites[hostname]); + }, + + getThirdParties(hostname) { + if (!hostname) { + throw new Error('getFirstParty requires a valid hostname argument'); + } + + const firstParty = this._websites[hostname]; + if ('thirdPartyRequests' in firstParty) { + return Promise.resolve(firstParty.thirdPartyRequests); + } + + return Promise.resolve(null); + } + }; + + it('should get all websites from store', async () => { + const websites = await mockStore.getAll(); + expect(websites).to.deep.equal(mockStore._websites); + }); + + it('should get website object for a1.com', async () => { + const website = await mockStore.getFirstParty('a1.com'); + expect(website).to.deep.equal(mockStore._websites['a1.com']); + }); + + it('error thrown when hostname is not passed for getFirstParty()', + async () => { + try { + await mockStore.getFirstParty(); + } catch (err) { + console.log('error from getFirstParty', err); + } + }); + + it('should get thirdPartyRequests for a1.com', async () => { + const thirdParties = await mockStore.getThirdParties('a1.com'); + const mockThirdParties = mockStore._websites['a1.com'].thirdPartyRequests; + expect(thirdParties).to.deep.equal(mockThirdParties); + }); + + it('error thrown when hostname is not passed for getThirdParties()', + async () => { + try { + await mockStore.getThirdParties(); + } catch (err) { + console.log('error from getThirdParties', err); + } + }); + + it('should return null for getThirdParties()', async () => { + const thirdPartyRequests = await mockStore.getThirdParties('a2.com'); + expect(thirdPartyRequests).to.equal(null); + }); + }); +}); \ No newline at end of file