Merge pull request #329 from ueokande/e2e-test

End-to-End testing
jh-changes
Shin'ya Ueoka 7 years ago committed by GitHub
commit a48915d4e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 8
      .travis.yml
  3. 28
      e2e/ambassador/manifest.json
  4. 36
      e2e/ambassador/src/background/index.js
  5. 7
      e2e/ambassador/src/background/ipc.js
  6. 18
      e2e/ambassador/src/background/tabs.js
  7. 29
      e2e/ambassador/src/client/ipc.js
  8. 29
      e2e/ambassador/src/client/keys.js
  9. 12
      e2e/ambassador/src/client/tabs.js
  10. 27
      e2e/ambassador/src/client/windows.js
  11. 37
      e2e/ambassador/src/content/index.js
  12. 40
      e2e/ambassador/src/content/ipc.js
  13. 24
      e2e/ambassador/src/shared/messages.js
  14. 37
      e2e/ambassador/webpack.config.js
  15. 48
      e2e/contents/tab.test.js
  16. 10
      e2e/karma-delay.js
  17. 53
      e2e/karma-webext-launcher.js
  18. 45
      e2e/karma.conf.js
  19. 14
      e2e/web-server/index.js
  20. 4128
      package-lock.json
  21. 8
      package.json

1
.gitignore vendored

@ -1,3 +1,4 @@
/node_modules/
/build/
/e2e/ambassador/build/
*.zip

@ -2,11 +2,17 @@ language: node_js
node_js:
- "6"
addons:
firefox: "56.0"
firefox: "58.0"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
cache:
directories:
- node_modules
script:
- npm run lint
- npm test
- npm run package
- npm run build
- npm run ambassador:build
- node e2e/web-server & npm run test:e2e

@ -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');

4128
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -5,8 +5,11 @@
"start": "webpack -w --debug --devtool inline-source-map",
"build": "NODE_ENV=production webpack --progress --display-error-details",
"package": "npm run build && script/package.sh",
"lint": "eslint --ext .jsx,.js src",
"test": "karma start"
"lint": "eslint --ext .jsx,.js src e2e/ambassador/src",
"test": "karma start",
"test:e2e": "karma start e2e/karma.conf.js",
"ambassador:start": "webpack -w --debug --context e2e/ambassador --config e2e/ambassador/webpack.config.js --devtool inline-source-map",
"ambassador:build": "webpack --context e2e/ambassador --config e2e/ambassador/webpack.config.js"
},
"repository": {
"type": "git",
@ -43,6 +46,7 @@
"preact": "^8.2.6",
"sass-loader": "^6.0.6",
"style-loader": "^0.19.0",
"web-ext": "github:ueokande/web-ext#patched-2.3.2",
"webpack": "^3.5.3"
}
}