commit
a48915d4e0
21 changed files with 4203 additions and 436 deletions
@ -1,3 +1,4 @@ |
|||||||
/node_modules/ |
/node_modules/ |
||||||
/build/ |
/build/ |
||||||
|
/e2e/ambassador/build/ |
||||||
*.zip |
*.zip |
||||||
|
@ -0,0 +1,28 @@ |
|||||||
|
{ |
||||||
|
"manifest_version": 2, |
||||||
|
"name": "ambassador", |
||||||
|
"description": "WebExtension test helper", |
||||||
|
"version": "0.1", |
||||||
|
"content_scripts": [ |
||||||
|
{ |
||||||
|
"all_frames": true, |
||||||
|
"matches": [ "<all_urls>" ], |
||||||
|
"js": [ "build/content.js" ], |
||||||
|
"run_at": "document_start", |
||||||
|
"match_about_blank": true |
||||||
|
} |
||||||
|
], |
||||||
|
"background": { |
||||||
|
"scripts": [ |
||||||
|
"build/background.js" |
||||||
|
] |
||||||
|
}, |
||||||
|
"permissions": [ |
||||||
|
"history", |
||||||
|
"sessions", |
||||||
|
"storage", |
||||||
|
"tabs", |
||||||
|
"clipboardRead", |
||||||
|
"activeTab" |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
import { |
||||||
|
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET, |
||||||
|
TABS_CREATE, |
||||||
|
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP, |
||||||
|
} from '../shared/messages'; |
||||||
|
import * as tabs from './tabs'; |
||||||
|
import { receiveContentMessage } from './ipc'; |
||||||
|
|
||||||
|
receiveContentMessage((message) => { |
||||||
|
switch (message.type) { |
||||||
|
case WINDOWS_CREATE: |
||||||
|
return browser.windows.create({ url: message.url }); |
||||||
|
case WINDOWS_REMOVE: |
||||||
|
return browser.windows.remove(message.windowId); |
||||||
|
case WINDOWS_GET: |
||||||
|
return browser.windows.get(message.windowId, { populate: true }); |
||||||
|
case TABS_CREATE: |
||||||
|
return tabs.create({ |
||||||
|
url: message.url, |
||||||
|
windowId: message.windowId, |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
receiveContentMessage((message) => { |
||||||
|
switch (message.type) { |
||||||
|
case EVENT_KEYPRESS: |
||||||
|
case EVENT_KEYDOWN: |
||||||
|
case EVENT_KEYUP: |
||||||
|
return browser.tabs.sendMessage( |
||||||
|
message.tabId, |
||||||
|
message |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,7 @@ |
|||||||
|
const receiveContentMessage = (func) => { |
||||||
|
browser.runtime.onMessage.addListener((message) => { |
||||||
|
return func(message); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { receiveContentMessage }; |
@ -0,0 +1,18 @@ |
|||||||
|
const create = (props = {}) => { |
||||||
|
return new Promise((resolve) => { |
||||||
|
browser.tabs.create(props).then((createdTab) => { |
||||||
|
let callback = (tabId, changeInfo, tab) => { |
||||||
|
if (tab.url !== 'about:blank' && tabId === createdTab.id && |
||||||
|
changeInfo.status === 'complete') { |
||||||
|
browser.tabs.onUpdated.removeListener(callback); |
||||||
|
resolve(tab); |
||||||
|
} |
||||||
|
}; |
||||||
|
browser.tabs.onUpdated.addListener(callback); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { |
||||||
|
create, |
||||||
|
}; |
@ -0,0 +1,29 @@ |
|||||||
|
import { METHOD_REQUEST, METHOD_RESPONSE } from '../shared/messages'; |
||||||
|
|
||||||
|
const generateId = () => { |
||||||
|
return Math.random().toString(); |
||||||
|
}; |
||||||
|
|
||||||
|
const send = (message) => { |
||||||
|
return new Promise((resolve) => { |
||||||
|
let id = generateId(); |
||||||
|
let callback = (e) => { |
||||||
|
let packet = e.data; |
||||||
|
if (e.source !== window || packet.method !== METHOD_RESPONSE || |
||||||
|
packet.id !== id) { |
||||||
|
return; |
||||||
|
} |
||||||
|
window.removeEventListener('message', callback); |
||||||
|
resolve(packet.message); |
||||||
|
}; |
||||||
|
window.addEventListener('message', callback); |
||||||
|
|
||||||
|
window.postMessage({ |
||||||
|
id, |
||||||
|
method: METHOD_REQUEST, |
||||||
|
message |
||||||
|
}, window.origin); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { send }; |
@ -0,0 +1,29 @@ |
|||||||
|
import { EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP } from '../shared/messages'; |
||||||
|
import * as ipc from './ipc'; |
||||||
|
|
||||||
|
const press = (tabId, key) => { |
||||||
|
return ipc.send({ |
||||||
|
type: EVENT_KEYPRESS, |
||||||
|
tabId, |
||||||
|
key, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const down = (tabId, key) => { |
||||||
|
return ipc.send({ |
||||||
|
type: EVENT_KEYDOWN, |
||||||
|
tabId, |
||||||
|
key, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
const up = (tabId, key) => { |
||||||
|
return ipc.send({ |
||||||
|
type: EVENT_KEYUP, |
||||||
|
tabId, |
||||||
|
key, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { press, down, up }; |
@ -0,0 +1,12 @@ |
|||||||
|
import { TABS_CREATE } from '../shared/messages'; |
||||||
|
import * as ipc from './ipc'; |
||||||
|
|
||||||
|
const create = (windowId, url) => { |
||||||
|
return ipc.send({ |
||||||
|
type: TABS_CREATE, |
||||||
|
windowId, |
||||||
|
url, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { create }; |
@ -0,0 +1,27 @@ |
|||||||
|
import { |
||||||
|
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET |
||||||
|
} from '../shared/messages'; |
||||||
|
import * as ipc from './ipc'; |
||||||
|
|
||||||
|
const create = (url) => { |
||||||
|
return ipc.send({ |
||||||
|
type: WINDOWS_CREATE, |
||||||
|
url, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const remove = (windowId) => { |
||||||
|
return ipc.send({ |
||||||
|
type: WINDOWS_REMOVE, |
||||||
|
windowId, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const get = (windowId) => { |
||||||
|
return ipc.send({ |
||||||
|
type: WINDOWS_GET, |
||||||
|
windowId, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { create, remove, get }; |
@ -0,0 +1,37 @@ |
|||||||
|
import { |
||||||
|
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET, |
||||||
|
TABS_CREATE, |
||||||
|
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP, |
||||||
|
} from '../shared/messages'; |
||||||
|
import * as ipc from './ipc'; |
||||||
|
|
||||||
|
ipc.receivePageMessage((message) => { |
||||||
|
switch (message.type) { |
||||||
|
case WINDOWS_CREATE: |
||||||
|
case WINDOWS_REMOVE: |
||||||
|
case WINDOWS_GET: |
||||||
|
case TABS_CREATE: |
||||||
|
case EVENT_KEYPRESS: |
||||||
|
case EVENT_KEYDOWN: |
||||||
|
case EVENT_KEYUP: |
||||||
|
return ipc.sendToBackground(message); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
ipc.receiveBackgroundMesssage((message) => { |
||||||
|
switch (message.type) { |
||||||
|
case EVENT_KEYPRESS: |
||||||
|
document.body.dispatchEvent( |
||||||
|
new KeyboardEvent('keypress', { 'key': message.key })); |
||||||
|
break; |
||||||
|
case EVENT_KEYDOWN: |
||||||
|
document.body.dispatchEvent( |
||||||
|
new KeyboardEvent('keydown', { 'key': message.key })); |
||||||
|
break; |
||||||
|
case EVENT_KEYUP: |
||||||
|
document.body.dispatchEvent( |
||||||
|
new KeyboardEvent('keyup', { 'key': message.key })); |
||||||
|
break; |
||||||
|
} |
||||||
|
return Promise.resolve({}); |
||||||
|
}); |
@ -0,0 +1,40 @@ |
|||||||
|
import { METHOD_REQUEST, METHOD_RESPONSE } from '../shared/messages'; |
||||||
|
|
||||||
|
const sendToBackground = (message) => { |
||||||
|
return browser.runtime.sendMessage(message); |
||||||
|
}; |
||||||
|
|
||||||
|
const receiveBackgroundMesssage = (func) => { |
||||||
|
return browser.runtime.onMessage.addListener((message) => { |
||||||
|
return Promise.resolve(func(message)); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const receivePageMessage = (func) => { |
||||||
|
window.addEventListener('message', (e) => { |
||||||
|
let packet = e.data; |
||||||
|
if (e.origin !== window.origin || packet.method !== METHOD_REQUEST) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
let resp = { |
||||||
|
id: packet.id, |
||||||
|
method: METHOD_RESPONSE, |
||||||
|
}; |
||||||
|
let respMessage = func(packet.message); |
||||||
|
if (respMessage instanceof Promise) { |
||||||
|
return respMessage.then((data) => { |
||||||
|
resp.message = data; |
||||||
|
e.source.postMessage(resp, e.origin); |
||||||
|
}); |
||||||
|
} else if (respMessage) { |
||||||
|
resp.message = respMessage; |
||||||
|
} |
||||||
|
e.source.postMessage(resp, e.origin); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export { |
||||||
|
sendToBackground, receiveBackgroundMesssage, |
||||||
|
receivePageMessage, |
||||||
|
}; |
@ -0,0 +1,24 @@ |
|||||||
|
const METHOD_REQUEST = 'request'; |
||||||
|
const METHOD_RESPONSE = 'response'; |
||||||
|
const WINDOWS_CREATE = 'windows.create'; |
||||||
|
const WINDOWS_REMOVE = 'windows.remove'; |
||||||
|
const WINDOWS_GET = 'windows.get'; |
||||||
|
const TABS_CREATE = 'tabs.create'; |
||||||
|
const EVENT_KEYPRESS = 'event.keypress'; |
||||||
|
const EVENT_KEYDOWN = 'event.keydown'; |
||||||
|
const EVENT_KEYUP = 'event.keyup'; |
||||||
|
|
||||||
|
export { |
||||||
|
METHOD_REQUEST, |
||||||
|
METHOD_RESPONSE, |
||||||
|
|
||||||
|
WINDOWS_CREATE, |
||||||
|
WINDOWS_REMOVE, |
||||||
|
WINDOWS_GET, |
||||||
|
|
||||||
|
TABS_CREATE, |
||||||
|
|
||||||
|
EVENT_KEYPRESS, |
||||||
|
EVENT_KEYDOWN, |
||||||
|
EVENT_KEYUP, |
||||||
|
}; |
@ -0,0 +1,37 @@ |
|||||||
|
const path = require('path'); |
||||||
|
|
||||||
|
const src = path.resolve(__dirname, 'src'); |
||||||
|
const dist = path.resolve(__dirname, 'build'); |
||||||
|
|
||||||
|
config = { |
||||||
|
entry: { |
||||||
|
content: path.join(src, 'content'), |
||||||
|
background: path.join(src, 'background') |
||||||
|
}, |
||||||
|
|
||||||
|
output: { |
||||||
|
path: dist, |
||||||
|
filename: '[name].js' |
||||||
|
}, |
||||||
|
|
||||||
|
module: { |
||||||
|
loaders: [ |
||||||
|
{ |
||||||
|
test: [ /\.js$/ ], |
||||||
|
exclude: /node_modules/, |
||||||
|
loader: 'babel-loader', |
||||||
|
query: { |
||||||
|
presets: ['es2015'] |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
|
||||||
|
resolve: { |
||||||
|
extensions: [ '.js' ], |
||||||
|
modules: [path.join(__dirname, 'src'), 'node_modules'] |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = config |
||||||
|
|
@ -0,0 +1,48 @@ |
|||||||
|
import { expect } from "chai"; |
||||||
|
import * as windows from "../ambassador/src/client/windows"; |
||||||
|
import * as tabs from "../ambassador/src/client/tabs"; |
||||||
|
import * as keys from "../ambassador/src/client/keys"; |
||||||
|
|
||||||
|
const SERVER_URL = "localhost:11111"; |
||||||
|
|
||||||
|
describe("tab test", () => { |
||||||
|
let targetWindow; |
||||||
|
|
||||||
|
before(() => { |
||||||
|
return windows.create().then((win) => { |
||||||
|
targetWindow = win; |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
after(() => { |
||||||
|
return windows.remove(targetWindow.id); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('press d', () => { |
||||||
|
it('deletes tab', () => { |
||||||
|
return tabs.create(targetWindow.id, SERVER_URL).then((tab) => { |
||||||
|
return keys.press(tab.id, 'd'); |
||||||
|
}).then(() => { |
||||||
|
return windows.get(targetWindow.id); |
||||||
|
}).then((after) => { |
||||||
|
expect(after.tabs).to.have.lengthOf(1); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('press zd', () => { |
||||||
|
it('duplicates tab', () => { |
||||||
|
let targetTab = 0; |
||||||
|
return tabs.create(targetWindow.id, SERVER_URL).then((tab) => { |
||||||
|
targetTab = tab; |
||||||
|
return keys.press(targetTab.id, 'z'); |
||||||
|
}).then(() => { |
||||||
|
return keys.press(targetTab.id, 'd'); |
||||||
|
}).then(() => { |
||||||
|
return windows.get(targetWindow.id); |
||||||
|
}).then((after) => { |
||||||
|
expect(after.tabs).to.have.lengthOf(3); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}) |
||||||
|
}); |
@ -0,0 +1,10 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
window.__karma__.start = (function(start){ |
||||||
|
return function(){ |
||||||
|
var args = arguments |
||||||
|
setTimeout(() => { |
||||||
|
start(args) |
||||||
|
}, 3000); |
||||||
|
}; |
||||||
|
}(window.__karma__.start)); |
@ -0,0 +1,53 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
var fs = require('fs') |
||||||
|
var path = require('path') |
||||||
|
|
||||||
|
var PREFS = { |
||||||
|
'browser.shell.checkDefaultBrowser': 'false', |
||||||
|
'browser.bookmarks.restore_default_bookmarks': 'false', |
||||||
|
'dom.disable_open_during_load': 'false', |
||||||
|
'dom.max_script_run_time': '0', |
||||||
|
'dom.min_background_timeout_value': '10', |
||||||
|
'extensions.autoDisableScopes': '0', |
||||||
|
'extensions.enabledScopes': '15', |
||||||
|
} |
||||||
|
|
||||||
|
var FirefoxWebExt = function (id, baseBrowserDecorator, args) { |
||||||
|
baseBrowserDecorator(this) |
||||||
|
|
||||||
|
this._start = function (url) { |
||||||
|
var self = this |
||||||
|
var command = this._getCommand() |
||||||
|
|
||||||
|
let prefArgs = [].concat(...Object.keys(PREFS).map((key) => { |
||||||
|
return ['--pref', key + '=' + PREFS[key]]; |
||||||
|
})); |
||||||
|
let sourceDirArgs = [].concat(...args.sourceDirs.map((dir) => { |
||||||
|
return ['--source-dir', dir]; |
||||||
|
})); |
||||||
|
|
||||||
|
self._execCommand( |
||||||
|
command, |
||||||
|
['run', '--start-url', url, '--no-input'].concat(sourceDirArgs, prefArgs) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FirefoxWebExt.prototype = { |
||||||
|
name: 'FirefoxWebExt', |
||||||
|
|
||||||
|
DEFAULT_CMD: { |
||||||
|
linux: 'node_modules/web-ext/bin/web-ext', |
||||||
|
darwin: 'node_modules/web-ext/bin/web-ext', |
||||||
|
win32: 'node_modules/web-ext/bin/web-ext', |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FirefoxWebExt.$inject = ['id', 'baseBrowserDecorator', 'args'] |
||||||
|
|
||||||
|
// PUBLISH DI MODULE
|
||||||
|
module.exports = { |
||||||
|
'launcher:FirefoxWebExt': ['type', FirefoxWebExt], |
||||||
|
} |
||||||
|
|
@ -0,0 +1,45 @@ |
|||||||
|
module.exports = function (config) { |
||||||
|
|
||||||
|
config.set({ |
||||||
|
basePath: '', |
||||||
|
frameworks: ['mocha'], |
||||||
|
files: [ |
||||||
|
'karma-delay.js', |
||||||
|
'**/*.test.js' |
||||||
|
], |
||||||
|
|
||||||
|
preprocessors: { |
||||||
|
'**/*.test.js': ['webpack'] |
||||||
|
}, |
||||||
|
|
||||||
|
port: 9876, |
||||||
|
colors: true, |
||||||
|
logLevel: config.LOG_INFO, |
||||||
|
|
||||||
|
customLaunchers: { |
||||||
|
FirefoxWebExtRunner: { |
||||||
|
base: 'FirefoxWebExt', |
||||||
|
sourceDirs: [ '.', 'e2e/ambassador'], |
||||||
|
}, |
||||||
|
}, |
||||||
|
browsers: ['FirefoxWebExtRunner'], |
||||||
|
sauceLabs: { |
||||||
|
username: 'michael_jackson' |
||||||
|
}, |
||||||
|
|
||||||
|
singleRun: true, |
||||||
|
|
||||||
|
webpackMiddleware: { |
||||||
|
noInfo: true |
||||||
|
}, |
||||||
|
|
||||||
|
reporters: ['mocha'], |
||||||
|
|
||||||
|
plugins: [ |
||||||
|
require('./karma-webext-launcher'), |
||||||
|
'karma-mocha', |
||||||
|
'karma-webpack', |
||||||
|
'karma-mocha-reporter', |
||||||
|
], |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
var http = require('http'); |
||||||
|
|
||||||
|
const content = |
||||||
|
'<!DOCTYPE html>' + |
||||||
|
'<html lang="en">' + |
||||||
|
'<body style="width:10000px; height:10000px">' + |
||||||
|
'</body>' + |
||||||
|
'</html">' ; |
||||||
|
|
||||||
|
|
||||||
|
http.createServer(function (req, res) { |
||||||
|
res.writeHead(200, {'Content-Type': 'text/html'}); |
||||||
|
res.end(content); |
||||||
|
}).listen(11111, '127.0.0.1'); |
File diff suppressed because it is too large
Load Diff
Reference in new issue