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'); +    }); +  }); +}); | 
