Merge pull request #559 from ueokande/lanthan-integration-test

Replace E2E tests with lanthan
jh-changes
Shin'ya Ueoka 6 years ago committed by GitHub
commit 859d0372b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      .circleci/config.yml
  2. 2
      .gitignore
  3. 28
      e2e/ambassador/manifest.json
  4. 49
      e2e/ambassador/src/background/index.js
  5. 7
      e2e/ambassador/src/background/ipc.js
  6. 28
      e2e/ambassador/src/background/tabs.js
  7. 29
      e2e/ambassador/src/client/ipc.js
  8. 28
      e2e/ambassador/src/client/keys.js
  9. 20
      e2e/ambassador/src/client/scrolls.js
  10. 60
      e2e/ambassador/src/client/tabs.js
  11. 27
      e2e/ambassador/src/client/windows.js
  12. 31
      e2e/ambassador/src/content/events.js
  13. 30
      e2e/ambassador/src/content/index.js
  14. 40
      e2e/ambassador/src/content/ipc.js
  15. 20
      e2e/ambassador/src/content/scrolls.js
  16. 40
      e2e/ambassador/src/shared/messages.js
  17. 24
      e2e/ambassador/webpack.config.js
  18. 83
      e2e/contents/follow.test.js
  19. 71
      e2e/contents/mark.test.js
  20. 102
      e2e/contents/navigate.test.js
  21. 106
      e2e/contents/scroll.test.js
  22. 192
      e2e/contents/tab.test.js
  23. 53
      e2e/contents/zoom.test.js
  24. 23
      e2e/eventually.js
  25. 10
      e2e/karma-delay.js
  26. 53
      e2e/karma-webext-launcher.js
  27. 53
      e2e/karma.conf.js
  28. 2
      e2e/main.js
  29. 121
      e2e/mark.test.js
  30. 167
      e2e/navigate.test.js
  31. 150
      e2e/scroll.test.js
  32. 202
      e2e/tab.test.js
  33. 88
      e2e/web-server/index.js
  34. 5
      e2e/web-server/url.js
  35. 81
      e2e/zoom.test.js
  36. 1362
      package-lock.json
  37. 7
      package.json

@ -6,6 +6,7 @@ executors:
- image: circleci/node:10-stretch-browsers
environment:
- FIREFOX_VERSION: "60.0esr"
- GECKODRIVER_VERSION: "0.24.0"
working_directory: ~
commands:
@ -29,6 +30,18 @@ commands:
- ~/firefox
- run: echo 'export PATH=~/firefox/$FIREFOX_VERSION:$PATH' >> $BASH_ENV
install_geckodriver:
steps:
- run:
name: Install geckodriver
command: |
mkdir -p geckodriver
url=https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v${GECKODRIVER_VERSION}-linux64.tar.gz
curl -sSLf "$url" | tar -C geckodriver xvf -
echo 'export PATH=~/geckodriver/$GECKODRIVER_VERSION:$PATH' >> $BASH_ENV
setup_npm:
steps:
- restore_cache:
@ -61,10 +74,9 @@ jobs:
- checkout
- setup_npm
- run: npm run build
- run: npm run ambassador:build
- run:
name: Run web server
command: node e2e/web-server
name: Run geckodriver
command: geckodriver
background: true
- run: npm run test:e2e

2
.gitignore vendored

@ -1,4 +1,4 @@
/node_modules/
/build/
/e2e/ambassador/build/
*.zip
lanthan-driver.log

@ -1,28 +0,0 @@
{
"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"
]
}

@ -1,49 +0,0 @@
import {
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET,
TABS_CREATE, TABS_SELECT_AT, TABS_GET, TABS_UPDATE, TABS_REMOVE,
TABS_GET_ZOOM, TABS_SET_ZOOM,
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
SCROLL_GET, SCROLL_SET,
} 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,
});
case TABS_SELECT_AT:
return tabs.selectAt({
windowId: message.windowId,
index: message.index,
});
case TABS_GET:
return browser.tabs.get(message.tabId);
case TABS_UPDATE:
return browser.tabs.update(message.tabId, message.properties);
case TABS_REMOVE:
return browser.tabs.remove(message.tabId);
case TABS_GET_ZOOM:
return browser.tabs.getZoom(message.tabId);
case TABS_SET_ZOOM:
return browser.tabs.setZoom(message.tabId, message.factor);
case EVENT_KEYPRESS:
case EVENT_KEYDOWN:
case EVENT_KEYUP:
case SCROLL_GET:
case SCROLL_SET:
return browser.tabs.sendMessage(
message.tabId,
message
);
}
});

@ -1,7 +0,0 @@
const receiveContentMessage = (func) => {
browser.runtime.onMessage.addListener((message) => {
return func(message);
});
};
export { receiveContentMessage };

@ -1,28 +0,0 @@
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);
// wait for 50 milliseconds to ensure plugin loaded;
setTimeout(() => resolve(tab), 50);
}
};
browser.tabs.onUpdated.addListener(callback);
});
});
};
const selectAt = (props = {}) => {
return browser.tabs.query({ windowId: props.windowId }).then((tabs) => {
let target = tabs[props.index];
return browser.tabs.update(target.id, { active: true });
});
};
export {
create, selectAt
};

@ -1,29 +0,0 @@
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 };

@ -1,28 +0,0 @@
import { EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP } from '../shared/messages';
import * as ipc from './ipc';
const NEUTRAL_MODIFIERS = { shiftKey: false, altKey: false, ctrlKey: false };
const press = (tabId, key, modifiers = NEUTRAL_MODIFIERS) => {
return ipc.send({ ...modifiers,
type: EVENT_KEYPRESS,
tabId,
key, });
};
const down = (tabId, key, modifiers = NEUTRAL_MODIFIERS) => {
return ipc.send({ modifiers,
type: EVENT_KEYDOWN,
tabId,
key, });
};
const up = (tabId, key, modifiers = NEUTRAL_MODIFIERS) => {
return ipc.send({ modifiers,
type: EVENT_KEYUP,
tabId,
key, });
};
export { press, down, up };

@ -1,20 +0,0 @@
import { SCROLL_GET, SCROLL_SET } from '../shared/messages';
import * as ipc from './ipc';
const get = (tabId) => {
return ipc.send({
type: SCROLL_GET,
tabId,
});
};
const set = (tabId, x, y) => {
return ipc.send({
type: SCROLL_SET,
tabId,
x,
y,
});
};
export { get, set };

@ -1,60 +0,0 @@
import {
TABS_CREATE, TABS_SELECT_AT, TABS_GET, TABS_UPDATE, TABS_REMOVE,
TABS_GET_ZOOM, TABS_SET_ZOOM,
} from '../shared/messages';
import * as ipc from './ipc';
const create = (windowId, url) => {
return ipc.send({
type: TABS_CREATE,
windowId,
url,
});
};
const selectAt = (windowId, index) => {
return ipc.send({
type: TABS_SELECT_AT,
windowId,
index,
});
};
const get = (tabId) => {
return ipc.send({
type: TABS_GET,
tabId,
});
};
const update = (tabId, properties) => {
return ipc.send({
type: TABS_UPDATE,
tabId,
properties,
});
};
const remove = (tabId) => {
return ipc.send({
type: TABS_REMOVE,
tabId
});
};
const getZoom = (tabId) => {
return ipc.send({
tabId,
type: TABS_GET_ZOOM,
});
};
const setZoom = (tabId, factor) => {
return ipc.send({
type: TABS_SET_ZOOM,
tabId,
factor,
});
};
export { create, selectAt, get, update, remove, getZoom, setZoom };

@ -1,27 +0,0 @@
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 };

@ -1,31 +0,0 @@
const keypress = (opts) => {
let event = new KeyboardEvent('keypress', {
key: opts.key,
altKey: opts.altKey,
shiftKey: opts.shiftKey,
ctrlKey: opts.ctrlKey
});
document.body.dispatchEvent(event);
};
const keydown = (opts) => {
let event = new KeyboardEvent('keydown', {
key: opts.key,
altKey: opts.altKey,
shiftKey: opts.shiftKey,
ctrlKey: opts.ctrlKey
});
document.body.dispatchEvent(event);
};
const keyup = (opts) => {
let event = new KeyboardEvent('keyup', {
key: opts.key,
altKey: opts.altKey,
shiftKey: opts.shiftKey,
ctrlKey: opts.ctrlKey
});
document.body.dispatchEvent(event);
};
export { keypress, keydown, keyup };

@ -1,30 +0,0 @@
import {
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
SCROLL_GET, SCROLL_SET,
} from '../shared/messages';
import * as ipc from './ipc';
import * as events from './events';
import * as scrolls from './scrolls';
ipc.receivePageMessage((message) => {
return ipc.sendToBackground(message);
});
ipc.receiveBackgroundMesssage((message) => {
switch (message.type) {
case EVENT_KEYPRESS:
events.keypress(message);
break;
case EVENT_KEYDOWN:
events.keydown(message);
break;
case EVENT_KEYUP:
events.keyup(message);
break;
case SCROLL_GET:
return Promise.resolve(scrolls.get());
case SCROLL_SET:
return Promise.resolve(scrolls.set(message.x, message.y));
}
return Promise.resolve({});
});

@ -1,40 +0,0 @@
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,
};

@ -1,20 +0,0 @@
const get = () => {
let element = document.documentElement;
return {
xMax: element.scrollWidth - element.clientWidth,
yMax: element.scrollHeight - element.clientHeight,
x: element.scrollLeft,
y: element.scrollTop,
frameWidth: element.clientWidth,
frameHeight: element.clientHeight,
};
};
const set = (x, y) => {
let element = document.documentElement;
element.scrollLeft = x;
element.scrollTop = y;
return get();
};
export { get, set };

@ -1,40 +0,0 @@
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 TABS_SELECT_AT = 'tabs.selectAt';
const TABS_GET = 'tabs.get';
const TABS_UPDATE = 'tabs.update';
const TABS_REMOVE = 'tabs.remove';
const TABS_GET_ZOOM = 'tabs.get.zoom';
const TABS_SET_ZOOM = 'tabs.set.zoom';
const EVENT_KEYPRESS = 'event.keypress';
const EVENT_KEYDOWN = 'event.keydown';
const EVENT_KEYUP = 'event.keyup';
const SCROLL_GET = 'scroll.get';
const SCROLL_SET = 'scroll.set';
export {
METHOD_REQUEST,
METHOD_RESPONSE,
WINDOWS_CREATE,
WINDOWS_REMOVE,
WINDOWS_GET,
TABS_GET,
TABS_UPDATE,
TABS_CREATE,
TABS_SELECT_AT,
TABS_GET_ZOOM,
TABS_SET_ZOOM,
TABS_REMOVE,
EVENT_KEYPRESS,
EVENT_KEYDOWN,
EVENT_KEYUP,
SCROLL_GET,
SCROLL_SET,
};

@ -1,24 +0,0 @@
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'
},
resolve: {
extensions: [ '.js' ],
modules: [path.join(__dirname, 'src'), 'node_modules']
}
};
module.exports = config

@ -1,83 +0,0 @@
import * as windows from "../ambassador/src/client/windows";
import * as tabs from "../ambassador/src/client/tabs";
import * as keys from "../ambassador/src/client/keys";
import { CLIENT_URL } from '../web-server/url';
describe("tab test", () => {
let targetWindow;
beforeEach(async () => {
targetWindow = await windows.create(CLIENT_URL);
});
afterEach(async () => {
await windows.remove(targetWindow.id);
});return
it('follows link by `f`', async() => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/follow');
await keys.press(tab.id, 'f');
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
await keys.press(tab.id, 'a');
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
tab = tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/follow#a');
});
it('follows link into new tab by `F`', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/follow');
await keys.press(tab.id, 'F', { shiftKey: true });
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
await keys.press(tab.id, 'a');
await new Promise(resolve => { setTimeout(() => resolve(), 500) });
let win = await windows.get(targetWindow.id);
let urls = win.tabs.map(t => t.url);
expect(urls).to.have.lengthOf(3);
expect(urls).to.include(CLIENT_URL + '/');
expect(urls).to.include(CLIENT_URL + '/follow');
expect(urls).to.include(CLIENT_URL + '/follow#a');
});
it('follows link with target=_blank into new tab by `f`', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/follow');
await keys.press(tab.id, 'f');
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
await keys.press(tab.id, 'b');
await new Promise(resolve => { setTimeout(() => resolve(), 500) });
let win = await windows.get(targetWindow.id);
let urls = win.tabs.map(t => t.url);
expect(urls).to.have.lengthOf(3);
expect(urls).to.include(CLIENT_URL + '/');
expect(urls).to.include(CLIENT_URL + '/follow');
expect(urls).to.include(CLIENT_URL + '/follow#external');
});
it('follows link with target=_blank into new tab by `F`', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/follow');
await keys.press(tab.id, 'F', { shiftKey: true });
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
await keys.press(tab.id, 'b');
await new Promise(resolve => { setTimeout(() => resolve(), 500) });
let win = await windows.get(targetWindow.id);
let urls = win.tabs.map(t => t.url);
expect(urls).to.have.lengthOf(3);
expect(urls).to.include(CLIENT_URL + '/');
expect(urls).to.include(CLIENT_URL + '/follow');
expect(urls).to.include(CLIENT_URL + '/follow#external');
});
it('follows area by `F`', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/follow');
await keys.press(tab.id, 'f');
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
await keys.press(tab.id, 'c');
await new Promise(resolve => { setTimeout(() => resolve(), 10) });
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/follow#area');
});
});

@ -1,71 +0,0 @@
import * as windows from "../ambassador/src/client/windows";
import * as tabs from "../ambassador/src/client/tabs";
import * as keys from "../ambassador/src/client/keys";
import * as scrolls from "../ambassador/src/client/scrolls";
import { CLIENT_URL } from '../web-server/url';
describe("mark test", () => {
let targetWindow;
before(async () => {
targetWindow = await windows.create();
});
after(async () => {
await windows.remove(targetWindow.id);
});
it('set a local mark and jump to it', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/mark#local');
await scrolls.set(tab.id, 100, 100);
await keys.press(tab.id, 'm');
await keys.press(tab.id, 'a');
await scrolls.set(tab.id, 200, 200);
await keys.press(tab.id, "'");
await keys.press(tab.id, 'a');
let scroll = await scrolls.get(tab.id);
expect(scroll.x).to.be.equals(100);
expect(scroll.y).to.be.equals(100);
});
it('set a global mark and jump to it', async () => {
let tab1 = await tabs.create(targetWindow.id, CLIENT_URL + '/mark#global1');
await scrolls.set(tab1.id, 100, 100);
await keys.press(tab1.id, 'm');
await keys.press(tab1.id, 'A');
await new Promise(resolve => { setTimeout(() => resolve(), 100) });
await scrolls.set(tab1.id, 200, 200);
let tab2 = await tabs.create(targetWindow.id, CLIENT_URL + '/mark#global2');
await keys.press(tab2.id, "'");
await keys.press(tab2.id, 'A');
await new Promise(resolve => { setTimeout(() => resolve(), 100) });
tab1 = await tabs.get(tab1.id);
expect(tab1.active).to.be.true;
let scroll = await scrolls.get(tab1.id);
expect(scroll.x).to.be.equals(100);
expect(scroll.y).to.be.equals(100);
});
it('set a global mark and creates new tab from gone', async () => {
let tab1 = await tabs.create(targetWindow.id, CLIENT_URL + '/mark#gone');
await scrolls.set(tab1.id, 100, 100);
await keys.press(tab1.id, 'm');
await keys.press(tab1.id, 'A');
await tabs.remove(tab1.id);
await new Promise(resolve => { setTimeout(() => resolve(), 100) });
let tab2 = await tabs.create(targetWindow.id, CLIENT_URL + '/mark#newtab');
await keys.press(tab2.id, "'");
await keys.press(tab2.id, 'A');
await new Promise(resolve => { setTimeout(() => resolve(), 100) });
let win = await windows.get(targetWindow.id);
let found = win.tabs.find(tab => tab.url === CLIENT_URL + '/mark#gone')
expect(found).to.be.an('object');
expect(found.id).to.not.equal(tab1.id);
});
});

@ -1,102 +0,0 @@
import * as windows from "../ambassador/src/client/windows";
import * as tabs from "../ambassador/src/client/tabs";
import * as keys from "../ambassador/src/client/keys";
import * as scrolls from "../ambassador/src/client/scrolls";
import { CLIENT_URL } from '../web-server/url';
describe("navigate test", () => {
let targetWindow;
before(async () => {
targetWindow = await windows.create();
await tabs.create(targetWindow.id, CLIENT_URL);
});
after(async () => {
await windows.remove(targetWindow.id);
});
it('goes to parent', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/a/b/c');
await keys.press(tab.id, 'g');
await keys.press(tab.id, 'u');
await new Promise((resolve) => setTimeout(resolve, 10));
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/a/b/');
});
it('removes hash', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/a/b/c#navigate');
await keys.press(tab.id, 'g');
await keys.press(tab.id, 'u');
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/a/b/c#');
});
it('goes to root', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/a/b/c');
await keys.press(tab.id, 'g');
await keys.press(tab.id, 'U', { shiftKey: true });
await new Promise((resolve) => setTimeout(resolve, 10));
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/');
});
it('goes back and forward in history', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/#navigate');
await keys.press(tab.id, 'g');
await keys.press(tab.id, 'u');
await keys.press(tab.id, 'H', { shiftKey: true });
await new Promise((resolve) => setTimeout(resolve, 10));
tab = await tabs.get(tab.id);
expect(tab.url, 'go back in history').to.be.equal(CLIENT_URL + '/#navigate');
await new Promise((resolve) => setTimeout(resolve, 10));
await keys.press(tab.id, 'L', { shiftKey: true });
tab = await tabs.get(tab.id);
expect(tab.url, 'go next in history').to.be.equal(CLIENT_URL + '/#');
});
it('goes previous page by <a>', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/a-pagenation?page=10');
await keys.press(tab.id, '[');
await keys.press(tab.id, '[');
await new Promise((resolve) => setTimeout(resolve, 10));
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/a-pagenation?page=9');
})
it('goes next page by <a>', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/a-pagenation?page=10');
await keys.press(tab.id, ']');
await keys.press(tab.id, ']');
await new Promise((resolve) => setTimeout(resolve, 100));
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/a-pagenation?page=11');
})
it('goes previous page by <link>', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/link-pagenation?page=10');
await keys.press(tab.id, '[');
await keys.press(tab.id, '[');
await new Promise((resolve) => setTimeout(resolve, 10));
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/link-pagenation?page=9');
})
it('goes next page by <link>', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '/link-pagenation?page=10');
await keys.press(tab.id, ']');
await keys.press(tab.id, ']');
await new Promise((resolve) => setTimeout(resolve, 10));
tab = await tabs.get(tab.id);
expect(tab.url).to.be.equal(CLIENT_URL + '/link-pagenation?page=11');
})
});

@ -1,106 +0,0 @@
import * as windows from "../ambassador/src/client/windows";
import * as tabs from "../ambassador/src/client/tabs";
import * as keys from "../ambassador/src/client/keys";
import * as scrolls from "../ambassador/src/client/scrolls";
import { CLIENT_URL } from '../web-server/url';
describe("scroll test", () => {
let targetWindow;
let targetTab;
before(async () => {
targetWindow = await windows.create();
targetTab = await tabs.create(targetWindow.id, CLIENT_URL + '/scroll');
});
after(async () => {
await windows.remove(targetWindow.id);
});
it('scrolls up by k', async () => {
let before = await scrolls.set(targetTab.id, 100, 100);
await keys.press(targetTab.id, 'k');
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.be.lessThan(before.y);
});
it('scrolls down by j', async () => {
let before = await scrolls.set(targetTab.id, 100, 100);
await keys.press(targetTab.id, 'j');
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.be.greaterThan(before.y);
});
it('scrolls left by h', async () => {
let before = await scrolls.set(targetTab.id, 100, 100)
await keys.press(targetTab.id, 'h');
let actual = await scrolls.get(targetTab.id);
expect(actual.x).to.be.lessThan(before.x);
});
it('scrolls top by gg', async () => {
await scrolls.set(targetTab.id, 100, 100);
await keys.press(targetTab.id, 'g');
await keys.press(targetTab.id, 'g');
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.be.equals(0);
});
it('scrolls bottom by G', async () => {
await scrolls.set(targetTab.id, 100, 100);
await keys.press(targetTab.id, 'G', { shiftKey: true });
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.be.equals(actual.yMax);
});
it('scrolls bottom by 0', async () => {
await scrolls.set(targetTab.id, 100, 100);
await keys.press(targetTab.id, '0');
let actual = await scrolls.get(targetTab.id);
expect(actual.x).to.be.equals(0);
});
it('scrolls bottom by $', async () => {
await scrolls.set(targetTab.id, 100, 100);
await keys.press(targetTab.id, '$');
let actual = await scrolls.get(targetTab.id);
expect(actual.x).to.be.equals(actual.xMax);
});
it('scrolls bottom by <C-U>', async () => {
let before = await scrolls.set(targetTab.id, 5000, 5000);
await keys.press(targetTab.id, 'u', { ctrlKey: true });
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.closeTo(before.y - before.frameHeight / 2, 1);
});
it('scrolls bottom by <C-D>', async () => {
let before = await scrolls.set(targetTab.id, 5000, 5000);
await keys.press(targetTab.id, 'd', { ctrlKey: true });
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.closeTo(before.y + before.frameHeight / 2, 1);
});
it('scrolls bottom by <C-B>', async () => {
let before = await scrolls.set(targetTab.id, 5000, 5000);
await keys.press(targetTab.id, 'b', { ctrlKey: true });
let actual = await await scrolls.get(targetTab.id);
expect(actual.y).to.equals(before.y - before.frameHeight);
});
it('scrolls bottom by <C-F>', async () => {
let before = await scrolls.set(targetTab.id, 5000, 5000);
await keys.press(targetTab.id, 'f', { ctrlKey: true });
let actual = await scrolls.get(targetTab.id);
expect(actual.y).to.equals(before.y + before.frameHeight);
});
});

@ -1,192 +0,0 @@
import * as windows from "../ambassador/src/client/windows";
import * as tabs from "../ambassador/src/client/tabs";
import * as keys from "../ambassador/src/client/keys";
import { CLIENT_URL } from '../web-server/url';
describe("tab test", () => {
let targetWindow;
beforeEach(async () => {
targetWindow = await windows.create(CLIENT_URL);
});
afterEach(async () => {
await windows.remove(targetWindow.id);
});
it('deletes tab by d', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL);
let before = await windows.get(targetWindow.id);
await keys.press(tab.id, 'd');
let actual = await windows.get(targetWindow.id);
expect(actual.tabs).to.have.lengthOf(before.tabs.length - 1);
});
it('deletes tabs to the right by D', async () => {
let tab1 = await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let before = await windows.get(targetWindow.id)
let tab = await tabs.selectAt(targetWindow.id, tab1.index)
await keys.press(tab.id, 'D', { shiftKey: true });
let actual = await windows.get(targetWindow.id);
expect(actual.tabs).to.have.lengthOf(before.tabs.length - 2);
});
it('duplicates tab by zd', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL);
let before = await windows.get(targetWindow.id)
await keys.press(tab.id, 'z');
await keys.press(tab.id, 'd');
let actual = await windows.get(targetWindow.id);
expect(actual.tabs).to.have.lengthOf(before.tabs.length + 1);
});
it('makes pinned by zp', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL);
let before = await windows.get(targetWindow.id);
await keys.press(tab.id, 'z');
await keys.press(tab.id, 'p');
let actual = await windows.get(targetWindow.id);
expect(actual.tabs[0].pinned).to.be.true;
});
it('selects previous tab by K', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let tab = await tabs.selectAt(targetWindow.id, 2);
await keys.press(tab.id, 'K', { shiftKey: true });
let win = await windows.get(targetWindow.id);
expect(win.tabs[1].active).to.be.true;
});
it('selects previous tab by K rotatory', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let tab = await tabs.selectAt(targetWindow.id, 0);
await keys.press(tab.id, 'K', { shiftKey: true });
let win = await windows.get(targetWindow.id);
expect(win.tabs[3].active).to.be.true;
});
it('selects next tab by J', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let tab = await tabs.selectAt(targetWindow.id, 2);
await keys.press(tab.id, 'J', { shiftKey: true });
let win = await windows.get(targetWindow.id);
expect(win.tabs[3].active).to.be.true;
});
it('selects previous tab by J rotatory', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let tab = await tabs.selectAt(targetWindow.id, 3);
await keys.press(tab.id, 'J', { shiftKey: true });
let win = await windows.get(targetWindow.id);
expect(win.tabs[0].active).to.be.true;
});
it('selects first tab by g0', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let tab = await tabs.selectAt(targetWindow.id, 2);
await keys.press(tab.id, 'g');
await keys.press(tab.id, '0');
let win = await windows.get(targetWindow.id);
expect(win.tabs[0].active).to.be.true;
});
it('selects last tab by g$', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
let tab = await tabs.selectAt(targetWindow.id, 2);
await keys.press(tab.id, 'g');
await keys.press(tab.id, '$');
let win = await windows.get(targetWindow.id);
expect(win.tabs[3].active).to.be.true;
});
it('selects last selected tab by <C-6>', async () => {
await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await tabs.create(targetWindow.id, CLIENT_URL + '#2');
await tabs.create(targetWindow.id, CLIENT_URL + '#3');
await tabs.selectAt(targetWindow.id, 1);
let tab = await tabs.selectAt(targetWindow.id, 3);
await keys.press(tab.id, '6', { ctrlKey: true });
let win = await windows.get(targetWindow.id);
expect(win.tabs[1].active).to.be.true;
});
it('deletes tab by d', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await keys.press(tab.id, 'd');
let win = await windows.get(targetWindow.id);
expect(win.tabs).to.have.lengthOf(1);
});
it('reopen tab by u', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '#1');
await keys.press(tab.id, 'd');
let win = await windows.get(targetWindow.id);
expect(win.tabs).to.have.lengthOf(1);
await keys.press(win.tabs[0].id, 'u');
await new Promise(resolve => setTimeout(resolve, 100));
win = await windows.get(targetWindow.id);
expect(win.tabs).to.have.lengthOf(2);
});
it('does not delete pinned tab by d', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '#1');
tab = await tabs.update(tab.id, { pinned: true });
await keys.press(tab.id, 'd');
let win = await windows.get(targetWindow.id);
expect(win.tabs).to.have.lengthOf(2);
});
it('deletes pinned tab by !d', async () => {
let tab = await tabs.create(targetWindow.id, CLIENT_URL + '#1');
tab = await tabs.update(tab.id, { pinned: true });
await keys.press(tab.id, '!');
await keys.press(tab.id, 'd');
let win = await windows.get(targetWindow.id);
expect(win.tabs).to.have.lengthOf(1);
});
it('opens view-source by gf', async () => {
await new Promise(resolve => setTimeout(resolve, 100));
let win = await windows.get(targetWindow.id);
let tab = win.tabs[0];
await keys.press(tab.id, 'g');
await keys.press(tab.id, 'f');
await new Promise(resolve => setTimeout(resolve, 500));
win = await windows.get(targetWindow.id);
let urls = win.tabs.map((t) => t.url)
expect(urls).to.include.members([CLIENT_URL + '/', 'view-source:' + CLIENT_URL + '/']);
});
});

@ -1,53 +0,0 @@
import * as windows from "../ambassador/src/client/windows";
import * as tabs from "../ambassador/src/client/tabs";
import * as keys from "../ambassador/src/client/keys";
import { CLIENT_URL } from '../web-server/url';
describe("zoom test", () => {
let targetWindow;
let targetTab;
before(async () => {
targetWindow = await windows.create(CLIENT_URL);
});
after(async () => {
await windows.remove(targetWindow.id);
});
beforeEach(async () => {
targetTab = await tabs.create(targetWindow.id, CLIENT_URL);
});
it('zooms-in by zi', async () => {
let before = await tabs.getZoom(targetTab.id);
await keys.press(targetTab.id, 'z');
await keys.press(targetTab.id, 'i');
await new Promise(resolve => setTimeout(resolve, 100));
let actual = await tabs.getZoom(targetTab.id);
expect(actual).to.be.greaterThan(before);
});
it('zooms-in by zo', async () => {
let before = await tabs.getZoom(targetTab.id);
await keys.press(targetTab.id, 'z');
await keys.press(targetTab.id, 'o');
await new Promise(resolve => setTimeout(resolve, 100));
let actual = await tabs.getZoom(targetTab.id);
expect(actual).to.be.lessThan(before);
});
it('zooms-in by zz', async () => {
await tabs.setZoom(targetTab.id, 1.5);
let before = await tabs.getZoom(targetTab.id);
await keys.press(targetTab.id, 'z');
await keys.press(targetTab.id, 'z');
await new Promise(resolve => setTimeout(resolve, 100));
let actual = await tabs.getZoom(targetTab.id);
expect(actual).to.be.lessThan(before);
expect(actual).to.equal(1);
});
});

@ -0,0 +1,23 @@
let defaultInterval = 100;
let defaultTimeout = 2000;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const eventually = async (fn, timeout = defaultTimeout, interval = defaultInterval) => {
let start = Date.now();
let loop = async() => {
try {
await fn();
} catch (err) {
if (Date.now() - start > timeout) {
throw err;
}
await new Promise((resolve) => setTimeout(resolve, interval))
await loop();
}
};
await loop();
};
module.exports = eventually;

@ -1,10 +0,0 @@
'use strict';
window.__karma__.start = (function(start){
return function(){
var args = arguments
setTimeout(() => {
start(args)
}, 3000);
};
}(window.__karma__.start));

@ -1,53 +0,0 @@
'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],
}

@ -1,53 +0,0 @@
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['mocha'],
files: [
'main.js',
'karma-delay.js',
'**/*.test.js'
],
preprocessors: {
'**/main.js': ['webpack'],
'**/*.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',
],
client: {
mocha: {
timeout: 5000
}
}
})
}

@ -1,2 +0,0 @@
import chai from 'chai';
global.expect = chai.expect;

@ -0,0 +1,121 @@
const express = require('express');
const lanthan = require('lanthan');
const path = require('path');
const assert = require('assert');
const eventually = require('./eventually');
const Key = lanthan.Key;
const newApp = () => {
let app = express();
app.get('/', (req, res) => {
res.send(`<!DOCTYPEhtml>
<html lang="en">
<body style="width:10000px; height:10000px"></body>
</html">`);
});
return app;
};
describe("mark test", () => {
const port = 12321;
let http;
let firefox;
let session;
let browser;
before(async() => {
http = newApp().listen(port);
firefox = await lanthan.firefox();
await firefox.session.installAddon(path.join(__dirname, '..'));
session = firefox.session;
browser = firefox.browser;
});
after(async() => {
if (firefox) {
await firefox.close();
}
http.close();
});
it('should set a local mark and jump to it', async () => {
await session.navigateTo(`http://127.0.0.1:${port}`);
let body = await session.findElementByCSS('body');
await session.executeScript(() => window.scrollTo(200, 200));
await body.sendKeys('m', 'a');
await session.executeScript(() => window.scrollTo(500, 500));
await body.sendKeys('\'', 'a');
await eventually(async() => {
let pageXOffset = await session.executeScript(() => window.pageXOffset);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert.equal(pageXOffset, 200);
assert.equal(pageYOffset, 200);
});
});
it('should set a global mark and jump to it', async () => {
await session.navigateTo(`http://127.0.0.1:${port}#first`);
let body = await session.findElementByCSS('body');
await session.executeScript(() => window.scrollTo(200, 200));
await body.sendKeys('m', 'A');
await session.executeScript(() => window.scrollTo(500, 500));
await body.sendKeys('\'', 'A');
await eventually(async() => {
let pageXOffset = await session.executeScript(() => window.pageXOffset);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert.equal(pageXOffset, 200);
assert.equal(pageYOffset, 200);
});
await browser.tabs.create({ url: `http://127.0.0.1:${port}#second` });
body = await session.findElementByCSS('body');
await body.sendKeys('\'', 'A');
await eventually(async() => {
let tab = (await browser.tabs.query({ active: true }))[0];
let url = new URL(tab.url);
assert.equal(url.hash, '#first');
let pageXOffset = await session.executeScript(() => window.pageXOffset);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert.equal(pageXOffset, 200);
assert.equal(pageYOffset, 200);
});
});
it('set a global mark and creates new tab from gone', async () => {
await session.navigateTo(`http://127.0.0.1:${port}#first`);
await session.executeScript(() => window.scrollTo(500, 500));
let body = await session.findElementByCSS('body');
await body.sendKeys('m', 'A');
let tab = (await browser.tabs.query({ active: true }))[0];
await browser.tabs.create({ url: `http://127.0.0.1:${port}#second` });
await browser.tabs.remove(tab.id);
let handles;
await eventually(async() => {
handles = await session.getWindowHandles();
assert.equal(handles.length, 2);
});
await session.switchToWindow(handles[0]);
await session.navigateTo(`http://127.0.0.1:${port}#second`);
body = await session.findElementByCSS('body');
await body.sendKeys('\'', 'A');
await eventually(async() => {
let tab = (await browser.tabs.query({ active: true }))[0];
let url = new URL(tab.url);
assert.equal(url.hash, '#first');
});
});
});

@ -0,0 +1,167 @@
const express = require('express');
const lanthan = require('lanthan');
const path = require('path');
const assert = require('assert');
const eventually = require('./eventually');
const Key = lanthan.Key;
const newApp = () => {
let app = express();
app.get('/pagenation-a/:page', (req, res) => {
res.status(200).send(`
<html lang="en">
<a href="/pagenation-a/${Number(req.params.page) - 1}">prev</a>
<a href="/pagenation-a/${Number(req.params.page) + 1}">next</a>
</html">`);
});
app.get('/pagenation-link/:page', (req, res) => {
res.status(200).send(`
<html lang="en">
<head>
<link rel="prev" href="/pagenation-link/${Number(req.params.page) - 1}"></link>
<link rel="next" href="/pagenation-link/${Number(req.params.page) + 1}"></link>
</head>
</html">`);
});
app.get('/*', (req, res) => {
res.send(`<!DOCTYPEhtml>
<html lang="en">
${req.path}
</html">`);
});
return app;
};
describe("zoom test", () => {
const port = 12321;
let http;
let firefox;
let session;
let browser;
before(async() => {
http = newApp().listen(port);
firefox = await lanthan.firefox();
await firefox.session.installAddon(path.join(__dirname, '..'));
session = firefox.session;
browser = firefox.browser;
});
after(async() => {
if (firefox) {
await firefox.close();
}
http.close();
});
it('should go to parent path without hash by gu', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/a/b/c`);
let body = await session.findElementByCSS('body');
await body.sendKeys('g', 'u');
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, `/a/b/`)
});
it('should remove hash by gu', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/a/b/c#hash`);
let body = await session.findElementByCSS('body');
await body.sendKeys('g', 'u');
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.hash, '')
assert.equal(url.pathname, `/a/b/c`)
});
it('should go to root path by gU', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/a/b/c#hash`);
let body = await session.findElementByCSS('body');
await body.sendKeys('g', Key.Shift, 'u');
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, `/`)
});
});
it('should go back and forward in history by H and L', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/first`);
await session.navigateTo(`http://127.0.0.1:${port}/second`);
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'h');
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, `/first`)
body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'l');
tab = (await browser.tabs.query({}))[0];
url = new URL(tab.url);
assert.equal(url.pathname, `/second`)
});
it('should go previous and next page in <a> by [[ and ]]', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/pagenation-a/10`);
let body = await session.findElementByCSS('body');
await body.sendKeys('[', '[');
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, '/pagenation-a/9');
});
});
it('should go next page in <a> by ]]', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/pagenation-a/10`);
let body = await session.findElementByCSS('body');
await body.sendKeys(']', ']');
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, '/pagenation-a/11');
});
});
it('should go previous page in <link> by ]]', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/pagenation-link/10`);
let body = await session.findElementByCSS('body');
await body.sendKeys('[', '[');
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, '/pagenation-link/9');
});
});
it('should go next page by in <link> by [[', async () => {
await session.navigateTo(`http://127.0.0.1:${port}/pagenation-link/10`);
let body = await session.findElementByCSS('body');
await body.sendKeys(']', ']');
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
assert.equal(url.pathname, '/pagenation-link/11');
});
});
});

@ -0,0 +1,150 @@
const express = require('express');
const lanthan = require('lanthan');
const path = require('path');
const assert = require('assert');
const Key = lanthan.Key;
const newApp = () => {
let app = express();
app.get('/', (req, res) => {
res.send(`<!DOCTYPEhtml>
<html lang="en">
<body style="width:10000px; height:10000px"></body>
</html">`);
});
return app;
};
describe("scroll test", () => {
const port = 12321;
let http;
let firefox;
let session;
let body;
before(async() => {
http = newApp().listen(port);
firefox = await lanthan.firefox();
await firefox.session.installAddon(path.join(__dirname, '..'));
session = firefox.session;
});
after(async() => {
if (firefox) {
await firefox.close();
}
http.close();
});
beforeEach(async() => {
await session.navigateTo(`http://127.0.0.1:${port}`);
body = await session.findElementByCSS('body');
});
it('scrolls up by k', async () => {
await body.sendKeys('j');
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert.equal(pageYOffset, 64);
});
it('scrolls down by j', async () => {
await session.executeScript(() => window.scrollTo(0, 200));
await body.sendKeys('k');
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert.equal(pageYOffset, 136);
});
it('scrolls left by h', async () => {
await session.executeScript(() => window.scrollTo(100, 100));
await body.sendKeys('h');
let pageXOffset = await session.executeScript(() => window.pageXOffset);
assert.equal(pageXOffset, 36);
});
it('scrolls left by l', async () => {
await session.executeScript(() => window.scrollTo(100, 100));
await body.sendKeys('l');
let pageXOffset = await session.executeScript(() => window.pageXOffset);
assert.equal(pageXOffset, 164);
});
it('scrolls top by gg', async () => {
await session.executeScript(() => window.scrollTo(0, 100));
await body.sendKeys('g', 'g');
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert.equal(pageYOffset, 0);
});
it('scrolls bottom by G', async () => {
await session.executeScript(() => window.scrollTo(0, 100));
await body.sendKeys(Key.Shift, 'g');
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert(pageYOffset > 5000);
});
it('scrolls bottom by 0', async () => {
await session.executeScript(() => window.scrollTo(0, 100));
await body.sendKeys(Key.Shift, '0');
let pageXOffset = await session.executeScript(() => window.pageXOffset);
assert(pageXOffset === 0);
});
it('scrolls bottom by $', async () => {
await session.executeScript(() => window.scrollTo(0, 100));
await body.sendKeys(Key.Shift, '$');
let pageXOffset = await session.executeScript(() => window.pageXOffset);
assert(pageXOffset > 5000);
});
it('scrolls bottom by <C-U>', async () => {
await session.executeScript(() => window.scrollTo(0, 1000));
await body.sendKeys(Key.Control, 'u');
let pageHeight =
await session.executeScript(() => window.document.documentElement.clientHeight);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert(Math.abs(pageYOffset - (1000 - Math.floor(pageHeight / 2))) < 5);
});
it('scrolls bottom by <C-D>', async () => {
await session.executeScript(() => window.scrollTo(0, 1000));
await body.sendKeys(Key.Control, 'd');
let pageHeight =
await session.executeScript(() => window.document.documentElement.clientHeight);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert(Math.abs(pageYOffset - (1000 + Math.floor(pageHeight / 2))) < 5);
});
it('scrolls bottom by <C-B>', async () => {
await session.executeScript(() => window.scrollTo(0, 1000));
await body.sendKeys(Key.Control, 'b');
let pageHeight =
await session.executeScript(() => window.document.documentElement.clientHeight);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert(Math.abs(pageYOffset - (1000 - pageHeight)) < 5);
});
it('scrolls bottom by <C-F>', async () => {
await session.executeScript(() => window.scrollTo(0, 1000));
await body.sendKeys(Key.Control, 'f');
let pageHeight =
await session.executeScript(() => window.document.documentElement.clientHeight);
let pageYOffset = await session.executeScript(() => window.pageYOffset);
assert(Math.abs(pageYOffset - (1000 + pageHeight)) < 5);
});
});

@ -0,0 +1,202 @@
const express = require('express');
const lanthan = require('lanthan');
const path = require('path');
const assert = require('assert');
const eventually = require('./eventually');
const Key = lanthan.Key;
const newApp = () => {
let app = express();
app.get('/', (req, res) => {
res.send('ok');
});
return app;
};
describe("tab test", () => {
const port = 12321;
const url = `http://127.0.0.1:${port}/`;
let http;
let firefox;
let session;
let browser;
let win;
let tabs;
before(async() => {
firefox = await lanthan.firefox();
await firefox.session.installAddon(path.join(__dirname, '..'));
session = firefox.session;
browser = firefox.browser;
http = newApp().listen(port);
});
after(async() => {
http.close();
if (firefox) {
await firefox.close();
}
});
beforeEach(async() => {
win = await browser.windows.create({ url: `${url}#0` });
for (let i = 1; i < 5; ++i) {
await browser.tabs.create({ url: `${url}#${i}`, windowId: win.id });
await session.navigateTo(`${url}#${i}`);
}
tabs = await browser.tabs.query({ windowId: win.id });
tabs.sort((t1, t2) => t1.index - t2.index);
});
afterEach(async() => {
await browser.windows.remove(win.id);
});
it('deletes tab by d', async () => {
let body = await session.findElementByCSS('body');
await body.sendKeys('d');
let current = await browser.tabs.query({ windowId: win.id });
assert(current.length === tabs.length - 1);
});
it('deletes tabs to the right by D', async () => {
await browser.tabs.update(tabs[1].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'd');
let current = await browser.tabs.query({ windowId: win.id });
assert(current.length === 2);
});
it('duplicates tab by zd', async () => {
await browser.tabs.update(tabs[0].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('z', 'd');
await eventually(async() => {
let current = await browser.tabs.query({ windowId: win.id });
current.sort((t1, t2) => t1.index - t2.index);
assert(current.length === tabs.length + 1);
assert(current[0].url === current[1].url);
});
});
it('makes pinned by zp', async () => {
await browser.tabs.update(tabs[0].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('z', 'p');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[0].pinned);
});
it('selects previous tab by K', async () => {
await browser.tabs.update(tabs[2].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'K');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[1].active);
});
it('selects previous tab by K rotatory', async () => {
await browser.tabs.update(tabs[0].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'K');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[current.length - 1].active)
});
it('selects next tab by J', async () => {
await browser.tabs.update(tabs[2].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'J');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[3].active);
});
it('selects previous tab by J rotatory', async () => {
await browser.tabs.update(tabs[tabs.length - 1].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Shift, 'J');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[0].active)
});
it('selects first tab by g0', async () => {
await browser.tabs.update(tabs[2].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('g', '0');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[0].active)
});
it('selects last tab by g$', async () => {
await browser.tabs.update(tabs[2].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('g', '$');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[current.length - 1].active)
});
it('selects last selected tab by <C-6>', async () => {
await browser.tabs.update(tabs[1].id, { active: true });
await browser.tabs.update(tabs[4].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys(Key.Control, '6');
let current = await browser.tabs.query({ windowId: win.id });
assert(current[1].active)
});
// browser.sessions.getRecentlyClosed() sometime throws "An unexpected error occurred"
// This might be a bug in Firefox.
it.skip('reopen tab by u', async () => {
await browser.tabs.remove(tabs[1].id);
let body = await session.findElementByCSS('body');
await body.sendKeys('u');
let current = await browser.tabs.query({ windowId: win.id });
assert(current.length === tabs.length);
});
it('does not delete pinned tab by d', async () => {
await browser.tabs.update(tabs[0].id, { active: true, pinned: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('d');
let current = await browser.tabs.query({ windowId: win.id });
assert(current.length === tabs.length);
});
it('deletes pinned tab by !d', async () => {
await browser.tabs.update(tabs[0].id, { active: true, pinned: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('!', 'd');
let current = await browser.tabs.query({ windowId: win.id });
assert(current.length === tabs.length - 1);
});
it('opens view-source by gf', async () => {
await browser.tabs.update(tabs[0].id, { active: true });
let body = await session.findElementByCSS('body');
await body.sendKeys('g', 'f');
await eventually(async() => {
let current = await browser.tabs.query({ windowId: win.id });
assert(current.length === tabs.length + 1);
assert(current[current.length - 1].url === `view-source:${url}#0`);
});
});
});

@ -1,88 +0,0 @@
'use strict';
var serverUrl = require('./url');
var http = require('http');
var url = require('url');
const handleScroll = (req, res) => {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<!DOCTYPEhtml><html lang="en"><body style="width:10000px; height:10000px"></body></html">');
};
const handleAPagenation = (req, res) => {
let u = url.parse(req.url);
let params = new url.URLSearchParams(u.search);
let page = params.get('page') === null ? null : Number(params.get('page'));
if (page === null || isNaN(page)) {
return handle404(req, res);
}
let body = '';
let nextLink = u.pathname + '?page=' + (page + 1);
let prevLink = u.pathname + '?page=' + (page - 1);
if (page > 1) {
body += '<a href="' + prevLink + '">prev</a> | ';
}
body += '<a href="' + nextLink + '">next</a>';
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<!DOCTYPEhtml><html lang="en"><body">' + body + '</body></html">');
};
const handleLinkPagenation = (req, res) => {
let u = url.parse(req.url);
let params = new url.URLSearchParams(u.search);
let page = params.get('page') === null ? null : Number(params.get('page'));
if (page === null || isNaN(page)) {
return handle404(req, res);
}
let head = '';
let nextLink = u.pathname + '?page=' + (page + 1);
let prevLink = u.pathname + '?page=' + (page - 1);
if (page > 1) {
head += '<link rel="prev" href="' + prevLink + '"></link>';
}
head += '<link rel="next" href="' + nextLink + '"></link>';
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<!DOCTYPEhtml><html lang="en"><head>' + head + '</head><body"></body></html">');
};
const handleFollow = (req, res) => {
let body = '';
body += '<a href="#a">a</a>';
body += '<a href="#external" target="_blank">external</a>';
body += '<img width="320" height="240" src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" usemap="#map"><map name="map"><area href="#area" shape="rect" coords="15,19,126,104"></map>'
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<!DOCTYPEhtml><html lang="en"><body">' + body + '</body></html">');
}
const handle404 = (req, res) => {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('not found')
};
http.createServer(function (req, res) {
if (req.method !== 'GET') {
handle404(req, res);
}
let u = url.parse(req.url);
if (u.pathname === '/scroll' || u.pathname === '/mark') {
handleScroll(req, res);
} else if (u.pathname === '/a-pagenation') {
handleAPagenation(req, res);
} else if (u.pathname === '/link-pagenation') {
handleLinkPagenation(req, res);
} else if (u.pathname === '/follow') {
handleFollow(req, res);
} else {
handle404(req, res);
}
console.log(`"${req.method} ${req.url}"`, res.statusCode)
}).listen(serverUrl.PORT, serverUrl.HOST);

@ -1,5 +0,0 @@
module.exports = {
PORT: 11111,
HOST: '127.0.0.1',
CLIENT_URL: 'http://127.0.0.1:11111',
}

@ -0,0 +1,81 @@
const express = require('express');
const lanthan = require('lanthan');
const path = require('path');
const assert = require('assert');
const eventually = require('./eventually');
const Key = lanthan.Key;
const newApp = () => {
let app = express();
app.get('/', (req, res) => {
res.send(`<!DOCTYPEhtml>
<html lang="en">
</html">`);
});
return app;
};
describe("zoom test", () => {
const port = 12321;
let http;
let firefox;
let session;
let browser;
let tab;
let body;
before(async() => {
http = newApp().listen(port);
firefox = await lanthan.firefox();
await firefox.session.installAddon(path.join(__dirname, '..'));
session = firefox.session;
browser = firefox.browser;
tab = (await browser.tabs.query({}))[0]
});
after(async() => {
if (firefox) {
await firefox.close();
}
http.close();
});
beforeEach(async() => {
await session.navigateTo(`http://127.0.0.1:${port}`);
body = await session.findElementByCSS('body');
});
it('should zoom in by zi', async () => {
let before = await browser.tabs.getZoom(tab.id);
await body.sendKeys('z', 'i');
await eventually(async() => {
let actual = await browser.tabs.getZoom(tab.id);
assert(before < actual);
});
});
it('should zoom out by zo', async () => {
let before = await browser.tabs.getZoom(tab.id);
await body.sendKeys('z', 'o');
await eventually(async() => {
let actual = await browser.tabs.getZoom(tab.id);
assert(before > actual);
});
});
it('scrolls left by h', async () => {
await browser.tabs.setZoom(tab.id, 2);
await body.sendKeys('z', 'z');
await eventually(async() => {
let actual = await browser.tabs.getZoom(tab.id);
assert(actual === 1);
});
});
});

1362
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -5,11 +5,9 @@
"start": "webpack --mode development -w --debug --devtool inline-source-map",
"build": "NODE_ENV=production webpack --mode production --progress --display-error-details",
"package": "npm run build && script/package.sh",
"lint": "eslint --ext .jsx,.js src e2e/ambassador/src",
"lint": "eslint --ext .jsx,.js src",
"test": "karma start",
"test:e2e": "karma start e2e/karma.conf.js",
"ambassador:start": "webpack --mode production -w --debug --context e2e/ambassador --config e2e/ambassador/webpack.config.js --devtool inline-source-map",
"ambassador:build": "webpack --mode production --context e2e/ambassador --config e2e/ambassador/webpack.config.js"
"test:e2e": "mocha --timeout 5000 e2e"
},
"repository": {
"type": "git",
@ -40,6 +38,7 @@
"karma-sinon": "^1.0.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.5",
"lanthan": "git+https://github.com/ueokande/lanthan.git#master",
"mocha": "^6.0.2",
"node-sass": "^4.11.0",
"preact": "^8.4.2",