aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/content/InputDriver.test.ts129
-rw-r--r--test/content/actions/follow-controller.test.ts34
-rw-r--r--test/content/actions/input.test.ts19
-rw-r--r--test/content/actions/mark.test.ts35
-rw-r--r--test/content/actions/setting.test.ts43
-rw-r--r--test/content/components/common/follow.html17
-rw-r--r--test/content/components/common/follow.test.ts25
-rw-r--r--test/content/components/common/hint.test.ts57
-rw-r--r--test/content/components/common/input.test.ts72
-rw-r--r--test/content/domains/Key.test.ts (renamed from test/shared/utils/keys.test.ts)69
-rw-r--r--test/content/domains/KeySequence.test.ts72
-rw-r--r--test/content/mock/MockConsoleClient.ts26
-rw-r--r--test/content/mock/MockScrollPresenter.ts47
-rw-r--r--test/content/presenters/Hint.test.html (renamed from test/content/components/common/hint.html)0
-rw-r--r--test/content/presenters/Hint.test.ts158
-rw-r--r--test/content/presenters/NavigationPresenter.test.ts (renamed from test/content/navigates.test.ts)41
-rw-r--r--test/content/reducers/addon.test.ts17
-rw-r--r--test/content/reducers/find.test.ts22
-rw-r--r--test/content/reducers/follow-controller.test.ts47
-rw-r--r--test/content/reducers/input.test.ts25
-rw-r--r--test/content/reducers/mark.test.ts41
-rw-r--r--test/content/reducers/setting.test.ts31
-rw-r--r--test/content/repositories/AddonEnabledRepository.test.ts15
-rw-r--r--test/content/repositories/FindRepository.test.ts15
-rw-r--r--test/content/repositories/FollowKeyRepository.test.ts31
-rw-r--r--test/content/repositories/FollowMasterRepository.test.ts49
-rw-r--r--test/content/repositories/FollowSlaveRepository.test.ts24
-rw-r--r--test/content/repositories/KeymapRepository.test.ts37
-rw-r--r--test/content/repositories/MarkKeyRepository.test.ts36
-rw-r--r--test/content/repositories/MarkRepository.test.ts13
-rw-r--r--test/content/repositories/SettingRepository.test.ts30
-rw-r--r--test/content/usecases/AddonEnabledUseCase.test.ts90
-rw-r--r--test/content/usecases/ClipboardUseCase.test.ts76
-rw-r--r--test/content/usecases/FindUseCase.test.ts161
-rw-r--r--test/content/usecases/HintKeyProducer.test.ts (renamed from test/content/hint-key-producer.test.ts)5
-rw-r--r--test/content/usecases/MarkUseCase.test.ts107
-rw-r--r--test/content/usecases/SettingUseCaase.test.ts71
37 files changed, 1235 insertions, 552 deletions
diff --git a/test/content/InputDriver.test.ts b/test/content/InputDriver.test.ts
new file mode 100644
index 0000000..b9f2c28
--- /dev/null
+++ b/test/content/InputDriver.test.ts
@@ -0,0 +1,129 @@
+import InputDriver from '../../src/content/InputDriver';
+import { expect } from 'chai';
+import Key from '../../src/content/domains/Key';
+
+describe('InputDriver', () => {
+ let target: HTMLElement;
+ let driver: InputDriver;
+
+ beforeEach(() => {
+ target = document.createElement('div');
+ document.body.appendChild(target);
+ driver = new InputDriver(target);
+ });
+
+ afterEach(() => {
+ target.remove();
+ target = null;
+ driver = null;
+ });
+
+ it('register callbacks', (done) => {
+ driver.onKey((key: Key): boolean => {
+ expect(key.key).to.equal('a');
+ expect(key.ctrlKey).to.be.true;
+ expect(key.shiftKey).to.be.false;
+ expect(key.altKey).to.be.false;
+ expect(key.metaKey).to.be.false;
+ done();
+ return true;
+ });
+
+ target.dispatchEvent(new KeyboardEvent('keydown', {
+ key: 'a',
+ ctrlKey: true,
+ shiftKey: false,
+ altKey: false,
+ metaKey: false,
+ }));
+ });
+
+ it('invoke callback once', () => {
+ let a = 0, b = 0;
+ driver.onKey((key: Key): boolean => {
+ if (key.key == 'a') {
+ ++a;
+ } else {
+ key.key == 'b'
+ ++b;
+ }
+ return true;
+ });
+
+ let events = [
+ new KeyboardEvent('keydown', { key: 'a' }),
+ new KeyboardEvent('keydown', { key: 'b' }),
+ new KeyboardEvent('keypress', { key: 'a' }),
+ new KeyboardEvent('keyup', { key: 'a' }),
+ new KeyboardEvent('keypress', { key: 'b' }),
+ new KeyboardEvent('keyup', { key: 'b' }),
+ ];
+ for (let e of events) {
+ target.dispatchEvent(e);
+ }
+
+ expect(a).to.equal(1);
+ expect(b).to.equal(1);
+ })
+
+ it('propagates and stop handler chain', () => {
+ let a = 0, b = 0, c = 0;
+ driver.onKey((key: Key): boolean => {
+ a++;
+ return false;
+ });
+ driver.onKey((key: Key): boolean => {
+ b++;
+ return true;
+ });
+ driver.onKey((key: Key): boolean => {
+ c++;
+ return true;
+ });
+
+ target.dispatchEvent(new KeyboardEvent('keydown', { key: 'b' }));
+
+ expect(a).to.equal(1);
+ expect(b).to.equal(1);
+ expect(c).to.equal(0);
+ })
+
+ it('does not invoke only meta keys', () => {
+ driver.onKey((key: Key): boolean=> {
+ expect.fail();
+ return false;
+ });
+
+ target.dispatchEvent(new KeyboardEvent('keydown', { key: 'Shift' }));
+ target.dispatchEvent(new KeyboardEvent('keydown', { key: 'Control' }));
+ target.dispatchEvent(new KeyboardEvent('keydown', { key: 'Alt' }));
+ target.dispatchEvent(new KeyboardEvent('keydown', { key: 'OS' }));
+ })
+
+ it('ignores events from input elements', () => {
+ ['input', 'textarea', 'select'].forEach((name) => {
+ let input = window.document.createElement(name);
+ let driver = new InputDriver(input);
+ driver.onKey((key: Key): boolean => {
+ expect.fail();
+ return false;
+ });
+ input.dispatchEvent(new KeyboardEvent('keydown', { key: 'x' }));
+ });
+ });
+
+ it('ignores events from contenteditable elements', () => {
+ let div = window.document.createElement('div');
+ let driver = new InputDriver(div);
+ driver.onKey((key: Key): boolean => {
+ expect.fail();
+ return false;
+ });
+
+ div.setAttribute('contenteditable', '');
+ div.dispatchEvent(new KeyboardEvent('keydown', { key: 'x' }));
+
+ div.setAttribute('contenteditable', 'true');
+ div.dispatchEvent(new KeyboardEvent('keydown', { key: 'x' }));
+ });
+});
diff --git a/test/content/actions/follow-controller.test.ts b/test/content/actions/follow-controller.test.ts
deleted file mode 100644
index a4b1710..0000000
--- a/test/content/actions/follow-controller.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import * as actions from 'content/actions';
-import * as followControllerActions from 'content/actions/follow-controller';
-
-describe('follow-controller actions', () => {
- describe('enable', () => {
- it('creates FOLLOW_CONTROLLER_ENABLE action', () => {
- let action = followControllerActions.enable(true);
- expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_ENABLE);
- expect(action.newTab).to.equal(true);
- });
- });
-
- describe('disable', () => {
- it('creates FOLLOW_CONTROLLER_DISABLE action', () => {
- let action = followControllerActions.disable(true);
- expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_DISABLE);
- });
- });
-
- describe('keyPress', () => {
- it('creates FOLLOW_CONTROLLER_KEY_PRESS action', () => {
- let action = followControllerActions.keyPress(100);
- expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_KEY_PRESS);
- expect(action.key).to.equal(100);
- });
- });
-
- describe('backspace', () => {
- it('creates FOLLOW_CONTROLLER_BACKSPACE action', () => {
- let action = followControllerActions.backspace(100);
- expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_BACKSPACE);
- });
- });
-});
diff --git a/test/content/actions/input.test.ts b/test/content/actions/input.test.ts
deleted file mode 100644
index 33238a5..0000000
--- a/test/content/actions/input.test.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import * as actions from 'content/actions';
-import * as inputActions from 'content/actions/input';
-
-describe("input actions", () => {
- describe("keyPress", () => {
- it('create INPUT_KEY_PRESS action', () => {
- let action = inputActions.keyPress('a');
- expect(action.type).to.equal(actions.INPUT_KEY_PRESS);
- expect(action.key).to.equal('a');
- });
- });
-
- describe("clearKeys", () => {
- it('create INPUT_CLEAR_KEYSaction', () => {
- let action = inputActions.clearKeys();
- expect(action.type).to.equal(actions.INPUT_CLEAR_KEYS);
- });
- });
-});
diff --git a/test/content/actions/mark.test.ts b/test/content/actions/mark.test.ts
deleted file mode 100644
index 6c6d59e..0000000
--- a/test/content/actions/mark.test.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import * as actions from 'content/actions';
-import * as markActions from 'content/actions/mark';
-
-describe('mark actions', () => {
- describe('startSet', () => {
- it('create MARK_START_SET action', () => {
- let action = markActions.startSet();
- expect(action.type).to.equal(actions.MARK_START_SET);
- });
- });
-
- describe('startJump', () => {
- it('create MARK_START_JUMP action', () => {
- let action = markActions.startJump();
- expect(action.type).to.equal(actions.MARK_START_JUMP);
- });
- });
-
- describe('cancel', () => {
- it('create MARK_CANCEL action', () => {
- let action = markActions.cancel();
- expect(action.type).to.equal(actions.MARK_CANCEL);
- });
- });
-
- describe('setLocal', () => {
- it('create setLocal action', () => {
- let action = markActions.setLocal('a', 20, 30);
- expect(action.type).to.equal(actions.MARK_SET_LOCAL);
- expect(action.key).to.equal('a');
- expect(action.x).to.equal(20);
- expect(action.y).to.equal(30);
- });
- });
-});
diff --git a/test/content/actions/setting.test.ts b/test/content/actions/setting.test.ts
deleted file mode 100644
index c831433..0000000
--- a/test/content/actions/setting.test.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import * as actions from 'content/actions';
-import * as settingActions from 'content/actions/setting';
-
-describe("setting actions", () => {
- describe("set", () => {
- it('create SETTING_SET action', () => {
- let action = settingActions.set({
- keymaps: {
- 'dd': 'remove current tab',
- 'z<C-A>': 'increment',
- },
- search: {
- default: "google",
- engines: {
- google: 'https://google.com/search?q={}',
- }
- },
- properties: {
- hintchars: 'abcd1234',
- },
- blacklist: [],
- });
- expect(action.type).to.equal(actions.SETTING_SET);
- expect(action.settings.properties.hintchars).to.equal('abcd1234');
- });
-
- it('overrides cancel keys', () => {
- let action = settingActions.set({
- keymaps: {
- "k": { "type": "scroll.vertically", "count": -1 },
- "j": { "type": "scroll.vertically", "count": 1 },
- }
- });
- let keymaps = action.settings.keymaps;
- expect(action.settings.keymaps).to.deep.equals({
- "k": { type: "scroll.vertically", count: -1 },
- "j": { type: "scroll.vertically", count: 1 },
- '<Esc>': { type: 'cancel' },
- '<C-[>': { type: 'cancel' },
- });
- });
- });
-});
diff --git a/test/content/components/common/follow.html b/test/content/components/common/follow.html
deleted file mode 100644
index b2a2d74..0000000
--- a/test/content/components/common/follow.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html>
- <body>
- <a id='visible_a' href='#' >link</a>
- <a href='#' style='display:none'>invisible 1</a>
- <a href='#' style='visibility:hidden'>invisible 2</a>
- <i>not link<i>
- <div id='editable_div_1' contenteditable>link</div>
- <div id='editable_div_2' contenteditable='true'>link</div>
- <div id='x' contenteditable='false'>link</div>
- <details>
- <summary id='summary_1'>summary link</summary>
- Some details
- <a href='#'>not visible</a>
- </details>
- </body>
-</html>
diff --git a/test/content/components/common/follow.test.ts b/test/content/components/common/follow.test.ts
deleted file mode 100644
index 90d6cf5..0000000
--- a/test/content/components/common/follow.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import FollowComponent from 'content/components/common/follow';
-
-describe('FollowComponent', () => {
- describe('#getTargetElements', () => {
- beforeEach(() => {
- document.body.innerHTML = __html__['test/content/components/common/follow.html'];
- });
-
- it('returns visible links', () => {
- let targets = FollowComponent.getTargetElements(
- window,
- { width: window.innerWidth, height: window.innerHeight },
- { x: 0, y: 0 });
- expect(targets).to.have.lengthOf(4);
-
- let ids = Array.prototype.map.call(targets, (e) => e.id);
- expect(ids).to.include.members([
- 'visible_a',
- 'editable_div_1',
- 'editable_div_2',
- 'summary_1',
- ]);
- });
- });
-});
diff --git a/test/content/components/common/hint.test.ts b/test/content/components/common/hint.test.ts
deleted file mode 100644
index 42d571f..0000000
--- a/test/content/components/common/hint.test.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import Hint from 'content/components/common/hint';
-
-describe('Hint class', () => {
- beforeEach(() => {
- document.body.innerHTML = __html__['test/content/components/common/hint.html'];
- });
-
- describe('#constructor', () => {
- it('creates a hint element with tag name', () => {
- let link = document.getElementById('test-link');
- let hint = new Hint(link, 'abc');
- expect(hint.element.textContent.trim()).to.be.equal('abc');
- });
-
- it('throws an exception when non-element given', () => {
- expect(() => new Hint(window, 'abc')).to.throw(TypeError);
- });
- });
-
- describe('#show', () => {
- it('shows an element', () => {
- let link = document.getElementById('test-link');
- let hint = new Hint(link, 'abc');
- hint.hide();
- hint.show();
-
- expect(hint.element.style.display).to.not.equal('none');
- });
- });
-
- describe('#hide', () => {
- it('hides an element', () => {
- let link = document.getElementById('test-link');
- let hint = new Hint(link, 'abc');
- hint.hide();
-
- expect(hint.element.style.display).to.equal('none');
- });
- });
-
- describe('#remove', () => {
- it('removes an element', () => {
- let link = document.getElementById('test-link');
- let hint = new Hint(link, 'abc');
-
- expect(hint.element.parentElement).to.not.be.null;
- hint.remove();
- expect(hint.element.parentElement).to.be.null;
- });
- });
-
- describe('#activate', () => {
- // TODO test activations
- });
-});
-
-
diff --git a/test/content/components/common/input.test.ts b/test/content/components/common/input.test.ts
deleted file mode 100644
index f3a943c..0000000
--- a/test/content/components/common/input.test.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import InputComponent from 'content/components/common/input';
-
-describe('InputComponent', () => {
- it('register callbacks', () => {
- let component = new InputComponent(window.document);
- let key = { key: 'a', ctrlKey: true, shiftKey: false, altKey: false, metaKey: false };
- component.onKey((key) => {
- expect(key).to.deep.equal(key);
- });
- component.onKeyDown(key);
- });
-
- it('invoke callback once', () => {
- let component = new InputComponent(window.document);
- let a = 0, b = 0;
- component.onKey((key) => {
- if (key.key == 'a') {
- ++a;
- } else {
- key.key == 'b'
- ++b;
- }
- });
-
- let elem = document.body;
- component.onKeyDown({ key: 'a', target: elem });
- component.onKeyDown({ key: 'b', target: elem });
- component.onKeyPress({ key: 'a', target: elem });
- component.onKeyUp({ key: 'a', target: elem });
- component.onKeyPress({ key: 'b', target: elem });
- component.onKeyUp({ key: 'b', target: elem });
-
- expect(a).is.equals(1);
- expect(b).is.equals(1);
- })
-
- it('does not invoke only meta keys', () => {
- let component = new InputComponent(window.document);
- component.onKey((key) => {
- expect.fail();
- });
- component.onKeyDown({ key: 'Shift' });
- component.onKeyDown({ key: 'Control' });
- component.onKeyDown({ key: 'Alt' });
- component.onKeyDown({ key: 'OS' });
- })
-
- it('ignores events from input elements', () => {
- ['input', 'textarea', 'select'].forEach((name) => {
- let target = window.document.createElement(name);
- let component = new InputComponent(target);
- component.onKey((key) => {
- expect.fail();
- });
- component.onKeyDown({ key: 'x', target });
- });
- });
-
- it('ignores events from contenteditable elements', () => {
- let target = window.document.createElement('div');
- let component = new InputComponent(target);
- component.onKey((key) => {
- expect.fail();
- });
-
- target.setAttribute('contenteditable', '');
- component.onKeyDown({ key: 'x', target });
-
- target.setAttribute('contenteditable', 'true');
- component.onKeyDown({ key: 'x', target });
- })
-});
diff --git a/test/shared/utils/keys.test.ts b/test/content/domains/Key.test.ts
index b2ad3cb..b3f9fb6 100644
--- a/test/shared/utils/keys.test.ts
+++ b/test/content/domains/Key.test.ts
@@ -1,11 +1,12 @@
-import * as keys from 'shared/utils/keys';
+import Key, * as keys from '../../../src/content/domains/Key';
+import { expect } from 'chai'
-describe("keys util", () => {
+describe("Key", () => {
describe('fromKeyboardEvent', () => {
it('returns from keyboard input Ctrl+X', () => {
- let k = keys.fromKeyboardEvent({
- key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true
- });
+ let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', {
+ key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true,
+ }));
expect(k.key).to.equal('x');
expect(k.shiftKey).to.be.false;
expect(k.ctrlKey).to.be.true;
@@ -14,9 +15,9 @@ describe("keys util", () => {
});
it('returns from keyboard input Shift+Esc', () => {
- let k = keys.fromKeyboardEvent({
+ let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', {
key: 'Escape', shiftKey: true, ctrlKey: false, altKey: false, metaKey: true
- });
+ }));
expect(k.key).to.equal('Esc');
expect(k.shiftKey).to.be.true;
expect(k.ctrlKey).to.be.false;
@@ -26,9 +27,9 @@ describe("keys util", () => {
it('returns from keyboard input Ctrl+$', () => {
// $ required shift pressing on most keyboards
- let k = keys.fromKeyboardEvent({
+ let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', {
key: '$', shiftKey: true, ctrlKey: true, altKey: false, metaKey: false
- });
+ }));
expect(k.key).to.equal('$');
expect(k.shiftKey).to.be.false;
expect(k.ctrlKey).to.be.true;
@@ -37,9 +38,9 @@ describe("keys util", () => {
});
it('returns from keyboard input Crtl+Space', () => {
- let k = keys.fromKeyboardEvent({
+ let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', {
key: ' ', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false
- });
+ }));
expect(k.key).to.equal('Space');
expect(k.shiftKey).to.be.false;
expect(k.ctrlKey).to.be.true;
@@ -122,43 +123,15 @@ describe("keys util", () => {
});
});
- describe('fromMapKeys', () => {
- it('returns mapped keys for Shift+Esc', () => {
- let keyArray = keys.fromMapKeys('<S-Esc>');
- expect(keyArray).to.have.lengthOf(1);
- expect(keyArray[0].key).to.equal('Esc');
- expect(keyArray[0].shiftKey).to.be.true;
- });
-
- it('returns mapped keys for a<C-B><A-C>d<M-e>', () => {
- let keyArray = keys.fromMapKeys('a<C-B><A-C>d<M-e>');
- expect(keyArray).to.have.lengthOf(5);
- expect(keyArray[0].key).to.equal('a');
- expect(keyArray[1].ctrlKey).to.be.true;
- expect(keyArray[1].key).to.equal('b');
- expect(keyArray[2].altKey).to.be.true;
- expect(keyArray[2].key).to.equal('c');
- expect(keyArray[3].key).to.equal('d');
- expect(keyArray[4].metaKey).to.be.true;
- expect(keyArray[4].key).to.equal('e');
- });
- })
-
describe('equals', () => {
- expect(keys.equals({
- key: 'x',
- ctrlKey: true,
- }, {
- key: 'x',
- ctrlKey: true,
- })).to.be.true;
-
- expect(keys.equals({
- key: 'X',
- shiftKey: true,
- }, {
- key: 'x',
- ctrlKey: true,
- })).to.be.false;
+ expect(keys.equals(
+ { key: 'x', ctrlKey: true, },
+ { key: 'x', ctrlKey: true, },
+ )).to.be.true;
+
+ expect(keys.equals(
+ { key: 'X', shiftKey: true, },
+ { key: 'x', ctrlKey: true, },
+ )).to.be.false;
});
});
diff --git a/test/content/domains/KeySequence.test.ts b/test/content/domains/KeySequence.test.ts
new file mode 100644
index 0000000..7387c06
--- /dev/null
+++ b/test/content/domains/KeySequence.test.ts
@@ -0,0 +1,72 @@
+import KeySequence, * as utils from '../../../src/content/domains/KeySequence';
+import { expect } from 'chai'
+
+describe("KeySequence", () => {
+ describe('#push', () => {
+ it('append a key to the sequence', () => {
+ let seq = KeySequence.from([]);
+ seq.push({ key: 'g' });
+ seq.push({ key: 'u', shiftKey: true });
+
+ let array = seq.getKeyArray();
+ expect(array[0]).to.deep.equal({ key: 'g' });
+ expect(array[1]).to.deep.equal({ key: 'u', shiftKey: true });
+ })
+ });
+
+ describe('#startsWith', () => {
+ it('returns true if the key sequence starts with param', () => {
+ let seq = KeySequence.from([
+ { key: 'g' },
+ { key: 'u', shiftKey: true },
+ ]);
+
+ expect(seq.startsWith(KeySequence.from([
+ ]))).to.be.true;
+ expect(seq.startsWith(KeySequence.from([
+ { key: 'g' },
+ ]))).to.be.true;
+ expect(seq.startsWith(KeySequence.from([
+ { key: 'g' }, { key: 'u', shiftKey: true },
+ ]))).to.be.true;
+ expect(seq.startsWith(KeySequence.from([
+ { key: 'g' }, { key: 'u', shiftKey: true }, { key: 'x' },
+ ]))).to.be.false;
+ expect(seq.startsWith(KeySequence.from([
+ { key: 'h' },
+ ]))).to.be.false;
+ })
+
+ it('returns true if the empty sequence starts with an empty sequence', () => {
+ let seq = KeySequence.from([]);
+
+ expect(seq.startsWith(KeySequence.from([]))).to.be.true;
+ expect(seq.startsWith(KeySequence.from([
+ { key: 'h' },
+ ]))).to.be.false;
+ })
+ });
+
+ describe('#fromMapKeys', () => {
+ it('returns mapped keys for Shift+Esc', () => {
+ let keyArray = utils.fromMapKeys('<S-Esc>').getKeyArray();
+ expect(keyArray).to.have.lengthOf(1);
+ expect(keyArray[0].key).to.equal('Esc');
+ expect(keyArray[0].shiftKey).to.be.true;
+ });
+
+ it('returns mapped keys for a<C-B><A-C>d<M-e>', () => {
+ let keyArray = utils.fromMapKeys('a<C-B><A-C>d<M-e>').getKeyArray();
+ expect(keyArray).to.have.lengthOf(5);
+ expect(keyArray[0].key).to.equal('a');
+ expect(keyArray[1].ctrlKey).to.be.true;
+ expect(keyArray[1].key).to.equal('b');
+ expect(keyArray[2].altKey).to.be.true;
+ expect(keyArray[2].key).to.equal('c');
+ expect(keyArray[3].key).to.equal('d');
+ expect(keyArray[4].metaKey).to.be.true;
+ expect(keyArray[4].key).to.equal('e');
+ });
+ })
+
+});
diff --git a/test/content/mock/MockConsoleClient.ts b/test/content/mock/MockConsoleClient.ts
new file mode 100644
index 0000000..8de2d83
--- /dev/null
+++ b/test/content/mock/MockConsoleClient.ts
@@ -0,0 +1,26 @@
+import ConsoleClient from '../../../src/content/client/ConsoleClient';
+
+export default class MockConsoleClient implements ConsoleClient {
+ public isError: boolean;
+
+ public text: string;
+
+ constructor() {
+ this.isError = false;
+ this.text = '';
+ }
+
+ info(text: string): Promise<void> {
+ this.isError = false;
+ this.text = text;
+ return Promise.resolve();
+ }
+
+ error(text: string): Promise<void> {
+ this.isError = true;
+ this.text = text;
+ return Promise.resolve();
+ }
+}
+
+
diff --git a/test/content/mock/MockScrollPresenter.ts b/test/content/mock/MockScrollPresenter.ts
new file mode 100644
index 0000000..819569a
--- /dev/null
+++ b/test/content/mock/MockScrollPresenter.ts
@@ -0,0 +1,47 @@
+import ScrollPresenter, { Point } from '../../../src/content/presenters/ScrollPresenter';
+
+export default class MockScrollPresenter implements ScrollPresenter {
+ private pos: Point;
+
+ constructor() {
+ this.pos = { x: 0, y: 0 };
+ }
+
+ getScroll(): Point {
+ return this.pos;
+ }
+
+ scrollVertically(amount: number, _smooth: boolean): void {
+ this.pos.y += amount;
+ }
+
+ scrollHorizonally(amount: number, _smooth: boolean): void {
+ this.pos.x += amount;
+ }
+
+ scrollPages(amount: number, _smooth: boolean): void {
+ this.pos.x += amount;
+ }
+
+ scrollTo(x: number, y: number, _smooth: boolean): void {
+ this.pos.x = x;
+ this.pos.y = y;
+ }
+
+ scrollToTop(_smooth: boolean): void {
+ this.pos.y = 0;
+ }
+
+ scrollToBottom(_smooth: boolean): void {
+ this.pos.y = Infinity;
+ }
+
+ scrollToHome(_smooth: boolean): void {
+ this.pos.x = 0;
+ }
+
+ scrollToEnd(_smooth: boolean): void {
+ this.pos.x = Infinity;
+ }
+}
+
diff --git a/test/content/components/common/hint.html b/test/content/presenters/Hint.test.html
index b50c5fe..b50c5fe 100644
--- a/test/content/components/common/hint.html
+++ b/test/content/presenters/Hint.test.html
diff --git a/test/content/presenters/Hint.test.ts b/test/content/presenters/Hint.test.ts
new file mode 100644
index 0000000..7994788
--- /dev/null
+++ b/test/content/presenters/Hint.test.ts
@@ -0,0 +1,158 @@
+import AbstractHint, { LinkHint, InputHint } from '../../../src/content/presenters/Hint';
+import { expect } from 'chai';
+
+class Hint extends AbstractHint {
+}
+
+describe('Hint', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `<a id='test-link' href='#'>link</a>`;
+ });
+
+ describe('#constructor', () => {
+ it('creates a hint element with tag name', () => {
+ let link = document.getElementById('test-link');
+ let hint = new Hint(link, 'abc');
+
+ let elem = document.querySelector('.vimvixen-hint');
+ expect(elem.textContent.trim()).to.be.equal('abc');
+ });
+ });
+
+ describe('#show', () => {
+ it('shows an element', () => {
+ let link = document.getElementById('test-link');
+ let hint = new Hint(link, 'abc');
+ hint.hide();
+ hint.show();
+
+ let elem = document.querySelector('.vimvixen-hint') as HTMLElement;
+ expect(elem.style.display).to.not.equal('none');
+ });
+ });
+
+ describe('#hide', () => {
+ it('hides an element', () => {
+ let link = document.getElementById('test-link') as HTMLElement;
+ let hint = new Hint(link, 'abc');
+ hint.hide();
+
+ let elem = document.querySelector('.vimvixen-hint') as HTMLElement;
+ expect(elem.style.display).to.equal('none');
+ });
+ });
+
+ describe('#remove', () => {
+ it('removes an element', () => {
+ let link = document.getElementById('test-link');
+ let hint = new Hint(link, 'abc');
+
+ let elem = document.querySelector('.vimvixen-hint');
+ expect(elem.parentElement).to.not.be.null;
+ hint.remove();
+ expect(elem.parentElement).to.be.null;
+ });
+ });
+});
+
+describe('LinkHint', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `
+<a id='test-link1' href='https://google.com/'>link</a>
+<a id='test-link2' href='https://yahoo.com/' target='_blank'>link</a>
+<a id='test-link3' href='#' target='_blank'>link</a>
+`;
+ });
+
+ describe('#getLink()', () => {
+ it('returns value of "href" attribute', () => {
+ let link = document.getElementById('test-link1') as HTMLAnchorElement;
+ let hint = new LinkHint(link, 'abc');
+
+ expect(hint.getLink()).to.equal('https://google.com/');
+ });
+ });
+
+ describe('#getLinkTarget()', () => {
+ it('returns value of "target" attribute', () => {
+ let link = document.getElementById('test-link1') as HTMLAnchorElement;
+ let hint = new LinkHint(link, 'abc');
+
+ expect(hint.getLinkTarget()).to.be.null;
+
+ link = document.getElementById('test-link2') as HTMLAnchorElement;
+ hint = new LinkHint(link, 'abc');
+
+ expect(hint.getLinkTarget()).to.equal('_blank');
+ });
+ });
+
+ describe('#click()', () => {
+ it('clicks a element', (done) => {
+ let link = document.getElementById('test-link3') as HTMLAnchorElement;
+ let hint = new LinkHint(link, 'abc');
+ link.onclick = () => { done() };
+
+ hint.click();
+ });
+ });
+});
+
+describe('InputHint', () => {
+ describe('#activate()', () => {
+ context('<input>', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `<input id='test-input'></input>`;
+ });
+
+ it('focuses to the input', () => {
+ let input = document.getElementById('test-input') as HTMLInputElement;
+ let hint = new InputHint(input, 'abc');
+ hint.activate();
+
+ expect(document.activeElement).to.equal(input);
+ });
+ });
+
+ context('<input type="checkbox">', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `<input type="checkbox" id='test-input'></input>`;
+ });
+
+ it('checks and focuses to the input', () => {
+ let input = document.getElementById('test-input') as HTMLInputElement;
+ let hint = new InputHint(input, 'abc');
+ hint.activate();
+
+ expect(input.checked).to.be.true;
+ });
+ });
+ context('<textarea>', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `<textarea id='test-textarea'></textarea>`;
+ });
+
+ it('focuses to the textarea', () => {
+ let textarea = document.getElementById('test-textarea') as HTMLTextAreaElement;
+ let hint = new InputHint(textarea, 'abc');
+ hint.activate();
+
+ expect(document.activeElement).to.equal(textarea);
+ });
+ });
+
+ context('<button>', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `<button id='test-button'></button>`;
+ });
+
+ it('clicks the button', (done) => {
+ let button = document.getElementById('test-button') as HTMLButtonElement;
+ button.onclick = () => { done() };
+
+ let hint = new InputHint(button, 'abc');
+ hint.activate();
+ });
+ });
+ });
+});
diff --git a/test/content/navigates.test.ts b/test/content/presenters/NavigationPresenter.test.ts
index 1d73344..c1aca9a 100644
--- a/test/content/navigates.test.ts
+++ b/test/content/presenters/NavigationPresenter.test.ts
@@ -1,19 +1,26 @@
-import * as navigates from 'content/navigates';
-
-const testRel = (done, rel, html) => {
- const method = rel === 'prev' ? 'linkPrev' : 'linkNext';
- document.body.innerHTML = html;
- navigates[method](window);
- setTimeout(() => {
- expect(document.location.hash).to.equal(`#${rel}`);
- done();
- }, 0);
-};
-
-const testPrev = html => done => testRel(done, 'prev', html);
-const testNext = html => done => testRel(done, 'next', html);
-
-describe('navigates module', () => {
+import NavigationPresenter, { NavigationPresenterImpl }
+ from '../../../src/content/presenters/NavigationPresenter';
+import { expect } from 'chai';
+
+describe('NavigationPresenter', () => {
+ let sut;
+
+ const testRel = (done, rel, html) => {
+ const method = rel === 'prev' ? sut.openLinkPrev.bind(sut) : sut.openLinkNext.bind(sut);
+ document.body.innerHTML = html;
+ method();
+ setTimeout(() => {
+ expect(document.location.hash).to.equal(`#${rel}`);
+ done();
+ }, 0);
+ };
+ const testPrev = html => done => testRel(done, 'prev', html);
+ const testNext = html => done => testRel(done, 'next', html);
+
+ before(() => {
+ sut = new NavigationPresenterImpl();
+ });
+
describe('#linkPrev', () => {
it('navigates to <link> elements whose rel attribute is "prev"', testPrev(
'<link rel="prev" href="#prev" />'
@@ -130,7 +137,7 @@ describe('navigates module', () => {
// NOTE: not able to test location
it('removes hash', () => {
window.location.hash = '#section-1';
- navigates.parent(window);
+ sut.openParent();
expect(document.location.hash).to.be.empty;
});
});
diff --git a/test/content/reducers/addon.test.ts b/test/content/reducers/addon.test.ts
deleted file mode 100644
index fb05244..0000000
--- a/test/content/reducers/addon.test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as actions from 'content/actions';
-import addonReducer from 'content/reducers/addon';
-
-describe("addon reducer", () => {
- it('return the initial state', () => {
- let state = addonReducer(undefined, {});
- expect(state).to.have.property('enabled', true);
- });
-
- it('return next state for ADDON_SET_ENABLED', () => {
- let action = { type: actions.ADDON_SET_ENABLED, enabled: true };
- let prev = { enabled: false };
- let state = addonReducer(prev, action);
-
- expect(state.enabled).is.equal(true);
- });
-});
diff --git a/test/content/reducers/find.test.ts b/test/content/reducers/find.test.ts
deleted file mode 100644
index 66a2c67..0000000
--- a/test/content/reducers/find.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import * as actions from 'content/actions';
-import findReducer from 'content/reducers/find';
-
-describe("find reducer", () => {
- it('return the initial state', () => {
- let state = findReducer(undefined, {});
- expect(state).to.have.property('keyword', null);
- expect(state).to.have.property('found', false);
- });
-
- it('return next state for FIND_SET_KEYWORD', () => {
- let action = {
- type: actions.FIND_SET_KEYWORD,
- keyword: 'xyz',
- found: true,
- };
- let state = findReducer({}, action);
-
- expect(state.keyword).is.equal('xyz');
- expect(state.found).to.be.true;
- });
-});
diff --git a/test/content/reducers/follow-controller.test.ts b/test/content/reducers/follow-controller.test.ts
deleted file mode 100644
index 39f326c..0000000
--- a/test/content/reducers/follow-controller.test.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as actions from 'content/actions';
-import followControllerReducer from 'content/reducers/follow-controller';
-
-describe('follow-controller reducer', () => {
- it ('returns the initial state', () => {
- let state = followControllerReducer(undefined, {});
- expect(state).to.have.property('enabled', false);
- expect(state).to.have.property('newTab');
- expect(state).to.have.deep.property('keys', '');
- });
-
- it ('returns next state for FOLLOW_CONTROLLER_ENABLE', () => {
- let action = { type: actions.FOLLOW_CONTROLLER_ENABLE, newTab: true };
- let state = followControllerReducer({ enabled: false, newTab: false }, action);
- expect(state).to.have.property('enabled', true);
- expect(state).to.have.property('newTab', true);
- expect(state).to.have.property('keys', '');
- });
-
- it ('returns next state for FOLLOW_CONTROLLER_DISABLE', () => {
- let action = { type: actions.FOLLOW_CONTROLLER_DISABLE };
- let state = followControllerReducer({ enabled: true }, action);
- expect(state).to.have.property('enabled', false);
- });
-
- it ('returns next state for FOLLOW_CONTROLLER_KEY_PRESS', () => {
- let action = { type: actions.FOLLOW_CONTROLLER_KEY_PRESS, key: 'a'};
- let state = followControllerReducer({ keys: '' }, action);
- expect(state).to.have.deep.property('keys', 'a');
-
- action = { type: actions.FOLLOW_CONTROLLER_KEY_PRESS, key: 'b'};
- state = followControllerReducer(state, action);
- expect(state).to.have.deep.property('keys', 'ab');
- });
-
- it ('returns next state for FOLLOW_CONTROLLER_BACKSPACE', () => {
- let action = { type: actions.FOLLOW_CONTROLLER_BACKSPACE };
- let state = followControllerReducer({ keys: 'ab' }, action);
- expect(state).to.have.deep.property('keys', 'a');
-
- state = followControllerReducer(state, action);
- expect(state).to.have.deep.property('keys', '');
-
- state = followControllerReducer(state, action);
- expect(state).to.have.deep.property('keys', '');
- });
-});
diff --git a/test/content/reducers/input.test.ts b/test/content/reducers/input.test.ts
deleted file mode 100644
index f892201..0000000
--- a/test/content/reducers/input.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as actions from 'content/actions';
-import inputReducer from 'content/reducers/input';
-
-describe("input reducer", () => {
- it('return the initial state', () => {
- let state = inputReducer(undefined, {});
- expect(state).to.have.deep.property('keys', []);
- });
-
- it('return next state for INPUT_KEY_PRESS', () => {
- let action = { type: actions.INPUT_KEY_PRESS, key: 'a' };
- let state = inputReducer(undefined, action);
- expect(state).to.have.deep.property('keys', ['a']);
-
- action = { type: actions.INPUT_KEY_PRESS, key: 'b' };
- state = inputReducer(state, action);
- expect(state).to.have.deep.property('keys', ['a', 'b']);
- });
-
- it('return next state for INPUT_CLEAR_KEYS', () => {
- let action = { type: actions.INPUT_CLEAR_KEYS };
- let state = inputReducer({ keys: [1, 2, 3] }, action);
- expect(state).to.have.deep.property('keys', []);
- });
-});
diff --git a/test/content/reducers/mark.test.ts b/test/content/reducers/mark.test.ts
deleted file mode 100644
index 1a51c3e..0000000
--- a/test/content/reducers/mark.test.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import * as actions from 'content/actions';
-import reducer from 'content/reducers/mark';
-
-describe("mark reducer", () => {
- it('return the initial state', () => {
- let state = reducer(undefined, {});
- expect(state.setMode).to.be.false;
- expect(state.jumpMode).to.be.false;
- expect(state.marks).to.be.empty;
- });
-
- it('starts set mode', () => {
- let action = { type: actions.MARK_START_SET };
- let state = reducer(undefined, action);
- expect(state.setMode).to.be.true;
- });
-
- it('starts jump mode', () => {
- let action = { type: actions.MARK_START_JUMP };
- let state = reducer(undefined, action);
- expect(state.jumpMode).to.be.true;
- });
-
- it('cancels set and jump mode', () => {
- let action = { type: actions.MARK_CANCEL };
- let state = reducer({ setMode: true }, action);
- expect(state.setMode).to.be.false;
-
- state = reducer({ jumpMode: true }, action);
- expect(state.jumpMode).to.be.false;
- });
-
- it('stores local mark', () => {
- let action = { type: actions.MARK_SET_LOCAL, key: 'a', x: 20, y: 30};
- let state = reducer({ setMode: true }, action);
- expect(state.setMode).to.be.false;
- expect(state.marks['a']).to.be.an('object')
- expect(state.marks['a'].x).to.equal(20)
- expect(state.marks['a'].y).to.equal(30)
- });
-});
diff --git a/test/content/reducers/setting.test.ts b/test/content/reducers/setting.test.ts
deleted file mode 100644
index 9b332aa..0000000
--- a/test/content/reducers/setting.test.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import * as actions from 'content/actions';
-import settingReducer from 'content/reducers/setting';
-
-describe("content setting reducer", () => {
- it('return the initial state', () => {
- let state = settingReducer(undefined, {});
- expect(state.keymaps).to.be.empty;
- });
-
- it('return next state for SETTING_SET', () => {
- let newSettings = { red: 'apple', yellow: 'banana' };
- let action = {
- type: actions.SETTING_SET,
- settings: {
- keymaps: {
- "zz": { type: "zoom.neutral" },
- "<S-Esc>": { "type": "addon.toggle.enabled" }
- },
- "blacklist": []
- }
- }
- let state = settingReducer(undefined, action);
- expect(state.keymaps).to.have.deep.all.members([
- { key: [{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
- { key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }],
- op: { type: 'zoom.neutral' }},
- { key: [{ key: 'Esc', shiftKey: true, ctrlKey: false, altKey: false, metaKey: false }],
- op: { type: 'addon.toggle.enabled' }},
- ]);
- });
-});
diff --git a/test/content/repositories/AddonEnabledRepository.test.ts b/test/content/repositories/AddonEnabledRepository.test.ts
new file mode 100644
index 0000000..3cea897
--- /dev/null
+++ b/test/content/repositories/AddonEnabledRepository.test.ts
@@ -0,0 +1,15 @@
+import { AddonEnabledRepositoryImpl } from '../../../src/content/repositories/AddonEnabledRepository';
+import { expect } from 'chai';
+
+describe('AddonEnabledRepositoryImpl', () => {
+ it('updates and gets current value', () => {
+ let sut = new AddonEnabledRepositoryImpl();
+
+ sut.set(true);
+ expect(sut.get()).to.be.true;
+
+ sut.set(false);
+ expect(sut.get()).to.be.false;
+ });
+});
+
diff --git a/test/content/repositories/FindRepository.test.ts b/test/content/repositories/FindRepository.test.ts
new file mode 100644
index 0000000..dcb2dff
--- /dev/null
+++ b/test/content/repositories/FindRepository.test.ts
@@ -0,0 +1,15 @@
+import { FindRepositoryImpl } from '../../../src/content/repositories/FindRepository';
+import { expect } from 'chai';
+
+describe('FindRepositoryImpl', () => {
+ it('updates and gets last keyword', () => {
+ let sut = new FindRepositoryImpl();
+
+ expect(sut.getLastKeyword()).to.be.null;
+
+ sut.setLastKeyword('monkey');
+
+ expect(sut.getLastKeyword()).to.equal('monkey');
+ });
+});
+
diff --git a/test/content/repositories/FollowKeyRepository.test.ts b/test/content/repositories/FollowKeyRepository.test.ts
new file mode 100644
index 0000000..eae58b9
--- /dev/null
+++ b/test/content/repositories/FollowKeyRepository.test.ts
@@ -0,0 +1,31 @@
+import FollowKeyRepository, { FollowKeyRepositoryImpl }
+ from '../../../src/content/repositories/FollowKeyRepository';
+import { expect } from 'chai';
+
+describe('FollowKeyRepositoryImpl', () => {
+ let sut: FollowKeyRepository;
+
+ before(() => {
+ sut = new FollowKeyRepositoryImpl();
+ });
+
+ describe('#getKeys()/#pushKey()/#popKey()', () => {
+ it('enqueues keys', () => {
+ expect(sut.getKeys()).to.be.empty;
+
+ sut.pushKey('a');
+ sut.pushKey('b');
+ sut.pushKey('c');
+ expect(sut.getKeys()).to.deep.equal(['a', 'b', 'c']);
+
+ sut.popKey();
+ expect(sut.getKeys()).to.deep.equal(['a', 'b']);
+
+ sut.clearKeys();
+ expect(sut.getKeys()).to.be.empty;
+ });
+ });
+});
+
+
+
diff --git a/test/content/repositories/FollowMasterRepository.test.ts b/test/content/repositories/FollowMasterRepository.test.ts
new file mode 100644
index 0000000..8c3f34e
--- /dev/null
+++ b/test/content/repositories/FollowMasterRepository.test.ts
@@ -0,0 +1,49 @@
+import FollowMasterRepository, { FollowMasterRepositoryImpl }
+ from '../../../src/content/repositories/FollowMasterRepository';
+import { expect } from 'chai';
+
+describe('FollowMasterRepositoryImpl', () => {
+ let sut: FollowMasterRepository;
+
+ before(() => {
+ sut = new FollowMasterRepositoryImpl();
+ });
+
+ describe('#getTags()/#addTag()/#clearTags()', () => {
+ it('gets, adds and clears tags', () => {
+ expect(sut.getTags()).to.be.empty;
+
+ sut.addTag('a');
+ sut.addTag('b');
+ sut.addTag('c');
+ expect(sut.getTags()).to.deep.equal(['a', 'b', 'c']);
+
+ sut.clearTags();
+ expect(sut.getTags()).to.be.empty;
+ });
+ });
+
+ describe('#getTagsByPrefix', () => {
+ it('gets tags matched by prefix', () => {
+ for (let tag of ['a', 'aa', 'ab', 'b', 'ba', 'bb']) {
+ sut.addTag(tag);
+ }
+ expect(sut.getTagsByPrefix('a')).to.deep.equal(['a', 'aa', 'ab']);
+ expect(sut.getTagsByPrefix('aa')).to.deep.equal(['aa']);
+ expect(sut.getTagsByPrefix('b')).to.deep.equal(['b', 'ba', 'bb']);
+ expect(sut.getTagsByPrefix('c')).to.be.empty;
+ });
+ });
+
+ describe('#setCurrentFollowMode()/#getCurrentNewTabMode()/#getCurrentBackgroundMode', () => {
+ it('updates and gets follow mode', () => {
+ sut.setCurrentFollowMode(false, true);
+ expect(sut.getCurrentNewTabMode()).to.be.false;
+ expect(sut.getCurrentBackgroundMode()).to.be.true;
+
+ sut.setCurrentFollowMode(true, false);
+ expect(sut.getCurrentNewTabMode()).to.be.true;
+ expect(sut.getCurrentBackgroundMode()).to.be.false;
+ });
+ });
+});
diff --git a/test/content/repositories/FollowSlaveRepository.test.ts b/test/content/repositories/FollowSlaveRepository.test.ts
new file mode 100644
index 0000000..10cf094
--- /dev/null
+++ b/test/content/repositories/FollowSlaveRepository.test.ts
@@ -0,0 +1,24 @@
+import FollowSlaveRepository, { FollowSlaveRepositoryImpl }
+ from '../../../src/content/repositories/FollowSlaveRepository';
+import { expect } from 'chai';
+
+describe('FollowSlaveRepository', () => {
+ let sut: FollowSlaveRepository;
+
+ before(() => {
+ sut = new FollowSlaveRepositoryImpl();
+ });
+
+ describe('#isFollowMode()/#enableFollowMode()/#disableFollowMode()', () => {
+ it('gets, adds updates follow mode', () => {
+ expect(sut.isFollowMode()).to.be.false;
+
+ sut.enableFollowMode();
+ expect(sut.isFollowMode()).to.be.true;
+
+ sut.disableFollowMode();
+ expect(sut.isFollowMode()).to.be.false;
+ });
+ });
+});
+
diff --git a/test/content/repositories/KeymapRepository.test.ts b/test/content/repositories/KeymapRepository.test.ts
new file mode 100644
index 0000000..34704d9
--- /dev/null
+++ b/test/content/repositories/KeymapRepository.test.ts
@@ -0,0 +1,37 @@
+import KeymapRepository, { KeymapRepositoryImpl }
+ from '../../../src/content/repositories/KeymapRepository';
+import { expect } from 'chai';
+
+describe('KeymapRepositoryImpl', () => {
+ let sut: KeymapRepository;
+
+ before(() => {
+ sut = new KeymapRepositoryImpl();
+ });
+
+ describe('#enqueueKey()', () => {
+ it('enqueues keys', () => {
+ sut.enqueueKey({ key: 'a' });
+ sut.enqueueKey({ key: 'b' });
+ let sequence = sut.enqueueKey({ key: 'c' });
+
+ expect(sequence.getKeyArray()).deep.equals([
+ { key: 'a' }, { key: 'b' }, { key: 'c' },
+ ]);
+ });
+ });
+
+ describe('#clear()', () => {
+ it('clears keys', () => {
+ sut.enqueueKey({ key: 'a' });
+ sut.enqueueKey({ key: 'b' });
+ sut.enqueueKey({ key: 'c' });
+ sut.clear();
+
+ let sequence = sut.enqueueKey({ key: 'a' });
+ expect(sequence.length()).to.equal(1);
+ });
+ });
+});
+
+
diff --git a/test/content/repositories/MarkKeyRepository.test.ts b/test/content/repositories/MarkKeyRepository.test.ts
new file mode 100644
index 0000000..8592332
--- /dev/null
+++ b/test/content/repositories/MarkKeyRepository.test.ts
@@ -0,0 +1,36 @@
+import MarkRepository, { MarkKeyRepositoryImpl }
+ from '../../../src/content/repositories/MarkKeyRepository';
+import { expect } from 'chai';
+
+describe('MarkKeyRepositoryImpl', () => {
+ let sut: MarkRepository;
+
+ before(() => {
+ sut = new MarkKeyRepositoryImpl();
+ })
+
+ describe('#isSetMode/#enableSetMode/#disabeSetMode', () => {
+ it('enables and disables set mode', () => {
+ expect(sut.isSetMode()).to.be.false;
+
+ sut.enableSetMode();
+ expect(sut.isSetMode()).to.be.true;
+
+ sut.disabeSetMode();
+ expect(sut.isSetMode()).to.be.false;
+ });
+ });
+
+ describe('#isJumpMode/#enableJumpMode/#disabeJumpMode', () => {
+ it('enables and disables jump mode', () => {
+ expect(sut.isJumpMode()).to.be.false;
+
+ sut.enableJumpMode();
+ expect(sut.isJumpMode()).to.be.true;
+
+ sut.disabeJumpMode();
+ expect(sut.isJumpMode()).to.be.false;
+ });
+ });
+});
+
diff --git a/test/content/repositories/MarkRepository.test.ts b/test/content/repositories/MarkRepository.test.ts
new file mode 100644
index 0000000..7fced5f
--- /dev/null
+++ b/test/content/repositories/MarkRepository.test.ts
@@ -0,0 +1,13 @@
+import { MarkRepositoryImpl } from '../../../src/content/repositories/MarkRepository';
+import { expect } from 'chai';
+
+describe('MarkRepositoryImpl', () => {
+ it('save and load marks', () => {
+ let sut = new MarkRepositoryImpl();
+
+ sut.set('a', { x: 10, y: 20 });
+ expect(sut.get('a')).to.deep.equal({ x: 10, y: 20 });
+ expect(sut.get('b')).to.be.null;
+ });
+});
+
diff --git a/test/content/repositories/SettingRepository.test.ts b/test/content/repositories/SettingRepository.test.ts
new file mode 100644
index 0000000..fea70b7
--- /dev/null
+++ b/test/content/repositories/SettingRepository.test.ts
@@ -0,0 +1,30 @@
+import { SettingRepositoryImpl } from '../../../src/content/repositories/SettingRepository';
+import { expect } from 'chai';
+
+describe('SettingRepositoryImpl', () => {
+ it('updates and gets current value', () => {
+ let sut = new SettingRepositoryImpl();
+
+ let settings = {
+ keymaps: {},
+ search: {
+ default: 'google',
+ engines: {
+ google: 'https://google.com/?q={}',
+ }
+ },
+ properties: {
+ hintchars: 'abcd1234',
+ smoothscroll: false,
+ complete: 'sbh',
+ },
+ blacklist: [],
+ }
+
+ sut.set(settings);
+
+ let actual = sut.get();
+ expect(actual.properties.hintchars).to.equal('abcd1234');
+ });
+});
+
diff --git a/test/content/usecases/AddonEnabledUseCase.test.ts b/test/content/usecases/AddonEnabledUseCase.test.ts
new file mode 100644
index 0000000..912bddf
--- /dev/null
+++ b/test/content/usecases/AddonEnabledUseCase.test.ts
@@ -0,0 +1,90 @@
+import AddonEnabledRepository from '../../../src/content/repositories/AddonEnabledRepository';
+import AddonEnabledUseCase from '../../../src/content/usecases/AddonEnabledUseCase';
+import AddonIndicatorClient from '../../../src/content/client/AddonIndicatorClient';
+import { expect } from 'chai';
+
+class MockAddonEnabledRepository implements AddonEnabledRepository {
+ private enabled: boolean;
+
+ constructor(init: boolean) {
+ this.enabled = init;
+ }
+
+ set(on: boolean): void {
+ this.enabled = on;
+ }
+
+ get(): boolean {
+ return this.enabled;
+ }
+}
+
+class MockAddonIndicatorClient implements AddonIndicatorClient {
+ public enabled: boolean;
+
+ constructor(init: boolean) {
+ this.enabled = init;
+ }
+
+ async setEnabled(enabled: boolean): Promise<void> {
+ this.enabled = enabled;
+ return
+ }
+}
+
+describe('AddonEnabledUseCase', () => {
+ let repository: MockAddonEnabledRepository;
+ let indicator: MockAddonIndicatorClient;
+ let sut: AddonEnabledUseCase;
+
+ beforeEach(() => {
+ repository = new MockAddonEnabledRepository(true);
+ indicator = new MockAddonIndicatorClient(false);
+ sut = new AddonEnabledUseCase({ repository, indicator });
+ });
+
+ describe('#enable', () => {
+ it('store and indicate as enabled', async() => {
+ await sut.enable();
+
+ expect(repository.get()).to.be.true;
+ expect(indicator.enabled).to.be.true;
+ });
+ });
+
+ describe('#disable', async() => {
+ it('store and indicate as disabled', async() => {
+ await sut.disable();
+
+ expect(repository.get()).to.be.false;
+ expect(indicator.enabled).to.be.false;
+ });
+ });
+
+ describe('#toggle', () => {
+ it('toggled enabled and disabled', async() => {
+ repository.set(true);
+ await sut.toggle();
+
+ expect(repository.get()).to.be.false;
+ expect(indicator.enabled).to.be.false;
+
+ repository.set(false);
+
+ await sut.toggle();
+
+ expect(repository.get()).to.be.true;
+ expect(indicator.enabled).to.be.true;
+ });
+ });
+
+ describe('#getEnabled', () => {
+ it('returns current addon enabled', () => {
+ repository.set(true);
+ expect(sut.getEnabled()).to.be.true;
+
+ repository.set(false);
+ expect(sut.getEnabled()).to.be.false;
+ });
+ });
+});
diff --git a/test/content/usecases/ClipboardUseCase.test.ts b/test/content/usecases/ClipboardUseCase.test.ts
new file mode 100644
index 0000000..862ee8a
--- /dev/null
+++ b/test/content/usecases/ClipboardUseCase.test.ts
@@ -0,0 +1,76 @@
+import ClipboardRepository from '../../../src/content/repositories/ClipboardRepository';
+import TabsClient from '../../../src/content/client/TabsClient';
+import MockConsoleClient from '../mock/MockConsoleClient';
+import ClipboardUseCase from '../../../src/content/usecases/ClipboardUseCase';
+import { expect } from 'chai';
+
+class MockClipboardRepository implements ClipboardRepository {
+ public clipboard: string;
+
+ constructor() {
+ this.clipboard = '';
+ }
+
+ read(): string {
+ return this.clipboard;
+ }
+
+ write(text: string): void {
+ this.clipboard = text;
+ }
+}
+
+class MockTabsClient implements TabsClient {
+ public last: string;
+
+ constructor() {
+ this.last = '';
+ }
+
+ openUrl(url: string, _newTab: boolean): Promise<void> {
+ this.last = url;
+ return Promise.resolve();
+ }
+}
+
+describe('ClipboardUseCase', () => {
+ let repository: MockClipboardRepository;
+ let client: MockTabsClient;
+ let consoleClient: MockConsoleClient;
+ let sut: ClipboardUseCase;
+
+ beforeEach(() => {
+ repository = new MockClipboardRepository();
+ client = new MockTabsClient();
+ consoleClient = new MockConsoleClient();
+ sut = new ClipboardUseCase({ repository, client: client, consoleClient });
+ });
+
+ describe('#yankCurrentURL', () => {
+ it('yanks current url', async () => {
+ let yanked = await sut.yankCurrentURL();
+
+ expect(yanked).to.equal(window.location.href);
+ expect(repository.clipboard).to.equal(yanked);
+ expect(consoleClient.text).to.equal('Yanked ' + yanked);
+ });
+ });
+
+ describe('#openOrSearch', () => {
+ it('opens url from the clipboard', async () => {
+ let url = 'https://github.com/ueokande/vim-vixen'
+ repository.clipboard = url;
+ await sut.openOrSearch(true);
+
+ expect(client.last).to.equal(url);
+ });
+
+ it('opens search results from the clipboard', async () => {
+ repository.clipboard = 'banana';
+ await sut.openOrSearch(true);
+
+ expect(client.last).to.equal('https://google.com/search?q=banana');
+ });
+ });
+});
+
diff --git a/test/content/usecases/FindUseCase.test.ts b/test/content/usecases/FindUseCase.test.ts
new file mode 100644
index 0000000..c7bfd39
--- /dev/null
+++ b/test/content/usecases/FindUseCase.test.ts
@@ -0,0 +1,161 @@
+import FindRepository from '../../../src/content/repositories/FindRepository';
+import FindPresenter from '../../../src/content/presenters/FindPresenter';
+import FindClient from '../../../src/content/client/FindClient';
+import FindUseCase from '../../../src/content/usecases/FindUseCase';
+import MockConsoleClient from '../mock/MockConsoleClient';
+import { expect } from 'chai';
+
+class MockFindRepository implements FindRepository {
+ public keyword: string | null;
+
+ constructor() {
+ this.keyword = null;
+ }
+
+ getLastKeyword(): string | null {
+ return this.keyword;
+ }
+
+ setLastKeyword(keyword: string): void {
+ this.keyword = keyword;
+ }
+}
+
+class MockFindPresenter implements FindPresenter {
+ public document: string;
+
+ public highlighted: boolean;
+
+ constructor() {
+ this.document = '';
+ this.highlighted = false;
+ }
+
+ find(keyword: string, _backward: boolean): boolean {
+ let found = this.document.includes(keyword);
+ this.highlighted = found;
+ return found;
+ }
+
+ clearSelection(): void {
+ this.highlighted = false;
+ }
+}
+
+class MockFindClient implements FindClient {
+ public keyword: string | null;
+
+ constructor() {
+ this.keyword = null;
+ }
+
+ getGlobalLastKeyword(): Promise<string | null> {
+ return Promise.resolve(this.keyword);
+ }
+
+ setGlobalLastKeyword(keyword: string): Promise<void> {
+ this.keyword = keyword;
+ return Promise.resolve();
+ }
+}
+
+describe('FindUseCase', () => {
+ let repository: MockFindRepository;
+ let presenter: MockFindPresenter;
+ let client: MockFindClient;
+ let consoleClient: MockConsoleClient;
+ let sut: FindUseCase;
+
+ beforeEach(() => {
+ repository = new MockFindRepository();
+ presenter = new MockFindPresenter();
+ client = new MockFindClient();
+ consoleClient = new MockConsoleClient();
+ sut = new FindUseCase({ repository, presenter, client, consoleClient });
+ });
+
+ describe('#startFind', () => {
+ it('find next by ketword', async() => {
+ presenter.document = 'monkey punch';
+
+ await sut.startFind('monkey');
+
+ expect(await presenter.highlighted).to.be.true;
+ expect(await consoleClient.text).to.equal('Pattern found: monkey');
+ expect(await repository.getLastKeyword()).to.equal('monkey');
+ expect(await client.getGlobalLastKeyword()).to.equal('monkey');
+ });
+
+ it('find next by last keyword', async() => {
+ presenter.document = 'gorilla kick';
+ repository.keyword = 'gorilla';
+
+ await sut.startFind(undefined);
+
+ expect(await presenter.highlighted).to.be.true;
+ expect(await consoleClient.text).to.equal('Pattern found: gorilla');
+ expect(await repository.getLastKeyword()).to.equal('gorilla');
+ expect(await client.getGlobalLastKeyword()).to.equal('gorilla');
+ });
+
+ it('find next by global last keyword', async() => {
+ presenter.document = 'chimpanzee typing';
+
+ repository.keyword = null;
+ client.keyword = 'chimpanzee';
+
+ await sut.startFind(undefined);
+
+ expect(await presenter.highlighted).to.be.true;
+ expect(await consoleClient.text).to.equal('Pattern found: chimpanzee');
+ expect(await repository.getLastKeyword()).to.equal('chimpanzee');
+ expect(await client.getGlobalLastKeyword()).to.equal('chimpanzee');
+ });
+
+ it('find not found error', async() => {
+ presenter.document = 'zoo';
+
+ await sut.startFind('giraffe');
+
+ expect(await presenter.highlighted).to.be.false;
+ expect(await consoleClient.text).to.equal('Pattern not found: giraffe');
+ expect(await repository.getLastKeyword()).to.equal('giraffe');
+ expect(await client.getGlobalLastKeyword()).to.equal('giraffe');
+ });
+
+ it('show errors when no last keywords', async() => {
+ repository.keyword = null;
+ client.keyword = null;
+
+ await sut.startFind(undefined);
+
+ expect(await consoleClient.text).to.equal('No previous search keywords');
+ expect(await consoleClient.isError).to.be.true;
+ });
+ });
+
+ describe('#findNext', () => {
+ it('finds by last keyword', async() => {
+ presenter.document = 'monkey punch';
+ repository.keyword = 'monkey';
+
+ await sut.findNext();
+
+ expect(await presenter.highlighted).to.be.true;
+ expect(await consoleClient.text).to.equal('Pattern found: monkey');
+ });
+
+ it('show errors when no last keywords', async() => {
+ repository.keyword = null;
+ client.keyword = null;
+
+ await sut.findNext();
+
+ expect(await consoleClient.text).to.equal('No previous search keywords');
+ expect(await consoleClient.isError).to.be.true;
+ });
+ });
+
+ describe('#findPrev', () => {
+ });
+});
diff --git a/test/content/hint-key-producer.test.ts b/test/content/usecases/HintKeyProducer.test.ts
index dcf477d..feafffb 100644
--- a/test/content/hint-key-producer.test.ts
+++ b/test/content/usecases/HintKeyProducer.test.ts
@@ -1,9 +1,10 @@
-import HintKeyProducer from 'content/hint-key-producer';
+import HintKeyProducer from '../../../src/content/usecases/HintKeyProducer';
+import { expect } from 'chai';
describe('HintKeyProducer class', () => {
describe('#constructor', () => {
it('throws an exception on empty charset', () => {
- expect(() => new HintKeyProducer([])).to.throw(TypeError);
+ expect(() => new HintKeyProducer('')).to.throw(TypeError);
});
});
diff --git a/test/content/usecases/MarkUseCase.test.ts b/test/content/usecases/MarkUseCase.test.ts
new file mode 100644
index 0000000..4f2dee4
--- /dev/null
+++ b/test/content/usecases/MarkUseCase.test.ts
@@ -0,0 +1,107 @@
+import MarkRepository from '../../../src/content/repositories/MarkRepository';
+import MarkUseCase from '../../../src/content/usecases/MarkUseCase';
+import MarkClient from '../../../src/content/client/MarkClient';
+import MockConsoleClient from '../mock/MockConsoleClient';
+import MockScrollPresenter from '../mock/MockScrollPresenter';
+import Mark from '../../../src/content/domains/Mark';
+import { expect } from 'chai';
+
+class MockMarkRepository implements MarkRepository {
+ private current: {[key: string]: Mark};
+
+ constructor() {
+ this.current = {};
+ }
+
+ set(key: string, mark: Mark): void {
+ this.current[key] = mark;
+ }
+
+ get(key: string): Mark | null {
+ return this.current[key];
+ }
+}
+
+class MockMarkClient implements MarkClient {
+ public marks: {[key: string]: Mark};
+ public last: string;
+
+ constructor() {
+ this.marks = {};
+ this.last = '';
+ }
+
+ setGloablMark(key: string, mark: Mark): Promise<void> {
+ this.marks[key] = mark;
+ return Promise.resolve();
+ }
+
+ jumpGlobalMark(key: string): Promise<void> {
+ this.last = key
+ return Promise.resolve();
+ }
+}
+
+describe('MarkUseCase', () => {
+ let repository: MockMarkRepository;
+ let client: MockMarkClient;
+ let consoleClient: MockConsoleClient;
+ let scrollPresenter: MockScrollPresenter;
+ let sut: MarkUseCase;
+
+ beforeEach(() => {
+ repository = new MockMarkRepository();
+ client = new MockMarkClient();
+ consoleClient = new MockConsoleClient();
+ scrollPresenter = new MockScrollPresenter();
+ sut = new MarkUseCase({
+ repository, client, consoleClient, scrollPresenter,
+ });
+ });
+
+ describe('#set', () => {
+ it('sets local mark', async() => {
+ scrollPresenter.scrollTo(10, 20, false);
+
+ await sut.set('x');
+
+ expect(repository.get('x')).to.deep.equals({ x: 10, y: 20 });
+ expect(consoleClient.text).to.equal("Set local mark to 'x'");
+ });
+
+ it('sets global mark', async() => {
+ scrollPresenter.scrollTo(30, 40, false);
+
+ await sut.set('Z');
+
+ expect(client.marks['Z']).to.deep.equals({ x: 30, y: 40 });
+ expect(consoleClient.text).to.equal("Set global mark to 'Z'");
+ });
+ });
+
+ describe('#jump', () => {
+ it('jumps to local mark', async() => {
+ repository.set('x', { x: 20, y: 40 });
+
+ await sut.jump('x');
+
+ expect(scrollPresenter.getScroll()).to.deep.equals({ x: 20, y: 40 });
+ });
+
+ it('throws an error when no local marks', () => {
+ return sut.jump('a').then(() => {
+ throw new Error('error');
+ }).catch((e) => {
+ expect(e).to.be.instanceof(Error);
+ })
+ })
+
+ it('jumps to global mark', async() => {
+ client.marks['Z'] = { x: 20, y: 0 };
+
+ await sut.jump('Z');
+
+ expect(client.last).to.equal('Z')
+ });
+ });
+});
diff --git a/test/content/usecases/SettingUseCaase.test.ts b/test/content/usecases/SettingUseCaase.test.ts
new file mode 100644
index 0000000..02cef78
--- /dev/null
+++ b/test/content/usecases/SettingUseCaase.test.ts
@@ -0,0 +1,71 @@
+import SettingRepository from '../../../src/content/repositories/SettingRepository';
+import SettingClient from '../../../src/content/client/SettingClient';
+import SettingUseCase from '../../../src/content/usecases/SettingUseCase';
+import Settings, { DefaultSetting } from '../../../src/shared/Settings';
+import { expect } from 'chai';
+
+class MockSettingRepository implements SettingRepository {
+ private current: Settings;
+
+ constructor() {
+ this.current = DefaultSetting;
+ }
+
+ set(settings: Settings): void {
+ this.current= settings;
+ }
+
+ get(): Settings {
+ return this.current;
+ }
+}
+
+class MockSettingClient implements SettingClient {
+ private data: Settings;
+
+ constructor(data: Settings) {
+ this.data = data;
+ }
+
+ load(): Promise<Settings> {
+ return Promise.resolve(this.data);
+ }
+}
+
+describe('AddonEnabledUseCase', () => {
+ let repository: MockSettingRepository;
+ let client: MockSettingClient;
+ let sut: SettingUseCase;
+
+ beforeEach(() => {
+ let testSettings = {
+ keymaps: {},
+ search: {
+ default: 'google',
+ engines: {
+ google: 'https://google.com/?q={}',
+ }
+ },
+ properties: {
+ hintchars: 'abcd1234',
+ smoothscroll: false,
+ complete: 'sbh',
+ },
+ blacklist: [],
+ };
+
+ repository = new MockSettingRepository();
+ client = new MockSettingClient(testSettings);
+ sut = new SettingUseCase({ repository, client });
+ });
+
+ describe('#reload', () => {
+ it('loads settings and store to repository', async() => {
+ let settings = await sut.reload();
+ expect(settings.properties.hintchars).to.equal('abcd1234');
+
+ let saved = repository.get();
+ expect(saved.properties.hintchars).to.equal('abcd1234');
+ });
+ });
+});