diff options
Diffstat (limited to 'test')
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'); + }); + }); +}); |