import "reflect-metadata"; import KeymapUseCase from '../../../src/content/usecases/KeymapUseCase'; import {expect} from 'chai'; import SettingRepository from "../../../src/content/repositories/SettingRepository"; import Settings from "../../../src/shared/settings/Settings"; import AddonEnabledRepository from "../../../src/content/repositories/AddonEnabledRepository"; import {KeymapRepositoryImpl} from "../../../src/content/repositories/KeymapRepository"; import Key from "../../../src/shared/settings/Key"; import AddressRepository from "../../../src/content/repositories/AddressRepository"; class MockSettingRepository implements SettingRepository { constructor( private readonly settings: Settings, ) { } get(): Settings { return this.settings; } set(_setting: Settings): void { throw new Error('TODO'); } } class MockAddonEnabledRepository implements AddonEnabledRepository { constructor( private readonly enabled: boolean, ) { } get(): boolean { return this.enabled; } set(_on: boolean): void { throw new Error('TODO'); } } class MockAddressRepository implements AddressRepository { constructor( private url: URL, ) { } getCurrentURL(): URL { return this.url; } } describe('KeymapUseCase', () => { context('with no-digis keymaps', () => { const settings = Settings.fromJSON({ keymaps: { k: {type: 'scroll.vertically', count: -1}, j: {type: 'scroll.vertically', count: 1}, gg: {type: 'scroll.top'}, }, }); let sut: KeymapUseCase; before(() => { sut = new KeymapUseCase( new KeymapRepositoryImpl(), new MockSettingRepository(settings), new MockAddonEnabledRepository(true), new MockAddressRepository(new URL('https://example.com')), ); }); it('returns matched operation', () => { expect(sut.nextOps(Key.fromMapKey('k'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: -1}}); expect(sut.nextOps(Key.fromMapKey('j'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: 1}}); expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.top'}}); expect(sut.nextOps(Key.fromMapKey('z'))).to.be.null; }); it('repeats n-times by numeric prefix and multiple key operations', () => { expect(sut.nextOps(Key.fromMapKey('1'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 10, op: {type: "scroll.top"}}); }); }); context('when keymaps containing numeric mappings', () => { const settings = Settings.fromJSON({ keymaps: { 20: {type: "scroll.top"}, g5: {type: 'scroll.bottom'}, }, }); let sut: KeymapUseCase; before(() => { sut = new KeymapUseCase( new KeymapRepositoryImpl(), new MockSettingRepository(settings), new MockAddonEnabledRepository(true), new MockAddressRepository(new URL('https://example.com')), ); }); it('returns the matched operation ends with digit', () => { expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('5'))).to.be.deep.equal({ repeat: 1, op: { type: 'scroll.bottom'}}); }); it('returns an operation matched the operation with digit keymaps', () => { expect(sut.nextOps(Key.fromMapKey('2'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('0'))).to.be.deep.equal({ repeat: 1, op: { type: 'scroll.top'}}); }); it('returns operations repeated by numeric prefix', () => { expect(sut.nextOps(Key.fromMapKey('2'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('5'))).to.be.deep.equal({ repeat: 2, op: { type: 'scroll.bottom'}}); }); it('does not matches with digit operation with numeric prefix', () => { expect(sut.nextOps(Key.fromMapKey('3'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('2'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('5'))).to.be.deep.equal({ repeat: 320, op: { type: 'scroll.bottom'}}); }); }); context('when the keys are mismatched with the operations', () => { const settings = Settings.fromJSON({ keymaps: { gg: {type: "scroll.top"}, G: {type: "scroll.bottom"}, }, }); let sut: KeymapUseCase; before(() => { sut = new KeymapUseCase( new KeymapRepositoryImpl(), new MockSettingRepository(settings), new MockAddonEnabledRepository(true), new MockAddressRepository(new URL('https://example.com')), ); }); it('clears input keys with no-matched operations', () => { expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('x'))).to.be.null; // clear expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({repeat: 1, op: {type: "scroll.top"}}); }); it('clears input keys and the prefix with no-matched operations', () => { expect(sut.nextOps(Key.fromMapKey('1'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('x'))).to.be.null; // clear expect(sut.nextOps(Key.fromMapKey('1'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 10, op: {type: "scroll.top"}}); }); }); context('when the site matches to the blacklist', () => { const settings = Settings.fromJSON({ keymaps: { k: {type: 'scroll.vertically', count: -1}, a: {type: 'addon.enable'}, b: {type: 'addon.toggle.enabled'}, }, }); let sut: KeymapUseCase; before(() => { sut = new KeymapUseCase( new KeymapRepositoryImpl(), new MockSettingRepository(settings), new MockAddonEnabledRepository(false), new MockAddressRepository(new URL('https://example.com')), ); }); it('returns only ADDON_ENABLE and ADDON_TOGGLE_ENABLED operation', () => { expect(sut.nextOps(Key.fromMapKey('k'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('a'))).to.deep.equal({ repeat: 1, op: {type: 'addon.enable'}}); expect(sut.nextOps(Key.fromMapKey('b'))).to.deep.equal({ repeat: 1, op: {type: 'addon.toggle.enabled'}}); }); }); context('when the site matches to the partial blacklist', () => { const settings = Settings.fromJSON({ keymaps: { k: {type: 'scroll.vertically', count: -1}, j: {type: 'scroll.vertically', count: 1}, gg: {type: "scroll.top"}, G: {type: "scroll.bottom"}, }, blacklist: [ { url: "example.com", keys: ['g'] }, { url: "example.org", keys: [''] } ], }); it('blocks keys in the partial blacklist', () => { let sut = new KeymapUseCase( new KeymapRepositoryImpl(), new MockSettingRepository(settings), new MockAddonEnabledRepository(true), new MockAddressRepository(new URL('https://example.com')), ); expect(sut.nextOps(Key.fromMapKey('k'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: -1}}); expect(sut.nextOps(Key.fromMapKey('j'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: 1}}); expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('G'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.bottom'}}); sut = new KeymapUseCase( new KeymapRepositoryImpl(), new MockSettingRepository(settings), new MockAddonEnabledRepository(true), new MockAddressRepository(new URL('https://example.org')), ); expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.top'}}); expect(sut.nextOps(Key.fromMapKey('G'))).to.be.null; }); }); });