Ignore keys on partial blacklist
This commit is contained in:
parent
9ff80fcac3
commit
7528fe831f
4 changed files with 159 additions and 0 deletions
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { AddonEnabledRepositoryImpl } from './repositories/AddonEnabledRepository';
|
import { AddonEnabledRepositoryImpl } from './repositories/AddonEnabledRepository';
|
||||||
import { AddonIndicatorClientImpl } from './client/AddonIndicatorClient';
|
import { AddonIndicatorClientImpl } from './client/AddonIndicatorClient';
|
||||||
|
import { AddressRepositoryImpl } from './repositories/AddressRepository';
|
||||||
import { ClipboardRepositoryImpl } from './repositories/ClipboardRepository';
|
import { ClipboardRepositoryImpl } from './repositories/ClipboardRepository';
|
||||||
import { ConsoleClientImpl } from './client/ConsoleClient';
|
import { ConsoleClientImpl } from './client/ConsoleClient';
|
||||||
import { ConsoleFramePresenterImpl } from './presenters/ConsoleFramePresenter';
|
import { ConsoleFramePresenterImpl } from './presenters/ConsoleFramePresenter';
|
||||||
|
@ -31,6 +32,7 @@ import { container } from 'tsyringe';
|
||||||
container.register('FollowMasterClient', { useValue: new FollowMasterClientImpl(window.top) });
|
container.register('FollowMasterClient', { useValue: new FollowMasterClientImpl(window.top) });
|
||||||
container.register('AddonEnabledRepository', { useClass: AddonEnabledRepositoryImpl });
|
container.register('AddonEnabledRepository', { useClass: AddonEnabledRepositoryImpl });
|
||||||
container.register('AddonIndicatorClient', { useClass: AddonIndicatorClientImpl });
|
container.register('AddonIndicatorClient', { useClass: AddonIndicatorClientImpl });
|
||||||
|
container.register('AddressRepository', { useClass: AddressRepositoryImpl });
|
||||||
container.register('ClipboardRepository', { useClass: ClipboardRepositoryImpl });
|
container.register('ClipboardRepository', { useClass: ClipboardRepositoryImpl });
|
||||||
container.register('ConsoleClient', { useClass: ConsoleClientImpl });
|
container.register('ConsoleClient', { useClass: ConsoleClientImpl });
|
||||||
container.register('ConsoleFramePresenter', { useClass: ConsoleFramePresenterImpl });
|
container.register('ConsoleFramePresenter', { useClass: ConsoleFramePresenterImpl });
|
||||||
|
|
9
src/content/repositories/AddressRepository.ts
Normal file
9
src/content/repositories/AddressRepository.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default interface AddressRepository {
|
||||||
|
getCurrentURL(): URL
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddressRepositoryImpl implements AddressRepository {
|
||||||
|
getCurrentURL(): URL {
|
||||||
|
return new URL(window.location.href);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import * as operations from '../../shared/operations';
|
||||||
import Keymaps from '../../shared/settings/Keymaps';
|
import Keymaps from '../../shared/settings/Keymaps';
|
||||||
import Key from '../../shared/settings/Key';
|
import Key from '../../shared/settings/Key';
|
||||||
import KeySequence from '../../shared/settings/KeySequence';
|
import KeySequence from '../../shared/settings/KeySequence';
|
||||||
|
import AddressRepository from '../repositories/AddressRepository';
|
||||||
|
|
||||||
type KeymapEntityMap = Map<KeySequence, operations.Operation>;
|
type KeymapEntityMap = Map<KeySequence, operations.Operation>;
|
||||||
|
|
||||||
|
@ -25,11 +26,19 @@ export default class KeymapUseCase {
|
||||||
|
|
||||||
@inject('AddonEnabledRepository')
|
@inject('AddonEnabledRepository')
|
||||||
private addonEnabledRepository: AddonEnabledRepository,
|
private addonEnabledRepository: AddonEnabledRepository,
|
||||||
|
|
||||||
|
@inject('AddressRepository')
|
||||||
|
private addressRepository: AddressRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextOp(key: Key): operations.Operation | null {
|
nextOp(key: Key): operations.Operation | null {
|
||||||
let sequence = this.repository.enqueueKey(key);
|
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 keymaps = this.keymapEntityMap();
|
||||||
let matched = Array.from(keymaps.keys()).filter(
|
let matched = Array.from(keymaps.keys()).filter(
|
||||||
|
@ -71,4 +80,10 @@ export default class KeymapUseCase {
|
||||||
) as [KeySequence, operations.Operation][];
|
) as [KeySequence, operations.Operation][];
|
||||||
return new Map<KeySequence, operations.Operation>(entries);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
133
test/content/usecases/KeymapUseCase.test.ts
Normal file
133
test/content/usecases/KeymapUseCase.test.ts
Normal file
|
@ -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;
|
||||||
|
});
|
||||||
|
});
|
Reference in a new issue