commit
9f1da8b9e4
10 changed files with 279 additions and 61 deletions
6
QA.md
6
QA.md
|
@ -6,12 +6,6 @@ Test operations with default key maps.
|
||||||
|
|
||||||
#### Scrolling
|
#### Scrolling
|
||||||
|
|
||||||
- [ ] <kbd>k</kbd>, <kbd>j</kbd>: scroll up and down
|
|
||||||
- [ ] <kbd>h</kbd>, <kbd>l</kbd>: scroll left and right
|
|
||||||
- [ ] <kbd>Ctrl</kbd>+<kbd>U</kbd>, <kbd>Ctrl</kbd>+<kbd>D</kbd>: scroll up and down by half of screen
|
|
||||||
- [ ] <kbd>Ctrl</kbd>+<kbd>B</kbd>, <kbd>Ctrl</kbd>+<kbd>F</kbd>: scroll up and down by a screen
|
|
||||||
- [ ] <kbd>0</kbd>, <kbd>$</kbd>: scroll to leftmost and rightmost
|
|
||||||
- [ ] <kbd>g</kbd><kbd>g</kbd>, <kbd>G</kbd>: scroll to top and bottom
|
|
||||||
- [ ] Smooth scroll by `:set smoothscroll`
|
- [ ] Smooth scroll by `:set smoothscroll`
|
||||||
- [ ] Non-smooth scroll by `:set nosmoothscroll`
|
- [ ] Non-smooth scroll by `:set nosmoothscroll`
|
||||||
- [ ] Configure custom hint character by settings `"smoothscroll": true`, `"smoothscroll": false`
|
- [ ] Configure custom hint character by settings `"smoothscroll": true`, `"smoothscroll": false`
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET,
|
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET,
|
||||||
TABS_CREATE,
|
TABS_CREATE,
|
||||||
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
|
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
|
||||||
|
SCROLL_GET, SCROLL_SET,
|
||||||
} from '../shared/messages';
|
} from '../shared/messages';
|
||||||
import * as tabs from './tabs';
|
import * as tabs from './tabs';
|
||||||
import { receiveContentMessage } from './ipc';
|
import { receiveContentMessage } from './ipc';
|
||||||
|
@ -19,15 +20,11 @@ receiveContentMessage((message) => {
|
||||||
url: message.url,
|
url: message.url,
|
||||||
windowId: message.windowId,
|
windowId: message.windowId,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
receiveContentMessage((message) => {
|
|
||||||
switch (message.type) {
|
|
||||||
case EVENT_KEYPRESS:
|
case EVENT_KEYPRESS:
|
||||||
case EVENT_KEYDOWN:
|
case EVENT_KEYDOWN:
|
||||||
case EVENT_KEYUP:
|
case EVENT_KEYUP:
|
||||||
|
case SCROLL_GET:
|
||||||
|
case SCROLL_SET:
|
||||||
return browser.tabs.sendMessage(
|
return browser.tabs.sendMessage(
|
||||||
message.tabId,
|
message.tabId,
|
||||||
message
|
message
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
import { EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP } from '../shared/messages';
|
import { EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP } from '../shared/messages';
|
||||||
import * as ipc from './ipc';
|
import * as ipc from './ipc';
|
||||||
|
|
||||||
const press = (tabId, key) => {
|
const NEUTRAL_MODIFIERS = { shiftKey: false, altKey: false, ctrlKey: false };
|
||||||
return ipc.send({
|
|
||||||
|
const press = (tabId, key, modifiers = NEUTRAL_MODIFIERS) => {
|
||||||
|
return ipc.send(Object.assign({}, modifiers, {
|
||||||
type: EVENT_KEYPRESS,
|
type: EVENT_KEYPRESS,
|
||||||
tabId,
|
tabId,
|
||||||
key,
|
key,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const down = (tabId, key) => {
|
const down = (tabId, key, modifiers = NEUTRAL_MODIFIERS) => {
|
||||||
return ipc.send({
|
return ipc.send(Object.assign({}, modifiers, {
|
||||||
type: EVENT_KEYDOWN,
|
type: EVENT_KEYDOWN,
|
||||||
tabId,
|
tabId,
|
||||||
key,
|
key,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const up = (tabId, key) => {
|
const up = (tabId, key, modifiers = NEUTRAL_MODIFIERS) => {
|
||||||
return ipc.send({
|
return ipc.send(Object.assign({}, modifiers, {
|
||||||
type: EVENT_KEYUP,
|
type: EVENT_KEYUP,
|
||||||
tabId,
|
tabId,
|
||||||
key,
|
key,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export { press, down, up };
|
export { press, down, up };
|
||||||
|
|
20
e2e/ambassador/src/client/scrolls.js
Normal file
20
e2e/ambassador/src/client/scrolls.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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 };
|
31
e2e/ambassador/src/content/events.js
Normal file
31
e2e/ambassador/src/content/events.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
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,37 +1,30 @@
|
||||||
import {
|
import {
|
||||||
WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET,
|
|
||||||
TABS_CREATE,
|
|
||||||
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
|
EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
|
||||||
|
SCROLL_GET, SCROLL_SET,
|
||||||
} from '../shared/messages';
|
} from '../shared/messages';
|
||||||
import * as ipc from './ipc';
|
import * as ipc from './ipc';
|
||||||
|
import * as events from './events';
|
||||||
|
import * as scrolls from './scrolls';
|
||||||
|
|
||||||
ipc.receivePageMessage((message) => {
|
ipc.receivePageMessage((message) => {
|
||||||
switch (message.type) {
|
return ipc.sendToBackground(message);
|
||||||
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) => {
|
ipc.receiveBackgroundMesssage((message) => {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case EVENT_KEYPRESS:
|
case EVENT_KEYPRESS:
|
||||||
document.body.dispatchEvent(
|
events.keypress(message);
|
||||||
new KeyboardEvent('keypress', { 'key': message.key }));
|
|
||||||
break;
|
break;
|
||||||
case EVENT_KEYDOWN:
|
case EVENT_KEYDOWN:
|
||||||
document.body.dispatchEvent(
|
events.keydown(message);
|
||||||
new KeyboardEvent('keydown', { 'key': message.key }));
|
|
||||||
break;
|
break;
|
||||||
case EVENT_KEYUP:
|
case EVENT_KEYUP:
|
||||||
document.body.dispatchEvent(
|
events.keyup(message);
|
||||||
new KeyboardEvent('keyup', { 'key': message.key }));
|
|
||||||
break;
|
break;
|
||||||
|
case SCROLL_GET:
|
||||||
|
return Promise.resolve(scrolls.get());
|
||||||
|
case SCROLL_SET:
|
||||||
|
return Promise.resolve(scrolls.set(message.x, message.y));
|
||||||
}
|
}
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
});
|
});
|
||||||
|
|
20
e2e/ambassador/src/content/scrolls.js
Normal file
20
e2e/ambassador/src/content/scrolls.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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 };
|
|
@ -7,6 +7,8 @@ const TABS_CREATE = 'tabs.create';
|
||||||
const EVENT_KEYPRESS = 'event.keypress';
|
const EVENT_KEYPRESS = 'event.keypress';
|
||||||
const EVENT_KEYDOWN = 'event.keydown';
|
const EVENT_KEYDOWN = 'event.keydown';
|
||||||
const EVENT_KEYUP = 'event.keyup';
|
const EVENT_KEYUP = 'event.keyup';
|
||||||
|
const SCROLL_GET = 'scroll.get';
|
||||||
|
const SCROLL_SET = 'scroll.set';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
METHOD_REQUEST,
|
METHOD_REQUEST,
|
||||||
|
@ -21,4 +23,6 @@ export {
|
||||||
EVENT_KEYPRESS,
|
EVENT_KEYPRESS,
|
||||||
EVENT_KEYDOWN,
|
EVENT_KEYDOWN,
|
||||||
EVENT_KEYUP,
|
EVENT_KEYUP,
|
||||||
|
SCROLL_GET,
|
||||||
|
SCROLL_SET,
|
||||||
};
|
};
|
||||||
|
|
151
e2e/contents/scroll.test.js
Normal file
151
e2e/contents/scroll.test.js
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
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";
|
||||||
|
import * as scrolls from "../ambassador/src/client/scrolls";
|
||||||
|
|
||||||
|
const SERVER_URL = "localhost:11111";
|
||||||
|
|
||||||
|
describe("scroll test", () => {
|
||||||
|
let targetWindow;
|
||||||
|
let targetTab;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
return windows.create().then((win) => {
|
||||||
|
targetWindow = win;
|
||||||
|
return tabs.create(targetWindow.id, SERVER_URL);
|
||||||
|
}).then((tab) => {
|
||||||
|
targetTab = tab;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
return windows.remove(targetWindow.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls up by k', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'k');
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.be.lessThan(before.y);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls down by j', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'j');
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.be.greaterThan(before.y);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls left by h', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'h');
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.x).to.be.lessThan(before.x);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls top by gg', () => {
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
return keys.press(targetTab.id, 'g');
|
||||||
|
}).then(() => {
|
||||||
|
return keys.press(targetTab.id, 'g');
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.be.equals(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by G', () => {
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
return keys.press(targetTab.id, 'G', { shiftKey: true });
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.be.equals(actual.yMax);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by 0', () => {
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
return keys.press(targetTab.id, '0');
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.x).to.be.equals(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by $', () => {
|
||||||
|
return scrolls.set(targetTab.id, 100, 100).then((scroll) => {
|
||||||
|
return keys.press(targetTab.id, '$');
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.x).to.be.equals(actual.xMax);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by <C-U>', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 5000, 5000).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'u', { ctrlKey: true });
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.closeTo(before.y - before.frameHeight / 2, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by <C-D>', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 5000, 5000).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'd', { ctrlKey: true });
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.closeTo(before.y + before.frameHeight / 2, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by <C-B>', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 5000, 5000).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'b', { ctrlKey: true });
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.equals(before.y - before.frameHeight);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls bottom by <C-F>', () => {
|
||||||
|
let before
|
||||||
|
return scrolls.set(targetTab.id, 5000, 5000).then((scroll) => {
|
||||||
|
before = scroll;
|
||||||
|
return keys.press(targetTab.id, 'f', { ctrlKey: true });
|
||||||
|
}).then(() => {
|
||||||
|
return scrolls.get(targetTab.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.y).to.equals(before.y + before.frameHeight);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -18,31 +18,37 @@ describe("tab test", () => {
|
||||||
return windows.remove(targetWindow.id);
|
return windows.remove(targetWindow.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('press d', () => {
|
it('deletes tab by d', () => {
|
||||||
it('deletes tab', () => {
|
let before;
|
||||||
return tabs.create(targetWindow.id, SERVER_URL).then((tab) => {
|
let targetTab;
|
||||||
return keys.press(tab.id, 'd');
|
return tabs.create(targetWindow.id, SERVER_URL).then((tab) => {
|
||||||
}).then(() => {
|
targetTab = tab;
|
||||||
return windows.get(targetWindow.id);
|
return windows.get(targetWindow.id);
|
||||||
}).then((after) => {
|
}).then((win) => {
|
||||||
expect(after.tabs).to.have.lengthOf(1);
|
before = win;
|
||||||
});
|
return keys.press(targetTab.id, 'd');
|
||||||
|
}).then(() => {
|
||||||
|
return windows.get(targetWindow.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.tabs).to.have.lengthOf(before.tabs.length - 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('press zd', () => {
|
it('duplicates tab by zd', () => {
|
||||||
it('duplicates tab', () => {
|
let before;
|
||||||
let targetTab = 0;
|
let targetTab;
|
||||||
return tabs.create(targetWindow.id, SERVER_URL).then((tab) => {
|
return tabs.create(targetWindow.id, SERVER_URL).then((tab) => {
|
||||||
targetTab = tab;
|
targetTab = tab;
|
||||||
return keys.press(targetTab.id, 'z');
|
return windows.get(targetWindow.id)
|
||||||
}).then(() => {
|
}).then((win) => {;
|
||||||
return keys.press(targetTab.id, 'd');
|
before = win;
|
||||||
}).then(() => {
|
return keys.press(targetTab.id, 'z');
|
||||||
return windows.get(targetWindow.id);
|
}).then(() => {
|
||||||
}).then((after) => {
|
return keys.press(targetTab.id, 'd');
|
||||||
expect(after.tabs).to.have.lengthOf(3);
|
}).then(() => {
|
||||||
});
|
return windows.get(targetWindow.id);
|
||||||
|
}).then((actual) => {
|
||||||
|
expect(actual.tabs).to.have.lengthOf(before.tabs.length + 1);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
Reference in a new issue