aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShin'ya UEOKA <ueokande@i-beam.org>2019-10-06 12:39:56 +0000
committerShin'ya UEOKA <ueokande@i-beam.org>2019-10-08 11:43:10 +0000
commit7528fe831fa4e17e5c427e89025ac76b078a9313 (patch)
tree9c40749b231ec1a8bdea3a9d720eb78b20ab7bab
parent9ff80fcac3600401c9fed053cc8422f89c404940 (diff)
Ignore keys on partial blacklist
-rw-r--r--src/content/di.ts2
-rw-r--r--src/content/repositories/AddressRepository.ts9
-rw-r--r--src/content/usecases/KeymapUseCase.ts15
-rw-r--r--test/content/usecases/KeymapUseCase.test.ts133
4 files changed, 159 insertions, 0 deletions
diff --git a/src/content/di.ts b/src/content/di.ts
index e18806a..63103a1 100644
--- a/src/content/di.ts
+++ b/src/content/di.ts
@@ -2,6 +2,7 @@
import { AddonEnabledRepositoryImpl } from './repositories/AddonEnabledRepository';
import { AddonIndicatorClientImpl } from './client/AddonIndicatorClient';
+import { AddressRepositoryImpl } from './repositories/AddressRepository';
import { ClipboardRepositoryImpl } from './repositories/ClipboardRepository';
import { ConsoleClientImpl } from './client/ConsoleClient';
import { ConsoleFramePresenterImpl } from './presenters/ConsoleFramePresenter';
@@ -31,6 +32,7 @@ import { container } from 'tsyringe';
container.register('FollowMasterClient', { useValue: new FollowMasterClientImpl(window.top) });
container.register('AddonEnabledRepository', { useClass: AddonEnabledRepositoryImpl });
container.register('AddonIndicatorClient', { useClass: AddonIndicatorClientImpl });
+container.register('AddressRepository', { useClass: AddressRepositoryImpl });
container.register('ClipboardRepository', { useClass: ClipboardRepositoryImpl });
container.register('ConsoleClient', { useClass: ConsoleClientImpl });
container.register('ConsoleFramePresenter', { useClass: ConsoleFramePresenterImpl });
diff --git a/src/content/repositories/AddressRepository.ts b/src/content/repositories/AddressRepository.ts
new file mode 100644
index 0000000..6f9487b
--- /dev/null
+++ b/src/content/repositories/AddressRepository.ts
@@ -0,0 +1,9 @@
+export default interface AddressRepository {
+ getCurrentURL(): URL
+}
+
+export class AddressRepositoryImpl implements AddressRepository {
+ getCurrentURL(): URL {
+ return new URL(window.location.href);
+ }
+}
diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts
index 495f6d0..67d667d 100644
--- a/src/content/usecases/KeymapUseCase.ts
+++ b/src/content/usecases/KeymapUseCase.ts
@@ -6,6 +6,7 @@ import * as operations from '../../shared/operations';
import Keymaps from '../../shared/settings/Keymaps';
import Key from '../../shared/settings/Key';
import KeySequence from '../../shared/settings/KeySequence';
+import AddressRepository from '../repositories/AddressRepository';
type KeymapEntityMap = Map<KeySequence, operations.Operation>;
@@ -25,11 +26,19 @@ export default class KeymapUseCase {
@inject('AddonEnabledRepository')
private addonEnabledRepository: AddonEnabledRepository,
+
+ @inject('AddressRepository')
+ private addressRepository: AddressRepository,
) {
}
nextOp(key: Key): operations.Operation | null {
let sequence = this.repository.enqueueKey(key);
+ if (sequence.length() === 1 && this.blacklistKey(key)) {
+ // ignore if the input starts with black list keys
+ this.repository.clear();
+ return null;
+ }
let keymaps = this.keymapEntityMap();
let matched = Array.from(keymaps.keys()).filter(
@@ -71,4 +80,10 @@ export default class KeymapUseCase {
) as [KeySequence, operations.Operation][];
return new Map<KeySequence, operations.Operation>(entries);
}
+
+ private blacklistKey(key: Key): boolean {
+ let url = this.addressRepository.getCurrentURL();
+ let blacklist = this.settingRepository.get().blacklist;
+ return blacklist.includeKey(url, key);
+ }
}
diff --git a/test/content/usecases/KeymapUseCase.test.ts b/test/content/usecases/KeymapUseCase.test.ts
new file mode 100644
index 0000000..5f2feba
--- /dev/null
+++ b/test/content/usecases/KeymapUseCase.test.ts
@@ -0,0 +1,133 @@
+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', () => {
+ it('returns matched operation', () => {
+ let settings = Settings.fromJSON({
+ keymaps: {
+ k: {type: 'scroll.vertically', count: -1},
+ j: {type: 'scroll.vertically', count: 1},
+ gg: {type: 'scroll.top'},
+ },
+ });
+ let sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(true),
+ new MockAddressRepository(new URL('https://example.com')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('k'))).to.deep.equal({type: 'scroll.vertically', count: -1});
+ expect(sut.nextOp(Key.fromMapKey('j'))).to.deep.equal({type: 'scroll.vertically', count: 1});
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.deep.equal({type: 'scroll.top'});
+ expect(sut.nextOp(Key.fromMapKey('z'))).to.be.null;
+ });
+
+ it('returns only ADDON_ENABLE and ADDON_TOGGLE_ENABLED operation', () => {
+ let settings = Settings.fromJSON({
+ keymaps: {
+ k: {type: 'scroll.vertically', count: -1},
+ a: {type: 'addon.enable'},
+ b: {type: 'addon.toggle.enabled'},
+ },
+ });
+ let sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(false),
+ new MockAddressRepository(new URL('https://example.com')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('k'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('a'))).to.deep.equal({type: 'addon.enable'});
+ expect(sut.nextOp(Key.fromMapKey('b'))).to.deep.equal({type: 'addon.toggle.enabled'});
+ });
+
+ it('blocks keys in the partial blacklist', () => {
+ let 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: ['<S-G>'] }
+ ],
+ });
+
+ let sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(true),
+ new MockAddressRepository(new URL('https://example.com')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('k'))).to.deep.equal({type: 'scroll.vertically', count: -1});
+ expect(sut.nextOp(Key.fromMapKey('j'))).to.deep.equal({type: 'scroll.vertically', count: 1});
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('G'))).to.deep.equal({type: 'scroll.bottom'});
+
+ sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(true),
+ new MockAddressRepository(new URL('https://example.org')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.deep.equal({type: 'scroll.top'});
+ expect(sut.nextOp(Key.fromMapKey('G'))).to.be.null;
+ });
+});