From 62a86c525378610444a9976dd4409ea207174d20 Mon Sep 17 00:00:00 2001 From: Shin'ya UEOKA Date: Thu, 3 Oct 2019 12:15:12 +0000 Subject: Make key class --- test/content/repositories/KeymapRepository.test.ts | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'test/content/repositories') diff --git a/test/content/repositories/KeymapRepository.test.ts b/test/content/repositories/KeymapRepository.test.ts index 34704d9..8f0be67 100644 --- a/test/content/repositories/KeymapRepository.test.ts +++ b/test/content/repositories/KeymapRepository.test.ts @@ -1,5 +1,6 @@ import KeymapRepository, { KeymapRepositoryImpl } from '../../../src/content/repositories/KeymapRepository'; +import Key from '../../../src/content/domains/Key' import { expect } from 'chai'; describe('KeymapRepositoryImpl', () => { @@ -11,24 +12,25 @@ describe('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' }, - ]); + sut.enqueueKey(Key.fromMapKey('a'); + sut.enqueueKey(Key.fromMapKey('b'); + let sequence = sut.enqueueKey(Key.fromMapKey('c')); + + let keys = sequence.getKeyArray(); + expect(keys[0].equals(Key.fromMapKey('a'))).to.be.true; + expect(keys[1].equals(Key.fromMapKey('b'))).to.be.true; + expect(keys[2].equals(Key.fromMapKey('c'))).to.be.true; }); }); describe('#clear()', () => { it('clears keys', () => { - sut.enqueueKey({ key: 'a' }); - sut.enqueueKey({ key: 'b' }); - sut.enqueueKey({ key: 'c' }); + sut.enqueueKey(Key.fromMapKey('a'); + sut.enqueueKey(Key.fromMapKey('b'); + sut.enqueueKey(Key.fromMapKey('c'); sut.clear(); - let sequence = sut.enqueueKey({ key: 'a' }); + let sequence = sut.enqueueKey(Key.fromMapKey('a')); expect(sequence.length()).to.equal(1); }); }); -- cgit v1.2.3 From b496cea5827165bd23a503231f94f708a976cad4 Mon Sep 17 00:00:00 2001 From: Shin'ya UEOKA Date: Thu, 3 Oct 2019 12:32:32 +0000 Subject: Make KeySequence class --- src/content/domains/KeySequence.ts | 60 +++++++++------------- src/content/repositories/KeymapRepository.ts | 4 +- src/content/usecases/KeymapUseCase.ts | 4 +- test/content/domains/KeySequence.test.ts | 57 ++++++++++---------- test/content/repositories/KeymapRepository.test.ts | 8 +-- 5 files changed, 61 insertions(+), 72 deletions(-) (limited to 'test/content/repositories') diff --git a/src/content/domains/KeySequence.ts b/src/content/domains/KeySequence.ts index 61ceab1..abae61a 100644 --- a/src/content/domains/KeySequence.ts +++ b/src/content/domains/KeySequence.ts @@ -1,14 +1,9 @@ import Key from './Key'; export default class KeySequence { - private keys: Key[]; - - private constructor(keys: Key[]) { - this.keys = keys; - } - - static from(keys: Key[]): KeySequence { - return new KeySequence(keys); + constructor( + public readonly keys: Key[], + ) { } push(key: Key): number { @@ -31,34 +26,29 @@ export default class KeySequence { return true; } - getKeyArray(): Key[] { - return this.keys; - } -} - -export const fromMapKeys = (keys: string): KeySequence => { - const fromMapKeysRecursive = ( - remainings: string, mappedKeys: Key[], - ): Key[] => { - if (remainings.length === 0) { - return mappedKeys; - } - - let nextPos = 1; - if (remainings.startsWith('<')) { - let ltPos = remainings.indexOf('>'); - if (ltPos > 0) { - nextPos = ltPos + 1; + static fromMapKeys(keys: string): KeySequence { + const fromMapKeysRecursive = ( + remaining: string, mappedKeys: Key[], + ): Key[] => { + if (remaining.length === 0) { + return mappedKeys; } - } - return fromMapKeysRecursive( - remainings.slice(nextPos), - mappedKeys.concat([Key.fromMapKey(remainings.slice(0, nextPos))]) - ); - }; + let nextPos = 1; + if (remaining.startsWith('<')) { + let ltPos = remaining.indexOf('>'); + if (ltPos > 0) { + nextPos = ltPos + 1; + } + } - let data = fromMapKeysRecursive(keys, []); - return KeySequence.from(data); -}; + return fromMapKeysRecursive( + remaining.slice(nextPos), + mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) + ); + }; + let data = fromMapKeysRecursive(keys, []); + return new KeySequence(data); + } +} diff --git a/src/content/repositories/KeymapRepository.ts b/src/content/repositories/KeymapRepository.ts index d7b5b7d..4463678 100644 --- a/src/content/repositories/KeymapRepository.ts +++ b/src/content/repositories/KeymapRepository.ts @@ -7,7 +7,7 @@ export default interface KeymapRepository { clear(): void; } -let current: KeySequence = KeySequence.from([]); +let current: KeySequence = new KeySequence([]); export class KeymapRepositoryImpl { @@ -17,6 +17,6 @@ export class KeymapRepositoryImpl { } clear(): void { - current = KeySequence.from([]); + current = new KeySequence([]); } } diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts index d0d039a..0f654c8 100644 --- a/src/content/usecases/KeymapUseCase.ts +++ b/src/content/usecases/KeymapUseCase.ts @@ -5,7 +5,7 @@ import AddonEnabledRepository from '../repositories/AddonEnabledRepository'; import * as operations from '../../shared/operations'; import { Keymaps } from '../../shared/Settings'; import Key from '../domains/Key'; -import KeySequence, * as keySequenceUtils from '../domains/KeySequence'; +import KeySequence from '../domains/KeySequence'; type KeymapEntityMap = Map; @@ -71,7 +71,7 @@ export default class KeymapUseCase { }; let entries = Object.entries(keymaps).map((entry) => { return [ - keySequenceUtils.fromMapKeys(entry[0]), + KeySequence.fromMapKeys(entry[0]), entry[1], ]; }) as [KeySequence, operations.Operation][]; diff --git a/test/content/domains/KeySequence.test.ts b/test/content/domains/KeySequence.test.ts index 9afc360..62af165 100644 --- a/test/content/domains/KeySequence.test.ts +++ b/test/content/domains/KeySequence.test.ts @@ -1,49 +1,48 @@ -import KeySequence, * as utils from '../../../src/content/domains/KeySequence'; +import KeySequence from '../../../src/content/domains/KeySequence'; import Key from '../../../src/content/domains/Key'; import { expect } from 'chai' describe("KeySequence", () => { describe('#push', () => { it('append a key to the sequence', () => { - let seq = KeySequence.from([]); + let seq = new KeySequence([]); seq.push(Key.fromMapKey('g')); seq.push(Key.fromMapKey('')); - let array = seq.getKeyArray(); - expect(array[0].key).to.equal('g'); - expect(array[1].key).to.equal('U'); - expect(array[1].shift).to.be.true; + expect(seq.keys[0].key).to.equal('g'); + expect(seq.keys[1].key).to.equal('U'); + expect(seq.keys[1].shift).to.be.true; }) }); describe('#startsWith', () => { it('returns true if the key sequence starts with param', () => { - let seq = KeySequence.from([ + let seq = new KeySequence([ Key.fromMapKey('g'), Key.fromMapKey(''), ]); - expect(seq.startsWith(KeySequence.from([ + expect(seq.startsWith(new KeySequence([ ]))).to.be.true; - expect(seq.startsWith(KeySequence.from([ + expect(seq.startsWith(new KeySequence([ Key.fromMapKey('g'), ]))).to.be.true; - expect(seq.startsWith(KeySequence.from([ + expect(seq.startsWith(new KeySequence([ Key.fromMapKey('g'), Key.fromMapKey(''), ]))).to.be.true; - expect(seq.startsWith(KeySequence.from([ + expect(seq.startsWith(new KeySequence([ Key.fromMapKey('g'), Key.fromMapKey(''), Key.fromMapKey('x'), ]))).to.be.false; - expect(seq.startsWith(KeySequence.from([ + expect(seq.startsWith(new KeySequence([ Key.fromMapKey('h'), ]))).to.be.false; }); it('returns true if the empty sequence starts with an empty sequence', () => { - let seq = KeySequence.from([]); + let seq = new KeySequence([]); - expect(seq.startsWith(KeySequence.from([]))).to.be.true; - expect(seq.startsWith(KeySequence.from([ + expect(seq.startsWith(new KeySequence([]))).to.be.true; + expect(seq.startsWith(new KeySequence([ Key.fromMapKey('h'), ]))).to.be.false; }) @@ -51,23 +50,23 @@ describe("KeySequence", () => { describe('#fromMapKeys', () => { it('returns mapped keys for Shift+Esc', () => { - let keyArray = utils.fromMapKeys('').getKeyArray(); - expect(keyArray).to.have.lengthOf(1); - expect(keyArray[0].key).to.equal('Esc'); - expect(keyArray[0].shift).to.be.true; + let keys = KeySequence.fromMapKeys('').keys; + expect(keys).to.have.lengthOf(1); + expect(keys[0].key).to.equal('Esc'); + expect(keys[0].shift).to.be.true; }); it('returns mapped keys for ad', () => { - let keyArray = utils.fromMapKeys('ad').getKeyArray(); - expect(keyArray).to.have.lengthOf(5); - expect(keyArray[0].key).to.equal('a'); - expect(keyArray[1].ctrl).to.be.true; - expect(keyArray[1].key).to.equal('b'); - expect(keyArray[2].alt).to.be.true; - expect(keyArray[2].key).to.equal('c'); - expect(keyArray[3].key).to.equal('d'); - expect(keyArray[4].meta).to.be.true; - expect(keyArray[4].key).to.equal('e'); + let keys = KeySequence.fromMapKeys('ad').keys; + expect(keys).to.have.lengthOf(5); + expect(keys[0].key).to.equal('a'); + expect(keys[1].ctrl).to.be.true; + expect(keys[1].key).to.equal('b'); + expect(keys[2].alt).to.be.true; + expect(keys[2].key).to.equal('c'); + expect(keys[3].key).to.equal('d'); + expect(keys[4].meta).to.be.true; + expect(keys[4].key).to.equal('e'); }); }) }); diff --git a/test/content/repositories/KeymapRepository.test.ts b/test/content/repositories/KeymapRepository.test.ts index 8f0be67..da5624e 100644 --- a/test/content/repositories/KeymapRepository.test.ts +++ b/test/content/repositories/KeymapRepository.test.ts @@ -16,7 +16,7 @@ describe('KeymapRepositoryImpl', () => { sut.enqueueKey(Key.fromMapKey('b'); let sequence = sut.enqueueKey(Key.fromMapKey('c')); - let keys = sequence.getKeyArray(); + let keys = sequence.keys; expect(keys[0].equals(Key.fromMapKey('a'))).to.be.true; expect(keys[1].equals(Key.fromMapKey('b'))).to.be.true; expect(keys[2].equals(Key.fromMapKey('c'))).to.be.true; @@ -25,9 +25,9 @@ describe('KeymapRepositoryImpl', () => { describe('#clear()', () => { it('clears keys', () => { - sut.enqueueKey(Key.fromMapKey('a'); - sut.enqueueKey(Key.fromMapKey('b'); - sut.enqueueKey(Key.fromMapKey('c'); + sut.enqueueKey(Key.fromMapKey('a')); + sut.enqueueKey(Key.fromMapKey('b')); + sut.enqueueKey(Key.fromMapKey('c')); sut.clear(); let sequence = sut.enqueueKey(Key.fromMapKey('a')); -- cgit v1.2.3 From 410ffbb0376b9399928ef8d4dd13079bfb120e14 Mon Sep 17 00:00:00 2001 From: Shin'ya UEOKA Date: Fri, 4 Oct 2019 04:01:35 +0000 Subject: Make Keymap class --- .../infrastructures/ContentMessageListener.ts | 5 +- src/background/repositories/SettingRepository.ts | 8 ++- src/content/client/SettingClient.ts | 4 +- src/content/usecases/KeymapUseCase.ts | 21 +++---- src/settings/actions/index.ts | 8 +-- src/settings/actions/setting.ts | 6 +- src/settings/components/index.tsx | 12 ++-- src/settings/reducers/setting.ts | 6 +- src/shared/SettingData.ts | 53 +++++++++-------- src/shared/Settings.ts | 27 +++++---- src/shared/settings/Keymaps.ts | 37 ++++++++++++ .../content/repositories/SettingRepository.test.ts | 6 +- test/shared/SettingData.test.ts | 48 ++++++++++------ test/shared/Settings.test.ts | 45 +++------------ test/shared/settings/Keymaps.test.ts | 66 ++++++++++++++++++++++ 15 files changed, 223 insertions(+), 129 deletions(-) create mode 100644 src/shared/settings/Keymaps.ts create mode 100644 test/shared/settings/Keymaps.test.ts (limited to 'test/content/repositories') diff --git a/src/background/infrastructures/ContentMessageListener.ts b/src/background/infrastructures/ContentMessageListener.ts index 3d24741..f348a74 100644 --- a/src/background/infrastructures/ContentMessageListener.ts +++ b/src/background/infrastructures/ContentMessageListener.ts @@ -8,6 +8,7 @@ import AddonEnabledController from '../controllers/AddonEnabledController'; import LinkController from '../controllers/LinkController'; import OperationController from '../controllers/OperationController'; import MarkController from '../controllers/MarkController'; +import { toJSON } from '../../shared/Settings'; @injectable() export default class ContentMessageListener { @@ -101,8 +102,8 @@ export default class ContentMessageListener { return this.commandController.exec(text); } - onSettingsQuery(): Promise { - return this.settingController.getSetting(); + async onSettingsQuery(): Promise { + return toJSON(await this.settingController.getSetting()); } onFindGetKeyword(): Promise { diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts index 2f159e5..c7a0e84 100644 --- a/src/background/repositories/SettingRepository.ts +++ b/src/background/repositories/SettingRepository.ts @@ -1,6 +1,6 @@ import { injectable } from 'tsyringe'; import MemoryStorage from '../infrastructures/MemoryStorage'; -import Settings from '../../shared/Settings'; +import Settings, { valueOf, toJSON } from '../../shared/Settings'; import * as PropertyDefs from '../../shared/property-defs'; const CACHED_SETTING_KEY = 'setting'; @@ -14,11 +14,13 @@ export default class SettingRepository { } get(): Promise { - return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); + let data = this.cache.get(CACHED_SETTING_KEY); + return Promise.resolve(valueOf(data)); } update(value: Settings): void { - return this.cache.set(CACHED_SETTING_KEY, value); + let data = toJSON(value); + return this.cache.set(CACHED_SETTING_KEY, data); } async setProperty( diff --git a/src/content/client/SettingClient.ts b/src/content/client/SettingClient.ts index 0850f11..a7cd1ee 100644 --- a/src/content/client/SettingClient.ts +++ b/src/content/client/SettingClient.ts @@ -1,4 +1,4 @@ -import Settings from '../../shared/Settings'; +import Settings, { valueOf } from '../../shared/Settings'; import * as messages from '../../shared/messages'; export default interface SettingClient { @@ -10,6 +10,6 @@ export class SettingClientImpl { let settings = await browser.runtime.sendMessage({ type: messages.SETTINGS_QUERY, }); - return settings as Settings; + return valueOf(settings); } } diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts index 0f654c8..62cd04c 100644 --- a/src/content/usecases/KeymapUseCase.ts +++ b/src/content/usecases/KeymapUseCase.ts @@ -3,16 +3,16 @@ import KeymapRepository from '../repositories/KeymapRepository'; import SettingRepository from '../repositories/SettingRepository'; import AddonEnabledRepository from '../repositories/AddonEnabledRepository'; import * as operations from '../../shared/operations'; -import { Keymaps } from '../../shared/Settings'; import Key from '../domains/Key'; import KeySequence from '../domains/KeySequence'; +import Keymaps from '../../shared/settings/Keymaps'; type KeymapEntityMap = Map; -const reservedKeymaps: Keymaps = { +const reservedKeymaps = Keymaps.fromJSON({ '': { type: operations.CANCEL }, '': { type: operations.CANCEL }, -}; +}); @injectable() export default class KeymapUseCase { @@ -65,16 +65,11 @@ export default class KeymapUseCase { } private keymapEntityMap(): KeymapEntityMap { - let keymaps = { - ...this.settingRepository.get().keymaps, - ...reservedKeymaps, - }; - let entries = Object.entries(keymaps).map((entry) => { - return [ - KeySequence.fromMapKeys(entry[0]), - entry[1], - ]; - }) as [KeySequence, operations.Operation][]; + let keymaps = this.settingRepository.get().keymaps.combine(reservedKeymaps); + let entries = keymaps.entries().map(entry => [ + KeySequence.fromMapKeys(entry[0]), + entry[1], + ]) as [KeySequence, operations.Operation][]; return new Map(entries); } } diff --git a/src/settings/actions/index.ts b/src/settings/actions/index.ts index b1e996e..dfa41c4 100644 --- a/src/settings/actions/index.ts +++ b/src/settings/actions/index.ts @@ -1,5 +1,5 @@ import { - JSONSettings, FormSettings, SettingSource, + JSONTextSettings, FormSettings, SettingSource, } from '../../shared/SettingData'; // Settings @@ -11,14 +11,14 @@ export const SETTING_SWITCH_TO_JSON = 'setting.switch.to.json'; interface SettingSetSettingsAcion { type: typeof SETTING_SET_SETTINGS; source: SettingSource; - json?: JSONSettings; + json?: JSONTextSettings; form?: FormSettings; } interface SettingShowErrorAction { type: typeof SETTING_SHOW_ERROR; error: string; - json: JSONSettings; + json: JSONTextSettings; } interface SettingSwitchToFormAction { @@ -28,7 +28,7 @@ interface SettingSwitchToFormAction { interface SettingSwitchToJsonAction { type: typeof SETTING_SWITCH_TO_JSON; - json: JSONSettings, + json: JSONTextSettings, } export type SettingAction = diff --git a/src/settings/actions/setting.ts b/src/settings/actions/setting.ts index 9eb416e..9404791 100644 --- a/src/settings/actions/setting.ts +++ b/src/settings/actions/setting.ts @@ -1,7 +1,7 @@ import * as actions from './index'; import * as storages from '../storage'; import SettingData, { - JSONSettings, FormSettings, SettingSource, + JSONTextSettings, FormSettings, SettingSource, } from '../../shared/SettingData'; const load = async(): Promise => { @@ -26,7 +26,7 @@ const save = async(data: SettingData): Promise => { return set(data); }; -const switchToForm = (json: JSONSettings): actions.SettingAction => { +const switchToForm = (json: JSONTextSettings): actions.SettingAction => { try { // toSettings exercise validation let form = FormSettings.fromSettings(json.toSettings()); @@ -44,7 +44,7 @@ const switchToForm = (json: JSONSettings): actions.SettingAction => { }; const switchToJson = (form: FormSettings): actions.SettingAction => { - let json = JSONSettings.fromSettings(form.toSettings()); + let json = JSONTextSettings.fromSettings(form.toSettings()); return { type: actions.SETTING_SWITCH_TO_JSON, json, diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx index eeac2cf..ada6efb 100644 --- a/src/settings/components/index.tsx +++ b/src/settings/components/index.tsx @@ -8,7 +8,7 @@ import BlacklistForm from './form/BlacklistForm'; import PropertiesForm from './form/PropertiesForm'; import * as settingActions from '../../settings/actions/setting'; import SettingData, { - JSONSettings, FormKeymaps, FormSearch, FormSettings, + JSONTextSettings, FormKeymaps, FormSearch, FormSettings, } from '../../shared/SettingData'; import { State as AppState } from '../reducers/setting'; import * as settings from '../../shared/Settings'; @@ -75,7 +75,7 @@ class SettingsComponent extends React.Component { ; } - renderJsonFields(json: JSONSettings, error: string) { + renderJsonFields(json: JSONTextSettings, error: string) { return
{ error={error} onValueChange={this.bindJson.bind(this)} onBlur={this.save.bind(this)} - value={json.toJSON()} + value={json.toJSONText()} />
; } @@ -97,7 +97,7 @@ class SettingsComponent extends React.Component { fields = this.renderFormFields(this.props.form); } else if (this.props.source === 'json') { fields = this.renderJsonFields( - this.props.json as JSONSettings, this.props.error); + this.props.json as JSONTextSettings, this.props.error); } return (
@@ -165,7 +165,7 @@ class SettingsComponent extends React.Component { bindJson(_name: string, value: string) { let data = new SettingData({ source: this.props.source, - json: JSONSettings.valueOf(value), + json: JSONTextSettings.fromText(value), }); this.props.dispatch(settingActions.set(data)); } @@ -183,7 +183,7 @@ class SettingsComponent extends React.Component { return; } this.props.dispatch( - settingActions.switchToForm(this.props.json as JSONSettings)); + settingActions.switchToForm(this.props.json as JSONTextSettings)); this.save(); } } diff --git a/src/settings/reducers/setting.ts b/src/settings/reducers/setting.ts index c4a21c7..89bf1cb 100644 --- a/src/settings/reducers/setting.ts +++ b/src/settings/reducers/setting.ts @@ -1,18 +1,18 @@ import * as actions from '../actions'; import { - JSONSettings, FormSettings, SettingSource, + JSONTextSettings, FormSettings, SettingSource, } from '../../shared/SettingData'; export interface State { source: SettingSource; - json?: JSONSettings; + json?: JSONTextSettings; form?: FormSettings; error: string; } const defaultState: State = { source: SettingSource.JSON, - json: JSONSettings.valueOf(''), + json: JSONTextSettings.fromText(''), error: '', }; diff --git a/src/shared/SettingData.ts b/src/shared/SettingData.ts index 14a7d35..eb83b75 100644 --- a/src/shared/SettingData.ts +++ b/src/shared/SettingData.ts @@ -1,5 +1,6 @@ import * as operations from './operations'; import Settings, * as settings from './Settings'; +import Keymaps from './settings/Keymaps'; export class FormKeymaps { private data: {[op: string]: string}; @@ -8,8 +9,8 @@ export class FormKeymaps { this.data = data; } - toKeymaps(): settings.Keymaps { - let keymaps: settings.Keymaps = {}; + toKeymaps(): Keymaps { + let keymaps: { [key: string]: operations.Operation } = {}; for (let name of Object.keys(this.data)) { let [type, argStr] = name.split('?'); let args = {}; @@ -19,7 +20,7 @@ export class FormKeymaps { let key = this.data[name]; keymaps[key] = operations.valueOf({ type, ...args }); } - return keymaps; + return Keymaps.fromJSON(keymaps); } toJSON(): {[op: string]: string} { @@ -42,10 +43,11 @@ export class FormKeymaps { return new FormKeymaps(data); } - static fromKeymaps(keymaps: settings.Keymaps): FormKeymaps { + static fromKeymaps(keymaps: Keymaps): FormKeymaps { + let json = keymaps.toJSON(); let data: {[op: string]: string} = {}; - for (let key of Object.keys(keymaps)) { - let op = keymaps[key]; + for (let key of Object.keys(json)) { + let op = json[key]; let args = { ...op }; delete args.type; @@ -109,27 +111,32 @@ export class FormSearch { } } -export class JSONSettings { - private json: string; - - constructor(json: any) { - this.json = json; +export class JSONTextSettings { + constructor( + private json: string, + ) { } toSettings(): Settings { return settings.valueOf(JSON.parse(this.json)); } - toJSON(): string { + toJSONText(): string { return this.json; } - static valueOf(o: ReturnType): JSONSettings { - return new JSONSettings(o); + static fromText(o: string): JSONTextSettings { + return new JSONTextSettings(o); } - static fromSettings(data: Settings): JSONSettings { - return new JSONSettings(JSON.stringify(data, undefined, 2)); + static fromSettings(data: Settings): JSONTextSettings { + let json = { + keymaps: data.keymaps.toJSON(), + search: data.search, + properties: data.properties, + blacklist: data.blacklist, + }; + return new JSONTextSettings(JSON.stringify(json, undefined, 2)); } } @@ -192,7 +199,7 @@ export class FormSettings { toSettings(): Settings { return settings.valueOf({ - keymaps: this.keymaps.toKeymaps(), + keymaps: this.keymaps.toKeymaps().toJSON(), search: this.search.toSearchSettings(), properties: this.properties, blacklist: this.blacklist, @@ -244,7 +251,7 @@ export enum SettingSource { export default class SettingData { private source: SettingSource; - private json?: JSONSettings; + private json?: JSONTextSettings; private form?: FormSettings; @@ -252,7 +259,7 @@ export default class SettingData { source, json, form }: { source: SettingSource, - json?: JSONSettings, + json?: JSONTextSettings, form?: FormSettings, }) { this.source = source; @@ -264,7 +271,7 @@ export default class SettingData { return this.source; } - getJSON(): JSONSettings { + getJSON(): JSONTextSettings { if (!this.json) { throw new TypeError('json settings not set'); } @@ -283,7 +290,7 @@ export default class SettingData { case SettingSource.JSON: return { source: this.source, - json: (this.json as JSONSettings).toJSON(), + json: (this.json as JSONTextSettings).toJSONText(), }; case SettingSource.Form: return { @@ -313,8 +320,8 @@ export default class SettingData { case SettingSource.JSON: return new SettingData({ source: o.source, - json: JSONSettings.valueOf( - o.json as ReturnType), + json: JSONTextSettings.fromText( + o.json as ReturnType), }); case SettingSource.Form: return new SettingData({ diff --git a/src/shared/Settings.ts b/src/shared/Settings.ts index d338e2a..3014abc 100644 --- a/src/shared/Settings.ts +++ b/src/shared/Settings.ts @@ -1,7 +1,5 @@ -import * as operations from './operations'; import * as PropertyDefs from './property-defs'; - -export type Keymaps = {[key: string]: operations.Operation}; +import Keymaps from './settings/Keymaps'; export interface Search { default: string; @@ -21,14 +19,6 @@ export default interface Settings { blacklist: string[]; } -export const keymapsValueOf = (o: any): Keymaps => { - return Object.keys(o).reduce((keymaps: Keymaps, key: string): Keymaps => { - let op = operations.valueOf(o[key]); - keymaps[key] = op; - return keymaps; - }, {}); -}; - export const searchValueOf = (o: any): Search => { if (typeof o.default !== 'string') { throw new TypeError('string field "default" not set"'); @@ -97,7 +87,7 @@ export const valueOf = (o: any): Settings => { for (let key of Object.keys(o)) { switch (key) { case 'keymaps': - settings.keymaps = keymapsValueOf(o.keymaps); + settings.keymaps = Keymaps.fromJSON(o.keymaps); break; case 'search': settings.search = searchValueOf(o.search); @@ -115,8 +105,17 @@ export const valueOf = (o: any): Settings => { return settings; }; +export const toJSON = (settings: Settings): any => { + return { + keymaps: settings.keymaps.toJSON(), + search: settings.search, + properties: settings.properties, + blacklist: settings.blacklist, + }; +}; + export const DefaultSetting: Settings = { - keymaps: { + keymaps: Keymaps.fromJSON({ '0': { 'type': 'scroll.home' }, ':': { 'type': 'command.show' }, 'o': { 'type': 'command.show.open', 'alter': false }, @@ -179,7 +178,7 @@ export const DefaultSetting: Settings = { 'N': { 'type': 'find.prev' }, '.': { 'type': 'repeat.last' }, '': { 'type': 'addon.toggle.enabled' } - }, + }), search: { default: 'google', engines: { diff --git a/src/shared/settings/Keymaps.ts b/src/shared/settings/Keymaps.ts new file mode 100644 index 0000000..a5558b0 --- /dev/null +++ b/src/shared/settings/Keymaps.ts @@ -0,0 +1,37 @@ +import * as operations from '../operations'; + +export type KeymapsJSON = { [key: string]: operations.Operation }; + +export default class Keymaps { + constructor( + private readonly data: KeymapsJSON, + ) { + } + + static fromJSON(json: any): Keymaps { + if (typeof json !== 'object' || json === null) { + throw new TypeError('invalid keymaps type: ' + JSON.stringify(json)); + } + + let data: KeymapsJSON = {}; + for (let key of Object.keys(json)) { + data[key] = operations.valueOf(json[key]); + } + return new Keymaps(data); + } + + combine(other: Keymaps): Keymaps { + return new Keymaps({ + ...this.data, + ...other.data, + }); + } + + toJSON(): KeymapsJSON { + return this.data; + } + + entries(): [string, operations.Operation][] { + return Object.entries(this.data); + } +} diff --git a/test/content/repositories/SettingRepository.test.ts b/test/content/repositories/SettingRepository.test.ts index fea70b7..457ca4c 100644 --- a/test/content/repositories/SettingRepository.test.ts +++ b/test/content/repositories/SettingRepository.test.ts @@ -1,12 +1,13 @@ import { SettingRepositoryImpl } from '../../../src/content/repositories/SettingRepository'; import { expect } from 'chai'; +import Keymaps from '../../../src/shared/settings/Keymaps'; describe('SettingRepositoryImpl', () => { it('updates and gets current value', () => { let sut = new SettingRepositoryImpl(); let settings = { - keymaps: {}, + keymaps: Keymaps.fromJSON({}), search: { default: 'google', engines: { @@ -19,7 +20,7 @@ describe('SettingRepositoryImpl', () => { complete: 'sbh', }, blacklist: [], - } + }; sut.set(settings); @@ -27,4 +28,3 @@ describe('SettingRepositoryImpl', () => { expect(actual.properties.hintchars).to.equal('abcd1234'); }); }); - diff --git a/test/shared/SettingData.test.ts b/test/shared/SettingData.test.ts index 8736ecb..9567f76 100644 --- a/test/shared/SettingData.test.ts +++ b/test/shared/SettingData.test.ts @@ -1,8 +1,9 @@ import SettingData, { - FormKeymaps, JSONSettings, FormSettings, + FormKeymaps, JSONTextSettings, FormSettings, } from '../../src/shared/SettingData'; -import Settings, { Keymaps } from '../../src/shared/Settings'; +import Settings from '../../src/shared/Settings'; import { expect } from 'chai'; +import Keymaps from '../../src/shared/settings/Keymaps'; describe('shared/SettingData', () => { describe('FormKeymaps', () => { @@ -11,9 +12,9 @@ describe('shared/SettingData', () => { let data = { 'scroll.vertically?{"count":1}': 'j', 'scroll.home': '0', - } + }; - let keymaps = FormKeymaps.valueOf(data).toKeymaps(); + let keymaps = FormKeymaps.valueOf(data).toKeymaps().toJSON(); expect(keymaps).to.deep.equal({ 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, @@ -23,13 +24,13 @@ describe('shared/SettingData', () => { describe('#fromKeymaps to #toJSON', () => { it('create from a Keymaps and create a JSON object', () => { - let data: Keymaps = { + let keymaps: Keymaps = Keymaps.fromJSON({ 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, - } + }); - let keymaps = FormKeymaps.fromKeymaps(data).toJSON(); - expect(keymaps).to.deep.equal({ + let form = FormKeymaps.fromKeymaps(keymaps).toJSON(); + expect(form).to.deep.equal({ 'scroll.vertically?{"count":1}': 'j', 'scroll.home': '0', }); @@ -56,15 +57,20 @@ describe('shared/SettingData', () => { "blacklist": [] }`; - let settings = JSONSettings.valueOf(o).toSettings(); - expect(settings).to.deep.equal(JSON.parse(o)); + let settings = JSONTextSettings.fromText(o).toSettings(); + expect({ + keymaps: settings.keymaps.toJSON(), + search: settings.search, + properties: settings.properties, + blacklist: settings.blacklist, + }).to.deep.equal(JSON.parse(o)); }); }); describe('#fromSettings to #toJSON', () => { it('create from a Settings and create a JSON string', () => { let o = { - keymaps: {}, + keymaps: Keymaps.fromJSON({}), search: { default: "google", engines: { @@ -79,8 +85,13 @@ describe('shared/SettingData', () => { blacklist: [], }; - let json = JSONSettings.fromSettings(o).toJSON(); - expect(JSON.parse(json)).to.deep.equal(o); + let json = JSONTextSettings.fromSettings(o).toJSONText(); + expect(JSON.parse(json)).to.deep.equal({ + keymaps: o.keymaps.toJSON(), + search: o.search, + properties: o.properties, + blacklist: o.blacklist, + }); }); }); }); @@ -108,7 +119,12 @@ describe('shared/SettingData', () => { }; let settings = FormSettings.valueOf(data).toSettings(); - expect(settings).to.deep.equal({ + expect({ + keymaps: settings.keymaps.toJSON(), + search: settings.search, + properties: settings.properties, + blacklist: settings.blacklist, + }).to.deep.equal({ keymaps: { 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, @@ -132,10 +148,10 @@ describe('shared/SettingData', () => { describe('#fromSettings to #toJSON', () => { it('create from a Settings and create a JSON string', () => { let data: Settings = { - keymaps: { + keymaps: Keymaps.fromJSON({ 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, - }, + }), search: { default: "google", engines: { diff --git a/test/shared/Settings.test.ts b/test/shared/Settings.test.ts index 04b28c4..9faf9d1 100644 --- a/test/shared/Settings.test.ts +++ b/test/shared/Settings.test.ts @@ -1,31 +1,7 @@ import * as settings from '../../src/shared/Settings'; -import { expect } from 'chai'; +import {expect} from 'chai'; describe('Settings', () => { - describe('#keymapsValueOf', () => { - it('returns empty object by empty settings', () => { - let keymaps = settings.keymapsValueOf({}); - expect(keymaps).to.be.empty; - }); - - it('returns keymaps by valid settings', () => { - let keymaps = settings.keymapsValueOf({ - k: { type: "scroll.vertically", count: -1 }, - j: { type: "scroll.vertically", count: 1 }, - }); - - expect(keymaps['k']).to.deep.equal({ type: "scroll.vertically", count: -1 }); - expect(keymaps['j']).to.deep.equal({ type: "scroll.vertically", count: 1 }); - }); - - it('throws a TypeError by invalid settings', () => { - expect(() => settings.keymapsValueOf(null)).to.throw(TypeError); - expect(() => settings.keymapsValueOf({ - k: { type: "invalid.operation" }, - })).to.throw(TypeError); - }); - }); - describe('#searchValueOf', () => { it('returns search settings by valid settings', () => { let search = settings.searchValueOf({ @@ -110,16 +86,6 @@ describe('Settings', () => { complete: "sbh" }); }); - - it('throws a TypeError by invalid settings', () => { - expect(() => settings.keymapsValueOf(null)).to.throw(TypeError); - expect(() => settings.keymapsValueOf({ - smoothscroll: 'false', - })).to.throw(TypeError); - expect(() => settings.keymapsValueOf({ - unknown: 'xyz' - })).to.throw(TypeError); - }); }); describe('#blacklistValueOf', () => { @@ -161,7 +127,12 @@ describe('Settings', () => { "blacklist": [] }); - expect(x).to.deep.equal({ + expect({ + keymaps: x.keymaps.toJSON(), + search: x.search, + properties: x.properties, + blacklist: x.blacklist, + }).to.deep.equal({ keymaps: {}, search: { default: "google", @@ -180,7 +151,7 @@ describe('Settings', () => { it('sets default settings', () => { let value = settings.valueOf({}); - expect(value.keymaps).to.not.be.empty; + expect(value.keymaps.toJSON()).to.not.be.empty; expect(value.properties).to.not.be.empty; expect(value.search.default).to.be.a('string'); expect(value.search.engines).to.be.an('object'); diff --git a/test/shared/settings/Keymaps.test.ts b/test/shared/settings/Keymaps.test.ts new file mode 100644 index 0000000..7896a63 --- /dev/null +++ b/test/shared/settings/Keymaps.test.ts @@ -0,0 +1,66 @@ +import Keymaps from '../../../src/shared/settings/Keymaps'; +import { expect } from 'chai'; + +describe('Keymaps', () => { + describe('#valueOf', () => { + it('returns empty object by empty settings', () => { + let keymaps = Keymaps.fromJSON({}).toJSON(); + expect(keymaps).to.be.empty; + }); + + it('returns keymaps by valid settings', () => { + let keymaps = Keymaps.fromJSON({ + k: { type: "scroll.vertically", count: -1 }, + j: { type: "scroll.vertically", count: 1 }, + }).toJSON(); + + expect(keymaps['k']).to.deep.equal({ type: "scroll.vertically", count: -1 }); + expect(keymaps['j']).to.deep.equal({ type: "scroll.vertically", count: 1 }); + }); + + it('throws a TypeError by invalid settings', () => { + expect(() => Keymaps.fromJSON(null)).to.throw(TypeError); + expect(() => Keymaps.fromJSON({ + k: { type: "invalid.operation" }, + })).to.throw(TypeError); + }); + }); + + describe('#combine', () => { + it('returns combined keymaps', () => { + let keymaps = Keymaps.fromJSON({ + k: { type: "scroll.vertically", count: -1 }, + j: { type: "scroll.vertically", count: 1 }, + }).combine(Keymaps.fromJSON({ + n: { type: "find.next" }, + N: { type: "find.prev" }, + })); + + let entries = keymaps.entries().sort(([name1], [name2]) => name1.localeCompare(name2)); + expect(entries).deep.equals([ + ['j', { type: "scroll.vertically", count: 1 }], + ['k', { type: "scroll.vertically", count: -1 }], + ['n', { type: "find.next" }], + ['N', { type: "find.prev" }], + ]); + }); + + it('overrides current keymaps', () => { + let keymaps = Keymaps.fromJSON({ + k: { type: "scroll.vertically", count: -1 }, + j: { type: "scroll.vertically", count: 1 }, + }).combine(Keymaps.fromJSON({ + n: { type: "find.next" }, + j: { type: "find.prev" }, + })); + + let entries = keymaps.entries().sort(([name1], [name2]) => name1.localeCompare(name2)); + expect(entries).deep.equals([ + ['j', { type: "find.prev" }], + ['k', { type: "scroll.vertically", count: -1 }], + ['n', { type: "find.next" }], + ]); + }); + }); +}); + -- cgit v1.2.3 From 2116ac90a6dfdb0910d7ad2896f70a052aa635cc Mon Sep 17 00:00:00 2001 From: Shin'ya UEOKA Date: Sat, 5 Oct 2019 01:08:07 +0000 Subject: Make Search class --- src/shared/SettingData.ts | 22 +++---- src/shared/Settings.ts | 44 ++----------- src/shared/settings/Search.ts | 76 ++++++++++++++++++++++ src/shared/urls.ts | 4 +- .../content/repositories/SettingRepository.test.ts | 5 +- test/shared/SettingData.test.ts | 19 +++--- test/shared/Settings.test.ts | 66 +------------------ test/shared/settings/Search.test.ts | 68 +++++++++++++++++++ test/shared/urls.test.ts | 8 ++- 9 files changed, 182 insertions(+), 130 deletions(-) create mode 100644 src/shared/settings/Search.ts create mode 100644 test/shared/settings/Search.test.ts (limited to 'test/content/repositories') diff --git a/src/shared/SettingData.ts b/src/shared/SettingData.ts index eb83b75..6605c80 100644 --- a/src/shared/SettingData.ts +++ b/src/shared/SettingData.ts @@ -1,6 +1,7 @@ import * as operations from './operations'; import Settings, * as settings from './Settings'; import Keymaps from './settings/Keymaps'; +import Search from './settings/Search'; export class FormKeymaps { private data: {[op: string]: string}; @@ -71,15 +72,12 @@ export class FormSearch { this.engines = engines; } - toSearchSettings(): settings.Search { - return { - default: this.default, - engines: this.engines.reduce( - (o: {[key: string]: string}, [name, url]) => { - o[name] = url; - return o; - }, {}), - }; + toSearchSettings(): Search { + let engines: { [name: string]: string } = {}; + for (let entry of this.engines) { + engines[entry[0]] = entry[1]; + } + return new Search(this.default, engines); } toJSON(): { @@ -102,12 +100,12 @@ export class FormSearch { return new FormSearch(o.default, o.engines); } - static fromSearch(search: settings.Search): FormSearch { + static fromSearch(search: Search): FormSearch { let engines = Object.entries(search.engines).reduce( (o: string[][], [name, url]) => { return o.concat([[name, url]]); }, []); - return new FormSearch(search.default, engines); + return new FormSearch(search.defaultEngine, engines); } } @@ -200,7 +198,7 @@ export class FormSettings { toSettings(): Settings { return settings.valueOf({ keymaps: this.keymaps.toKeymaps().toJSON(), - search: this.search.toSearchSettings(), + search: this.search.toSearchSettings().toJSON(), properties: this.properties, blacklist: this.blacklist, }); diff --git a/src/shared/Settings.ts b/src/shared/Settings.ts index 3014abc..e2bb3f4 100644 --- a/src/shared/Settings.ts +++ b/src/shared/Settings.ts @@ -1,10 +1,6 @@ import * as PropertyDefs from './property-defs'; import Keymaps from './settings/Keymaps'; - -export interface Search { - default: string; - engines: { [key: string]: string }; -} +import Search from './settings/Search'; export interface Properties { hintchars: string; @@ -19,36 +15,6 @@ export default interface Settings { blacklist: string[]; } -export const searchValueOf = (o: any): Search => { - if (typeof o.default !== 'string') { - throw new TypeError('string field "default" not set"'); - } - for (let name of Object.keys(o.engines)) { - if ((/\s/).test(name)) { - throw new TypeError( - `While space in the search engine not allowed: "${name}"`); - } - let url = o.engines[name]; - if (typeof url !== 'string') { - throw new TypeError('"engines" not an object of string'); - } - let matches = url.match(/{}/g); - if (matches === null) { - throw new TypeError(`No {}-placeholders in URL of "${name}"`); - } else if (matches.length > 1) { - throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`); - } - - } - if (!Object.prototype.hasOwnProperty.call(o.engines, o.default)) { - throw new TypeError(`Default engine "${o.default}" not found`); - } - return { - default: o.default as string, - engines: { ...o.engines }, - }; -}; - export const propertiesValueOf = (o: any): Properties => { let defNames = new Set(PropertyDefs.defs.map(def => def.name)); let unknownName = Object.keys(o).find(name => !defNames.has(name)); @@ -90,7 +56,7 @@ export const valueOf = (o: any): Settings => { settings.keymaps = Keymaps.fromJSON(o.keymaps); break; case 'search': - settings.search = searchValueOf(o.search); + settings.search = Search.fromJSON(o.search); break; case 'properties': settings.properties = propertiesValueOf(o.properties); @@ -108,7 +74,7 @@ export const valueOf = (o: any): Settings => { export const toJSON = (settings: Settings): any => { return { keymaps: settings.keymaps.toJSON(), - search: settings.search, + search: settings.search.toJSON(), properties: settings.properties, blacklist: settings.blacklist, }; @@ -179,7 +145,7 @@ export const DefaultSetting: Settings = { '.': { 'type': 'repeat.last' }, '': { 'type': 'addon.toggle.enabled' } }), - search: { + search: Search.fromJSON({ default: 'google', engines: { 'google': 'https://google.com/search?q={}', @@ -189,7 +155,7 @@ export const DefaultSetting: Settings = { 'twitter': 'https://twitter.com/search?q={}', 'wikipedia': 'https://en.wikipedia.org/w/index.php?search={}' } - }, + }), properties: { hintchars: 'abcdefghijklmnopqrstuvwxyz', smoothscroll: false, diff --git a/src/shared/settings/Search.ts b/src/shared/settings/Search.ts new file mode 100644 index 0000000..4580236 --- /dev/null +++ b/src/shared/settings/Search.ts @@ -0,0 +1,76 @@ +type Entries = { [name: string]: string }; + +export type SearchJSON = { + default: string; + engines: { [key: string]: string }; +}; + +export default class Search { + constructor( + public defaultEngine: string, + public engines: Entries, + ) { + } + + static fromJSON(json: any): Search { + let defaultEngine = Search.getStringField(json, 'default'); + let engines = Search.getObjectField(json, 'engines'); + + for (let [name, url] of Object.entries(engines)) { + if ((/\s/).test(name)) { + throw new TypeError( + `While space in the search engine not allowed: "${name}"`); + } + if (typeof url !== 'string') { + throw new TypeError( + `Invalid type of value in filed "engines": ${JSON.stringify(json)}`); + } + let matches = url.match(/{}/g); + if (matches === null) { + throw new TypeError(`No {}-placeholders in URL of "${name}"`); + } else if (matches.length > 1) { + throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`); + } + } + + if (!Object.keys(engines).includes(defaultEngine)) { + throw new TypeError(`Default engine "${defaultEngine}" not found`); + } + + return new Search( + json.default as string, + json.engines, + ); + } + + toJSON(): SearchJSON { + return { + default: this.defaultEngine, + engines: this.engines, + }; + } + + private static getStringField(json: any, name: string): string { + if (!Object.prototype.hasOwnProperty.call(json, name)) { + throw new TypeError( + `missing field "${name}" on search: ${JSON.stringify(json)}`); + } + if (typeof json[name] !== 'string') { + throw new TypeError( + `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`); + } + return json[name]; + } + + private static getObjectField(json: any, name: string): Object { + if (!Object.prototype.hasOwnProperty.call(json, name)) { + throw new TypeError( + `missing field "${name}" on search: ${JSON.stringify(json)}`); + } + if (typeof json[name] !== 'object' || json[name] === null) { + throw new TypeError( + `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`); + } + return json[name]; + } +} diff --git a/src/shared/urls.ts b/src/shared/urls.ts index bbdb1ea..64ea4f2 100644 --- a/src/shared/urls.ts +++ b/src/shared/urls.ts @@ -1,4 +1,4 @@ -import { Search } from './Settings'; +import Search from './settings/Search'; const trimStart = (str: string): string => { // NOTE String.trimStart is available on Firefox 61 @@ -19,7 +19,7 @@ const searchUrl = (keywords: string, search: Search): string => { if (keywords.includes('.') && !keywords.includes(' ')) { return 'http://' + keywords; } - let template = search.engines[search.default]; + let template = search.engines[search.defaultEngine]; let query = keywords; let first = trimStart(keywords).split(' ')[0]; diff --git a/test/content/repositories/SettingRepository.test.ts b/test/content/repositories/SettingRepository.test.ts index 457ca4c..363fcec 100644 --- a/test/content/repositories/SettingRepository.test.ts +++ b/test/content/repositories/SettingRepository.test.ts @@ -1,6 +1,7 @@ import { SettingRepositoryImpl } from '../../../src/content/repositories/SettingRepository'; import { expect } from 'chai'; import Keymaps from '../../../src/shared/settings/Keymaps'; +import Search from '../../../src/shared/settings/Search'; describe('SettingRepositoryImpl', () => { it('updates and gets current value', () => { @@ -8,12 +9,12 @@ describe('SettingRepositoryImpl', () => { let settings = { keymaps: Keymaps.fromJSON({}), - search: { + search: Search.fromJSON({ default: 'google', engines: { google: 'https://google.com/?q={}', } - }, + }), properties: { hintchars: 'abcd1234', smoothscroll: false, diff --git a/test/shared/SettingData.test.ts b/test/shared/SettingData.test.ts index 9567f76..f8995d9 100644 --- a/test/shared/SettingData.test.ts +++ b/test/shared/SettingData.test.ts @@ -4,6 +4,7 @@ import SettingData, { import Settings from '../../src/shared/Settings'; import { expect } from 'chai'; import Keymaps from '../../src/shared/settings/Keymaps'; +import Search from '../../src/shared/settings/Search'; describe('shared/SettingData', () => { describe('FormKeymaps', () => { @@ -60,7 +61,7 @@ describe('shared/SettingData', () => { let settings = JSONTextSettings.fromText(o).toSettings(); expect({ keymaps: settings.keymaps.toJSON(), - search: settings.search, + search: settings.search.toJSON(), properties: settings.properties, blacklist: settings.blacklist, }).to.deep.equal(JSON.parse(o)); @@ -71,12 +72,12 @@ describe('shared/SettingData', () => { it('create from a Settings and create a JSON string', () => { let o = { keymaps: Keymaps.fromJSON({}), - search: { + search: Search.fromJSON({ default: "google", engines: { google: "https://google.com/search?q={}", }, - }, + }), properties: { hintchars: "abcdefghijklmnopqrstuvwxyz", smoothscroll: false, @@ -88,7 +89,7 @@ describe('shared/SettingData', () => { let json = JSONTextSettings.fromSettings(o).toJSONText(); expect(JSON.parse(json)).to.deep.equal({ keymaps: o.keymaps.toJSON(), - search: o.search, + search: o.search.toJSON(), properties: o.properties, blacklist: o.blacklist, }); @@ -121,7 +122,7 @@ describe('shared/SettingData', () => { let settings = FormSettings.valueOf(data).toSettings(); expect({ keymaps: settings.keymaps.toJSON(), - search: settings.search, + search: settings.search.toJSON(), properties: settings.properties, blacklist: settings.blacklist, }).to.deep.equal({ @@ -152,12 +153,12 @@ describe('shared/SettingData', () => { 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, }), - search: { + search: Search.fromJSON({ default: "google", engines: { "google": "https://google.com/search?q={}" } - }, + }), properties: { hintchars: "abcdefghijklmnopqrstuvwxyz", smoothscroll: false, @@ -278,7 +279,7 @@ describe('shared/SettingData', () => { }; let settings = SettingData.valueOf(data).toSettings(); - expect(settings.search.default).to.equal('google'); + expect(settings.search.defaultEngine).to.equal('google'); }); it('parse object from form source', () => { @@ -302,7 +303,7 @@ describe('shared/SettingData', () => { }; let settings = SettingData.valueOf(data).toSettings(); - expect(settings.search.default).to.equal('yahoo'); + expect(settings.search.defaultEngine).to.equal('yahoo'); }); }); }); diff --git a/test/shared/Settings.test.ts b/test/shared/Settings.test.ts index 9faf9d1..ed791a1 100644 --- a/test/shared/Settings.test.ts +++ b/test/shared/Settings.test.ts @@ -1,67 +1,7 @@ import * as settings from '../../src/shared/Settings'; -import {expect} from 'chai'; +import { expect } from 'chai'; describe('Settings', () => { - describe('#searchValueOf', () => { - it('returns search settings by valid settings', () => { - let search = settings.searchValueOf({ - default: "google", - engines: { - "google": "https://google.com/search?q={}", - "yahoo": "https://search.yahoo.com/search?p={}", - } - }); - - expect(search).to.deep.equal({ - default: "google", - engines: { - "google": "https://google.com/search?q={}", - "yahoo": "https://search.yahoo.com/search?p={}", - } - }); - }); - - it('throws a TypeError by invalid settings', () => { - expect(() => settings.searchValueOf(null)).to.throw(TypeError); - expect(() => settings.searchValueOf({})).to.throw(TypeError); - expect(() => settings.searchValueOf([])).to.throw(TypeError); - expect(() => settings.searchValueOf({ - default: 123, - engines: {} - })).to.throw(TypeError); - expect(() => settings.searchValueOf({ - default: "google", - engines: { - "google": 123456, - } - })).to.throw(TypeError); - expect(() => settings.searchValueOf({ - default: "wikipedia", - engines: { - "google": "https://google.com/search?q={}", - "yahoo": "https://search.yahoo.com/search?p={}", - } - })).to.throw(TypeError); - expect(() => settings.searchValueOf({ - default: "g o o g l e", - engines: { - "g o o g l e": "https://google.com/search?q={}", - } - })).to.throw(TypeError); - expect(() => settings.searchValueOf({ - default: "google", - engines: { - "google": "https://google.com/search", - } - })).to.throw(TypeError); - expect(() => settings.searchValueOf({ - default: "google", - engines: { - "google": "https://google.com/search?q={}&r={}", - } - })).to.throw(TypeError); - }); - }); describe('#propertiesValueOf', () => { it('returns with default properties by empty settings', () => { @@ -129,7 +69,7 @@ describe('Settings', () => { expect({ keymaps: x.keymaps.toJSON(), - search: x.search, + search: x.search.toJSON(), properties: x.properties, blacklist: x.blacklist, }).to.deep.equal({ @@ -153,7 +93,7 @@ describe('Settings', () => { let value = settings.valueOf({}); expect(value.keymaps.toJSON()).to.not.be.empty; expect(value.properties).to.not.be.empty; - expect(value.search.default).to.be.a('string'); + expect(value.search.defaultEngine).to.be.a('string'); expect(value.search.engines).to.be.an('object'); expect(value.blacklist).to.be.empty; }); diff --git a/test/shared/settings/Search.test.ts b/test/shared/settings/Search.test.ts new file mode 100644 index 0000000..7c9134d --- /dev/null +++ b/test/shared/settings/Search.test.ts @@ -0,0 +1,68 @@ +import Search from '../../../src/shared/settings/Search'; +import { expect } from 'chai'; + +describe('Search', () => { + it('returns search settings by valid settings', () => { + let search = Search.fromJSON({ + default: 'google', + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?p={}', + } + }); + + expect(search.defaultEngine).to.equal('google') + expect(search.engines).to.deep.equals({ + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?p={}', + }); + expect(search.toJSON()).to.deep.equal({ + default: 'google', + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?p={}', + } + }); + }); + + it('throws a TypeError by invalid settings', () => { + expect(() => Search.fromJSON(null)).to.throw(TypeError); + expect(() => Search.fromJSON({})).to.throw(TypeError); + expect(() => Search.fromJSON([])).to.throw(TypeError); + expect(() => Search.fromJSON({ + default: 123, + engines: {} + })).to.throw(TypeError); + expect(() => Search.fromJSON({ + default: 'google', + engines: { + 'google': 123456, + } + })).to.throw(TypeError); + expect(() => Search.fromJSON({ + default: 'wikipedia', + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?p={}', + } + })).to.throw(TypeError); + expect(() => Search.fromJSON({ + default: 'g o o g l e', + engines: { + 'g o o g l e': 'https://google.com/search?q={}', + } + })).to.throw(TypeError); + expect(() => Search.fromJSON({ + default: 'google', + engines: { + 'google': 'https://google.com/search', + } + })).to.throw(TypeError); + expect(() => Search.fromJSON({ + default: 'google', + engines: { + 'google': 'https://google.com/search?q={}&r={}', + } + })).to.throw(TypeError); + }); +}); diff --git a/test/shared/urls.test.ts b/test/shared/urls.test.ts index f2950b6..3a3eea6 100644 --- a/test/shared/urls.test.ts +++ b/test/shared/urls.test.ts @@ -1,14 +1,16 @@ -import * as parsers from 'shared/urls'; +import * as parsers from '../../src/shared/urls'; +import { expect } from 'chai'; +import Search from '../../src/shared/settings/Search'; describe("shared/commands/parsers", () => { describe('#searchUrl', () => { - const config = { + const config = Search.fromJSON({ default: 'google', engines: { google: 'https://google.com/search?q={}', yahoo: 'https://yahoo.com/search?q={}', } - }; + }); it('convertes search url', () => { expect(parsers.searchUrl('google.com', config)) -- cgit v1.2.3 From 0dec6c641fc11348f89a12680a087ccda1181f66 Mon Sep 17 00:00:00 2001 From: Shin'ya UEOKA Date: Sat, 5 Oct 2019 07:19:48 +0000 Subject: Make Settings class --- src/background/controllers/SettingController.ts | 2 +- .../infrastructures/ContentMessageListener.ts | 3 +- src/background/repositories/SettingRepository.ts | 7 +- src/background/usecases/SettingUseCase.ts | 2 +- src/content/client/SettingClient.ts | 4 +- src/content/repositories/SettingRepository.ts | 2 +- src/content/usecases/SettingUseCase.ts | 2 +- src/shared/SettingData.ts | 6 +- src/shared/Settings.ts | 127 ----------------- src/shared/settings/Settings.ts | 154 +++++++++++++++++++++ .../content/repositories/SettingRepository.test.ts | 13 +- test/content/usecases/SettingUseCaase.test.ts | 2 +- test/shared/SettingData.test.ts | 39 +++--- test/shared/Settings.test.ts | 54 -------- test/shared/settings/Settings.test.ts | 54 ++++++++ 15 files changed, 246 insertions(+), 225 deletions(-) delete mode 100644 src/shared/Settings.ts create mode 100644 src/shared/settings/Settings.ts delete mode 100644 test/shared/Settings.test.ts create mode 100644 test/shared/settings/Settings.test.ts (limited to 'test/content/repositories') diff --git a/src/background/controllers/SettingController.ts b/src/background/controllers/SettingController.ts index 34951ff..8d05852 100644 --- a/src/background/controllers/SettingController.ts +++ b/src/background/controllers/SettingController.ts @@ -1,7 +1,7 @@ import { injectable } from 'tsyringe'; import SettingUseCase from '../usecases/SettingUseCase'; import ContentMessageClient from '../infrastructures/ContentMessageClient'; -import Settings from '../../shared/Settings'; +import Settings from '../../shared/settings/Settings'; @injectable() export default class SettingController { diff --git a/src/background/infrastructures/ContentMessageListener.ts b/src/background/infrastructures/ContentMessageListener.ts index f348a74..f80d686 100644 --- a/src/background/infrastructures/ContentMessageListener.ts +++ b/src/background/infrastructures/ContentMessageListener.ts @@ -8,7 +8,6 @@ import AddonEnabledController from '../controllers/AddonEnabledController'; import LinkController from '../controllers/LinkController'; import OperationController from '../controllers/OperationController'; import MarkController from '../controllers/MarkController'; -import { toJSON } from '../../shared/Settings'; @injectable() export default class ContentMessageListener { @@ -103,7 +102,7 @@ export default class ContentMessageListener { } async onSettingsQuery(): Promise { - return toJSON(await this.settingController.getSetting()); + return (await this.settingController.getSetting()).toJSON(); } onFindGetKeyword(): Promise { diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts index a11b65f..e775a32 100644 --- a/src/background/repositories/SettingRepository.ts +++ b/src/background/repositories/SettingRepository.ts @@ -1,6 +1,6 @@ import { injectable } from 'tsyringe'; import MemoryStorage from '../infrastructures/MemoryStorage'; -import Settings, { valueOf, toJSON } from '../../shared/Settings'; +import Settings from '../../shared/settings/Settings'; import Properties from '../../shared/settings/Properties'; const CACHED_SETTING_KEY = 'setting'; @@ -15,12 +15,11 @@ export default class SettingRepository { get(): Promise { let data = this.cache.get(CACHED_SETTING_KEY); - return Promise.resolve(valueOf(data)); + return Promise.resolve(Settings.fromJSON(data)); } update(value: Settings): void { - let data = toJSON(value); - return this.cache.set(CACHED_SETTING_KEY, data); + return this.cache.set(CACHED_SETTING_KEY, value.toJSON()); } async setProperty( diff --git a/src/background/usecases/SettingUseCase.ts b/src/background/usecases/SettingUseCase.ts index d73521f..d78d440 100644 --- a/src/background/usecases/SettingUseCase.ts +++ b/src/background/usecases/SettingUseCase.ts @@ -3,7 +3,7 @@ import PersistentSettingRepository from '../repositories/PersistentSettingRepository'; import SettingRepository from '../repositories/SettingRepository'; import { DefaultSettingData } from '../../shared/SettingData'; -import Settings from '../../shared/Settings'; +import Settings from '../../shared/settings/Settings'; import NotifyPresenter from '../presenters/NotifyPresenter'; @injectable() diff --git a/src/content/client/SettingClient.ts b/src/content/client/SettingClient.ts index a7cd1ee..fc62720 100644 --- a/src/content/client/SettingClient.ts +++ b/src/content/client/SettingClient.ts @@ -1,4 +1,4 @@ -import Settings, { valueOf } from '../../shared/Settings'; +import Settings from '../../shared/settings/Settings'; import * as messages from '../../shared/messages'; export default interface SettingClient { @@ -10,6 +10,6 @@ export class SettingClientImpl { let settings = await browser.runtime.sendMessage({ type: messages.SETTINGS_QUERY, }); - return valueOf(settings); + return Settings.fromJSON(settings); } } diff --git a/src/content/repositories/SettingRepository.ts b/src/content/repositories/SettingRepository.ts index d718794..4ba26e0 100644 --- a/src/content/repositories/SettingRepository.ts +++ b/src/content/repositories/SettingRepository.ts @@ -1,4 +1,4 @@ -import Settings, { DefaultSetting } from '../../shared/Settings'; +import Settings, { DefaultSetting } from '../../shared/settings/Settings'; let current: Settings = DefaultSetting; diff --git a/src/content/usecases/SettingUseCase.ts b/src/content/usecases/SettingUseCase.ts index d5f66c6..4608039 100644 --- a/src/content/usecases/SettingUseCase.ts +++ b/src/content/usecases/SettingUseCase.ts @@ -1,7 +1,7 @@ import { injectable, inject } from 'tsyringe'; import SettingRepository from '../repositories/SettingRepository'; import SettingClient from '../client/SettingClient'; -import Settings from '../../shared/Settings'; +import Settings from '../../shared/settings/Settings'; @injectable() export default class SettingUseCase { diff --git a/src/shared/SettingData.ts b/src/shared/SettingData.ts index 8ef8385..2dedfef 100644 --- a/src/shared/SettingData.ts +++ b/src/shared/SettingData.ts @@ -1,5 +1,5 @@ import * as operations from './operations'; -import Settings, * as settings from './Settings'; +import Settings from './settings/Settings'; import Keymaps from './settings/Keymaps'; import Search from './settings/Search'; import Properties from './settings/Properties'; @@ -118,7 +118,7 @@ export class JSONTextSettings { } toSettings(): Settings { - return settings.valueOf(JSON.parse(this.json)); + return Settings.fromJSON(JSON.parse(this.json)); } toJSONText(): string { @@ -198,7 +198,7 @@ export class FormSettings { } toSettings(): Settings { - return settings.valueOf({ + return Settings.fromJSON({ keymaps: this.keymaps.toKeymaps().toJSON(), search: this.search.toSearchSettings().toJSON(), properties: this.properties.toJSON(), diff --git a/src/shared/Settings.ts b/src/shared/Settings.ts deleted file mode 100644 index 2767820..0000000 --- a/src/shared/Settings.ts +++ /dev/null @@ -1,127 +0,0 @@ -import Keymaps from './settings/Keymaps'; -import Search from './settings/Search'; -import Properties from './settings/Properties'; -import Blacklist from './settings/Blacklist'; - -export default interface Settings { - keymaps: Keymaps; - search: Search; - properties: Properties; - blacklist: Blacklist; -} - -export const valueOf = (o: any): Settings => { - let settings = { ...DefaultSetting }; - for (let key of Object.keys(o)) { - switch (key) { - case 'keymaps': - settings.keymaps = Keymaps.fromJSON(o.keymaps); - break; - case 'search': - settings.search = Search.fromJSON(o.search); - break; - case 'properties': - settings.properties = Properties.fromJSON(o.properties); - break; - case 'blacklist': - settings.blacklist = Blacklist.fromJSON(o.blacklist); - break; - default: - throw new TypeError('unknown setting: ' + key); - } - } - return settings; -}; - -export const toJSON = (settings: Settings): any => { - return { - keymaps: settings.keymaps.toJSON(), - search: settings.search.toJSON(), - properties: settings.properties.toJSON(), - blacklist: settings.blacklist.toJSON(), - }; -}; - -export const DefaultSetting: Settings = { - keymaps: Keymaps.fromJSON({ - '0': { 'type': 'scroll.home' }, - ':': { 'type': 'command.show' }, - 'o': { 'type': 'command.show.open', 'alter': false }, - 'O': { 'type': 'command.show.open', 'alter': true }, - 't': { 'type': 'command.show.tabopen', 'alter': false }, - 'T': { 'type': 'command.show.tabopen', 'alter': true }, - 'w': { 'type': 'command.show.winopen', 'alter': false }, - 'W': { 'type': 'command.show.winopen', 'alter': true }, - 'b': { 'type': 'command.show.buffer' }, - 'a': { 'type': 'command.show.addbookmark', 'alter': true }, - 'k': { 'type': 'scroll.vertically', 'count': -1 }, - 'j': { 'type': 'scroll.vertically', 'count': 1 }, - 'h': { 'type': 'scroll.horizonally', 'count': -1 }, - 'l': { 'type': 'scroll.horizonally', 'count': 1 }, - '': { 'type': 'scroll.pages', 'count': -0.5 }, - '': { 'type': 'scroll.pages', 'count': 0.5 }, - '': { 'type': 'scroll.pages', 'count': -1 }, - '': { 'type': 'scroll.pages', 'count': 1 }, - 'gg': { 'type': 'scroll.top' }, - 'G': { 'type': 'scroll.bottom' }, - '$': { 'type': 'scroll.end' }, - 'd': { 'type': 'tabs.close' }, - 'D': { 'type': 'tabs.close', 'select': 'left' }, - 'x$': { 'type': 'tabs.close.right' }, - '!d': { 'type': 'tabs.close.force' }, - 'u': { 'type': 'tabs.reopen' }, - 'K': { 'type': 'tabs.prev' }, - 'J': { 'type': 'tabs.next' }, - 'gT': { 'type': 'tabs.prev' }, - 'gt': { 'type': 'tabs.next' }, - 'g0': { 'type': 'tabs.first' }, - 'g$': { 'type': 'tabs.last' }, - '': { 'type': 'tabs.prevsel' }, - 'r': { 'type': 'tabs.reload', 'cache': false }, - 'R': { 'type': 'tabs.reload', 'cache': true }, - 'zp': { 'type': 'tabs.pin.toggle' }, - 'zd': { 'type': 'tabs.duplicate' }, - 'zi': { 'type': 'zoom.in' }, - 'zo': { 'type': 'zoom.out' }, - 'zz': { 'type': 'zoom.neutral' }, - 'f': { 'type': 'follow.start', 'newTab': false, 'background': false }, - 'F': { 'type': 'follow.start', 'newTab': true, 'background': false }, - 'm': { 'type': 'mark.set.prefix' }, - '\'': { 'type': 'mark.jump.prefix' }, - 'H': { 'type': 'navigate.history.prev' }, - 'L': { 'type': 'navigate.history.next' }, - '[[': { 'type': 'navigate.link.prev' }, - ']]': { 'type': 'navigate.link.next' }, - 'gu': { 'type': 'navigate.parent' }, - 'gU': { 'type': 'navigate.root' }, - 'gi': { 'type': 'focus.input' }, - 'gf': { 'type': 'page.source' }, - 'gh': { 'type': 'page.home', 'newTab': false }, - 'gH': { 'type': 'page.home', 'newTab': true }, - 'y': { 'type': 'urls.yank' }, - 'p': { 'type': 'urls.paste', 'newTab': false }, - 'P': { 'type': 'urls.paste', 'newTab': true }, - '/': { 'type': 'find.start' }, - 'n': { 'type': 'find.next' }, - 'N': { 'type': 'find.prev' }, - '.': { 'type': 'repeat.last' }, - '': { 'type': 'addon.toggle.enabled' } - }), - search: Search.fromJSON({ - default: 'google', - engines: { - 'google': 'https://google.com/search?q={}', - 'yahoo': 'https://search.yahoo.com/search?p={}', - 'bing': 'https://www.bing.com/search?q={}', - 'duckduckgo': 'https://duckduckgo.com/?q={}', - 'twitter': 'https://twitter.com/search?q={}', - 'wikipedia': 'https://en.wikipedia.org/w/index.php?search={}' - } - }), - properties: Properties.fromJSON({ - hintchars: 'abcdefghijklmnopqrstuvwxyz', - smoothscroll: false, - complete: 'sbh' - }), - blacklist: Blacklist.fromJSON([]), -}; diff --git a/src/shared/settings/Settings.ts b/src/shared/settings/Settings.ts new file mode 100644 index 0000000..116c7d7 --- /dev/null +++ b/src/shared/settings/Settings.ts @@ -0,0 +1,154 @@ +import Keymaps, { KeymapsJSON } from './Keymaps'; +import Search, { SearchJSON } from './Search'; +import Properties, { PropertiesJSON } from './Properties'; +import Blacklist, { BlacklistJSON } from './Blacklist'; + +export type SettingsJSON = { + keymaps: KeymapsJSON, + search: SearchJSON, + properties: PropertiesJSON, + blacklist: BlacklistJSON, +}; + +export default class Settings { + public keymaps: Keymaps; + + public search: Search; + + public properties: Properties; + + public blacklist: Blacklist; + + constructor({ + keymaps, + search, + properties, + blacklist, + }: { + keymaps: Keymaps; + search: Search; + properties: Properties; + blacklist: Blacklist; + }) { + this.keymaps = keymaps; + this.search = search; + this.properties = properties; + this.blacklist = blacklist; + } + + static fromJSON(json: any): Settings { + let settings = { ...DefaultSetting }; + for (let key of Object.keys(json)) { + switch (key) { + case 'keymaps': + settings.keymaps = Keymaps.fromJSON(json.keymaps); + break; + case 'search': + settings.search = Search.fromJSON(json.search); + break; + case 'properties': + settings.properties = Properties.fromJSON(json.properties); + break; + case 'blacklist': + settings.blacklist = Blacklist.fromJSON(json.blacklist); + break; + default: + throw new TypeError('unknown setting: ' + key); + } + } + return new Settings(settings); + } + + toJSON(): SettingsJSON { + return { + keymaps: this.keymaps.toJSON(), + search: this.search.toJSON(), + properties: this.properties.toJSON(), + blacklist: this.blacklist.toJSON(), + }; + } +} + +export const DefaultSetting: Settings = Settings.fromJSON({ + keymaps: { + '0': { 'type': 'scroll.home' }, + ':': { 'type': 'command.show' }, + 'o': { 'type': 'command.show.open', 'alter': false }, + 'O': { 'type': 'command.show.open', 'alter': true }, + 't': { 'type': 'command.show.tabopen', 'alter': false }, + 'T': { 'type': 'command.show.tabopen', 'alter': true }, + 'w': { 'type': 'command.show.winopen', 'alter': false }, + 'W': { 'type': 'command.show.winopen', 'alter': true }, + 'b': { 'type': 'command.show.buffer' }, + 'a': { 'type': 'command.show.addbookmark', 'alter': true }, + 'k': { 'type': 'scroll.vertically', 'count': -1 }, + 'j': { 'type': 'scroll.vertically', 'count': 1 }, + 'h': { 'type': 'scroll.horizonally', 'count': -1 }, + 'l': { 'type': 'scroll.horizonally', 'count': 1 }, + '': { 'type': 'scroll.pages', 'count': -0.5 }, + '': { 'type': 'scroll.pages', 'count': 0.5 }, + '': { 'type': 'scroll.pages', 'count': -1 }, + '': { 'type': 'scroll.pages', 'count': 1 }, + 'gg': { 'type': 'scroll.top' }, + 'G': { 'type': 'scroll.bottom' }, + '$': { 'type': 'scroll.end' }, + 'd': { 'type': 'tabs.close' }, + 'D': { 'type': 'tabs.close', 'select': 'left' }, + 'x$': { 'type': 'tabs.close.right' }, + '!d': { 'type': 'tabs.close.force' }, + 'u': { 'type': 'tabs.reopen' }, + 'K': { 'type': 'tabs.prev' }, + 'J': { 'type': 'tabs.next' }, + 'gT': { 'type': 'tabs.prev' }, + 'gt': { 'type': 'tabs.next' }, + 'g0': { 'type': 'tabs.first' }, + 'g$': { 'type': 'tabs.last' }, + '': { 'type': 'tabs.prevsel' }, + 'r': { 'type': 'tabs.reload', 'cache': false }, + 'R': { 'type': 'tabs.reload', 'cache': true }, + 'zp': { 'type': 'tabs.pin.toggle' }, + 'zd': { 'type': 'tabs.duplicate' }, + 'zi': { 'type': 'zoom.in' }, + 'zo': { 'type': 'zoom.out' }, + 'zz': { 'type': 'zoom.neutral' }, + 'f': { 'type': 'follow.start', 'newTab': false, 'background': false }, + 'F': { 'type': 'follow.start', 'newTab': true, 'background': false }, + 'm': { 'type': 'mark.set.prefix' }, + '\'': { 'type': 'mark.jump.prefix' }, + 'H': { 'type': 'navigate.history.prev' }, + 'L': { 'type': 'navigate.history.next' }, + '[[': { 'type': 'navigate.link.prev' }, + ']]': { 'type': 'navigate.link.next' }, + 'gu': { 'type': 'navigate.parent' }, + 'gU': { 'type': 'navigate.root' }, + 'gi': { 'type': 'focus.input' }, + 'gf': { 'type': 'page.source' }, + 'gh': { 'type': 'page.home', 'newTab': false }, + 'gH': { 'type': 'page.home', 'newTab': true }, + 'y': { 'type': 'urls.yank' }, + 'p': { 'type': 'urls.paste', 'newTab': false }, + 'P': { 'type': 'urls.paste', 'newTab': true }, + '/': { 'type': 'find.start' }, + 'n': { 'type': 'find.next' }, + 'N': { 'type': 'find.prev' }, + '.': { 'type': 'repeat.last' }, + '': { 'type': 'addon.toggle.enabled' } + }, + search: { + default: 'google', + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?p={}', + 'bing': 'https://www.bing.com/search?q={}', + 'duckduckgo': 'https://duckduckgo.com/?q={}', + 'twitter': 'https://twitter.com/search?q={}', + 'wikipedia': 'https://en.wikipedia.org/w/index.php?search={}' + } + }, + properties: { + hintchars: 'abcdefghijklmnopqrstuvwxyz', + smoothscroll: false, + complete: 'sbh' + }, + blacklist: [], +}); diff --git a/test/content/repositories/SettingRepository.test.ts b/test/content/repositories/SettingRepository.test.ts index 363fcec..db4c528 100644 --- a/test/content/repositories/SettingRepository.test.ts +++ b/test/content/repositories/SettingRepository.test.ts @@ -1,27 +1,26 @@ import { SettingRepositoryImpl } from '../../../src/content/repositories/SettingRepository'; import { expect } from 'chai'; -import Keymaps from '../../../src/shared/settings/Keymaps'; -import Search from '../../../src/shared/settings/Search'; +import Settings from '../../../src/shared/settings/Settings'; describe('SettingRepositoryImpl', () => { it('updates and gets current value', () => { let sut = new SettingRepositoryImpl(); - let settings = { - keymaps: Keymaps.fromJSON({}), - search: Search.fromJSON({ + let settings = Settings.fromJSON({ + keymaps: {}, + search:{ default: 'google', engines: { google: 'https://google.com/?q={}', } - }), + }, properties: { hintchars: 'abcd1234', smoothscroll: false, complete: 'sbh', }, blacklist: [], - }; + }); sut.set(settings); diff --git a/test/content/usecases/SettingUseCaase.test.ts b/test/content/usecases/SettingUseCaase.test.ts index e9633f4..136c5af 100644 --- a/test/content/usecases/SettingUseCaase.test.ts +++ b/test/content/usecases/SettingUseCaase.test.ts @@ -1,7 +1,7 @@ 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 Settings, { DefaultSetting } from '../../../src/shared/settings/Settings'; import { expect } from 'chai'; class MockSettingRepository implements SettingRepository { diff --git a/test/shared/SettingData.test.ts b/test/shared/SettingData.test.ts index 4391e73..138d768 100644 --- a/test/shared/SettingData.test.ts +++ b/test/shared/SettingData.test.ts @@ -1,12 +1,9 @@ import SettingData, { FormKeymaps, JSONTextSettings, FormSettings, } from '../../src/shared/SettingData'; -import Settings from '../../src/shared/Settings'; +import Settings from '../../src/shared/settings/Settings'; import { expect } from 'chai'; import Keymaps from '../../src/shared/settings/Keymaps'; -import Search from '../../src/shared/settings/Search'; -import Properties from '../../src/shared/settings/Properties'; -import Blacklist from '../../src/shared/settings/Blacklist' describe('shared/SettingData', () => { describe('FormKeymaps', () => { @@ -72,21 +69,21 @@ describe('shared/SettingData', () => { describe('#fromSettings to #toJSON', () => { it('create from a Settings and create a JSON string', () => { - let o = { - keymaps: Keymaps.fromJSON({}), - search: Search.fromJSON({ + let o = Settings.fromJSON({ + keymaps: {}, + search: { default: "google", engines: { google: "https://google.com/search?q={}", }, - }), - properties: Properties.fromJSON({ + }, + properties: { hintchars: "abcdefghijklmnopqrstuvwxyz", smoothscroll: false, complete: "sbh" - }), - blacklist: Blacklist.fromJSON([]), - }; + }, + blacklist: [], + }); let json = JSONTextSettings.fromSettings(o).toJSONText(); expect(JSON.parse(json)).to.deep.equal({ @@ -150,24 +147,24 @@ describe('shared/SettingData', () => { describe('#fromSettings to #toJSON', () => { it('create from a Settings and create a JSON string', () => { - let data: Settings = { - keymaps: Keymaps.fromJSON({ + let data: Settings = Settings.fromJSON({ + keymaps: { 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, - }), - search: Search.fromJSON({ + }, + search: { default: "google", engines: { "google": "https://google.com/search?q={}" } - }), - properties: Properties.fromJSON({ + }, + properties: { hintchars: "abcdefghijklmnopqrstuvwxyz", smoothscroll: false, complete: "sbh" - }), - blacklist: Blacklist.fromJSON([]), - }; + }, + blacklist: [], + }); let json = FormSettings.fromSettings(data).toJSON(); expect(json).to.deep.equal({ diff --git a/test/shared/Settings.test.ts b/test/shared/Settings.test.ts deleted file mode 100644 index 9688798..0000000 --- a/test/shared/Settings.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as settings from '../../src/shared/Settings'; -import {expect} from 'chai'; - -describe('Settings', () => { - describe('#valueOf', () => { - it('returns settings by valid settings', () => { - let x = settings.valueOf({ - keymaps: {}, - "search": { - "default": "google", - "engines": { - "google": "https://google.com/search?q={}", - } - }, - "properties": {}, - "blacklist": [] - }); - - expect({ - keymaps: x.keymaps.toJSON(), - search: x.search.toJSON(), - properties: x.properties.toJSON(), - blacklist: x.blacklist.toJSON(), - }).to.deep.equal({ - keymaps: {}, - search: { - default: "google", - engines: { - google: "https://google.com/search?q={}", - } - }, - properties: { - hintchars: "abcdefghijklmnopqrstuvwxyz", - smoothscroll: false, - complete: "sbh" - }, - blacklist: [] - }); - }); - - it('sets default settings', () => { - let value = settings.valueOf({}); - expect(value.keymaps.toJSON()).to.not.be.empty; - expect(value.properties.toJSON()).to.not.be.empty; - expect(value.search.defaultEngine).to.be.a('string'); - expect(value.search.engines).to.be.an('object'); - expect(value.blacklist.toJSON()).to.be.empty; - }); - - it('throws a TypeError with an unknown field', () => { - expect(() => settings.valueOf({ name: 'alice' })).to.throw(TypeError) - }); - }); -}); diff --git a/test/shared/settings/Settings.test.ts b/test/shared/settings/Settings.test.ts new file mode 100644 index 0000000..ab6af04 --- /dev/null +++ b/test/shared/settings/Settings.test.ts @@ -0,0 +1,54 @@ +import Settings from '../../../src/shared/settings/Settings'; +import { expect } from 'chai'; + +describe('Settings', () => { + describe('#valueOf', () => { + it('returns settings by valid settings', () => { + let x = Settings.fromJSON({ + keymaps: {}, + "search": { + "default": "google", + "engines": { + "google": "https://google.com/search?q={}", + } + }, + "properties": {}, + "blacklist": [] + }); + + expect({ + keymaps: x.keymaps.toJSON(), + search: x.search.toJSON(), + properties: x.properties.toJSON(), + blacklist: x.blacklist.toJSON(), + }).to.deep.equal({ + keymaps: {}, + search: { + default: "google", + engines: { + google: "https://google.com/search?q={}", + } + }, + properties: { + hintchars: "abcdefghijklmnopqrstuvwxyz", + smoothscroll: false, + complete: "sbh" + }, + blacklist: [] + }); + }); + + it('sets default settings', () => { + let value = Settings.fromJSON({}); + expect(value.keymaps.toJSON()).to.not.be.empty; + expect(value.properties.toJSON()).to.not.be.empty; + expect(value.search.defaultEngine).to.be.a('string'); + expect(value.search.engines).to.be.an('object'); + expect(value.blacklist.toJSON()).to.be.empty; + }); + + it('throws a TypeError with an unknown field', () => { + expect(() => Settings.fromJSON({ name: 'alice' })).to.throw(TypeError) + }); + }); +}); -- cgit v1.2.3 From da3ce77aa0a50568ee4ddb423d07bc50422bd79c Mon Sep 17 00:00:00 2001 From: Shin'ya UEOKA Date: Sun, 6 Oct 2019 03:21:38 +0000 Subject: Move Key to settings --- src/content/InputDriver.ts | 36 +++++- src/content/client/FollowMasterClient.ts | 2 +- src/content/controllers/FollowKeyController.ts | 2 +- src/content/controllers/KeymapController.ts | 2 +- src/content/controllers/MarkKeyController.ts | 2 +- src/content/domains/Key.ts | 93 -------------- src/content/domains/KeySequence.ts | 54 -------- src/content/repositories/KeymapRepository.ts | 4 +- src/content/usecases/FollowSlaveUseCase.ts | 2 +- src/content/usecases/KeymapUseCase.ts | 11 +- src/shared/settings/Key.ts | 61 +++++++++ src/shared/settings/KeySequence.ts | 54 ++++++++ test/content/InputDriver.test.ts | 51 +++++++- test/content/domains/Key.test.ts | 139 --------------------- test/content/domains/KeySequence.test.ts | 72 ----------- test/content/repositories/KeymapRepository.test.ts | 2 +- test/shared/settings/Key.test.ts | 92 ++++++++++++++ test/shared/settings/KeySequence.test.ts | 72 +++++++++++ 18 files changed, 375 insertions(+), 376 deletions(-) delete mode 100644 src/content/domains/Key.ts delete mode 100644 src/content/domains/KeySequence.ts create mode 100644 src/shared/settings/Key.ts create mode 100644 src/shared/settings/KeySequence.ts delete mode 100644 test/content/domains/Key.test.ts delete mode 100644 test/content/domains/KeySequence.test.ts create mode 100644 test/shared/settings/Key.test.ts create mode 100644 test/shared/settings/KeySequence.test.ts (limited to 'test/content/repositories') diff --git a/src/content/InputDriver.ts b/src/content/InputDriver.ts index e77d857..bc184d2 100644 --- a/src/content/InputDriver.ts +++ b/src/content/InputDriver.ts @@ -1,5 +1,5 @@ import * as dom from '../shared/utils/dom'; -import Key from './domains/Key'; +import Key from '../shared/settings/Key'; const cancelKey = (e: KeyboardEvent): boolean => { if (e.key === 'Escape') { @@ -11,6 +11,38 @@ const cancelKey = (e: KeyboardEvent): boolean => { return false; }; +const modifiedKeyName = (name: string): string => { + if (name === ' ') { + return 'Space'; + } + if (name.length === 1) { + return name; + } else if (name === 'Escape') { + return 'Esc'; + } + return name; +}; + +// visible for testing +export const keyFromKeyboardEvent = (e: KeyboardEvent): Key => { + let key = modifiedKeyName(e.key); + let shift = e.shiftKey; + if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) { + // make shift false for symbols to enable key bindings by symbold keys. + // But this limits key bindings by symbol keys with Shift + // (such as Shift+$>. + shift = false; + } + + return new Key({ + key: modifiedKeyName(e.key), + shift: shift, + ctrl: e.ctrlKey, + alt: e.altKey, + meta: e.metaKey, + }); +}; + export default class InputDriver { private pressed: {[key: string]: string} = {}; @@ -66,7 +98,7 @@ export default class InputDriver { return; } - let key = Key.fromKeyboardEvent(e); + let key = keyFromKeyboardEvent(e); for (let listener of this.onKeyListeners) { let stop = listener(key); if (stop) { diff --git a/src/content/client/FollowMasterClient.ts b/src/content/client/FollowMasterClient.ts index f79c8b0..6681e8a 100644 --- a/src/content/client/FollowMasterClient.ts +++ b/src/content/client/FollowMasterClient.ts @@ -1,5 +1,5 @@ import * as messages from '../../shared/messages'; -import Key from '../domains/Key'; +import Key from '../../shared/settings/Key'; export default interface FollowMasterClient { startFollow(newTab: boolean, background: boolean): void; diff --git a/src/content/controllers/FollowKeyController.ts b/src/content/controllers/FollowKeyController.ts index 59d2271..0fd94ff 100644 --- a/src/content/controllers/FollowKeyController.ts +++ b/src/content/controllers/FollowKeyController.ts @@ -1,6 +1,6 @@ import { injectable } from 'tsyringe'; import FollowSlaveUseCase from '../usecases/FollowSlaveUseCase'; -import Key from '../domains/Key'; +import Key from '../../shared/settings/Key'; @injectable() export default class FollowKeyController { diff --git a/src/content/controllers/KeymapController.ts b/src/content/controllers/KeymapController.ts index fcfaff1..6157a71 100644 --- a/src/content/controllers/KeymapController.ts +++ b/src/content/controllers/KeymapController.ts @@ -9,7 +9,7 @@ import ClipboardUseCase from '../usecases/ClipboardUseCase'; import OperationClient from '../client/OperationClient'; import MarkKeyyUseCase from '../usecases/MarkKeyUseCase'; import FollowMasterClient from '../client/FollowMasterClient'; -import Key from '../domains/Key'; +import Key from '../../shared/settings/Key'; @injectable() export default class KeymapController { diff --git a/src/content/controllers/MarkKeyController.ts b/src/content/controllers/MarkKeyController.ts index 886e5ff..e7653ee 100644 --- a/src/content/controllers/MarkKeyController.ts +++ b/src/content/controllers/MarkKeyController.ts @@ -1,7 +1,7 @@ import { injectable } from 'tsyringe'; import MarkUseCase from '../usecases/MarkUseCase'; import MarkKeyyUseCase from '../usecases/MarkKeyUseCase'; -import Key from '../domains/Key'; +import Key from '../../shared/settings/Key'; @injectable() export default class MarkKeyController { diff --git a/src/content/domains/Key.ts b/src/content/domains/Key.ts deleted file mode 100644 index 669edfc..0000000 --- a/src/content/domains/Key.ts +++ /dev/null @@ -1,93 +0,0 @@ -const modifiedKeyName = (name: string): string => { - if (name === ' ') { - return 'Space'; - } - if (name.length === 1) { - return name; - } else if (name === 'Escape') { - return 'Esc'; - } - return name; -}; - -export default class Key { - public readonly key: string; - - public readonly shift: boolean; - - public readonly ctrl: boolean; - - public readonly alt: boolean; - - public readonly meta: boolean; - - constructor({ key, shift, ctrl, alt, meta }: { - key: string; - shift: boolean; - ctrl: boolean; - alt: boolean; - meta: boolean; - }) { - this.key = key; - this.shift = shift; - this.ctrl = ctrl; - this.alt = alt; - this.meta = meta; - } - - static fromMapKey(str: string): Key { - if (str.startsWith('<') && str.endsWith('>')) { - let inner = str.slice(1, -1); - let shift = inner.includes('S-'); - let base = inner.slice(inner.lastIndexOf('-') + 1); - if (shift && base.length === 1) { - base = base.toUpperCase(); - } else if (!shift && base.length === 1) { - base = base.toLowerCase(); - } - return new Key({ - key: base, - shift: shift, - ctrl: inner.includes('C-'), - alt: inner.includes('A-'), - meta: inner.includes('M-'), - }); - } - - return new Key({ - key: str, - shift: str.toLowerCase() !== str, - ctrl: false, - alt: false, - meta: false, - }); - } - - static fromKeyboardEvent(e: KeyboardEvent): Key { - let key = modifiedKeyName(e.key); - let shift = e.shiftKey; - if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) { - // make shift false for symbols to enable key bindings by symbold keys. - // But this limits key bindings by symbol keys with Shift - // (such as Shift+$>. - shift = false; - } - - return new Key({ - key: modifiedKeyName(e.key), - shift: shift, - ctrl: e.ctrlKey, - alt: e.altKey, - meta: e.metaKey, - }); - } - - equals(key: Key) { - return this.key === key.key && - this.ctrl === key.ctrl && - this.meta === key.meta && - this.alt === key.alt && - this.shift === key.shift; - } -} - diff --git a/src/content/domains/KeySequence.ts b/src/content/domains/KeySequence.ts deleted file mode 100644 index abae61a..0000000 --- a/src/content/domains/KeySequence.ts +++ /dev/null @@ -1,54 +0,0 @@ -import Key from './Key'; - -export default class KeySequence { - constructor( - public readonly keys: Key[], - ) { - } - - push(key: Key): number { - return this.keys.push(key); - } - - length(): number { - return this.keys.length; - } - - startsWith(o: KeySequence): boolean { - if (this.keys.length < o.keys.length) { - return false; - } - for (let i = 0; i < o.keys.length; ++i) { - if (!this.keys[i].equals(o.keys[i])) { - return false; - } - } - return true; - } - - static fromMapKeys(keys: string): KeySequence { - const fromMapKeysRecursive = ( - remaining: string, mappedKeys: Key[], - ): Key[] => { - if (remaining.length === 0) { - return mappedKeys; - } - - let nextPos = 1; - if (remaining.startsWith('<')) { - let ltPos = remaining.indexOf('>'); - if (ltPos > 0) { - nextPos = ltPos + 1; - } - } - - return fromMapKeysRecursive( - remaining.slice(nextPos), - mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) - ); - }; - - let data = fromMapKeysRecursive(keys, []); - return new KeySequence(data); - } -} diff --git a/src/content/repositories/KeymapRepository.ts b/src/content/repositories/KeymapRepository.ts index 4463678..3391229 100644 --- a/src/content/repositories/KeymapRepository.ts +++ b/src/content/repositories/KeymapRepository.ts @@ -1,5 +1,5 @@ -import Key from '../domains/Key'; -import KeySequence from '../domains/KeySequence'; +import Key from '../../shared/settings/Key'; +import KeySequence from '../../shared/settings/KeySequence'; export default interface KeymapRepository { enqueueKey(key: Key): KeySequence; diff --git a/src/content/usecases/FollowSlaveUseCase.ts b/src/content/usecases/FollowSlaveUseCase.ts index 2bd16ee..d471adb 100644 --- a/src/content/usecases/FollowSlaveUseCase.ts +++ b/src/content/usecases/FollowSlaveUseCase.ts @@ -4,7 +4,7 @@ import FollowPresenter from '../presenters/FollowPresenter'; import TabsClient from '../client/TabsClient'; import FollowMasterClient from '../client/FollowMasterClient'; import { LinkHint, InputHint } from '../presenters/Hint'; -import Key from '../domains/Key'; +import Key from '../../shared/settings/Key'; interface Size { width: number; diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts index 62cd04c..495f6d0 100644 --- a/src/content/usecases/KeymapUseCase.ts +++ b/src/content/usecases/KeymapUseCase.ts @@ -3,9 +3,9 @@ import KeymapRepository from '../repositories/KeymapRepository'; import SettingRepository from '../repositories/SettingRepository'; import AddonEnabledRepository from '../repositories/AddonEnabledRepository'; import * as operations from '../../shared/operations'; -import Key from '../domains/Key'; -import KeySequence from '../domains/KeySequence'; import Keymaps from '../../shared/settings/Keymaps'; +import Key from '../../shared/settings/Key'; +import KeySequence from '../../shared/settings/KeySequence'; type KeymapEntityMap = Map; @@ -66,10 +66,9 @@ export default class KeymapUseCase { private keymapEntityMap(): KeymapEntityMap { let keymaps = this.settingRepository.get().keymaps.combine(reservedKeymaps); - let entries = keymaps.entries().map(entry => [ - KeySequence.fromMapKeys(entry[0]), - entry[1], - ]) as [KeySequence, operations.Operation][]; + let entries = keymaps.entries().map( + ([keys, op]) => [KeySequence.fromMapKeys(keys), op] + ) as [KeySequence, operations.Operation][]; return new Map(entries); } } diff --git a/src/shared/settings/Key.ts b/src/shared/settings/Key.ts new file mode 100644 index 0000000..b11eeb2 --- /dev/null +++ b/src/shared/settings/Key.ts @@ -0,0 +1,61 @@ +export default class Key { + public readonly key: string; + + public readonly shift: boolean; + + public readonly ctrl: boolean; + + public readonly alt: boolean; + + public readonly meta: boolean; + + constructor({ key, shift, ctrl, alt, meta }: { + key: string; + shift: boolean; + ctrl: boolean; + alt: boolean; + meta: boolean; + }) { + this.key = key; + this.shift = shift; + this.ctrl = ctrl; + this.alt = alt; + this.meta = meta; + } + + static fromMapKey(str: string): Key { + if (str.startsWith('<') && str.endsWith('>')) { + let inner = str.slice(1, -1); + let shift = inner.includes('S-'); + let base = inner.slice(inner.lastIndexOf('-') + 1); + if (shift && base.length === 1) { + base = base.toUpperCase(); + } else if (!shift && base.length === 1) { + base = base.toLowerCase(); + } + return new Key({ + key: base, + shift: shift, + ctrl: inner.includes('C-'), + alt: inner.includes('A-'), + meta: inner.includes('M-'), + }); + } + + return new Key({ + key: str, + shift: str.toLowerCase() !== str, + ctrl: false, + alt: false, + meta: false, + }); + } + + equals(key: Key) { + return this.key === key.key && + this.ctrl === key.ctrl && + this.meta === key.meta && + this.alt === key.alt && + this.shift === key.shift; + } +} diff --git a/src/shared/settings/KeySequence.ts b/src/shared/settings/KeySequence.ts new file mode 100644 index 0000000..4955583 --- /dev/null +++ b/src/shared/settings/KeySequence.ts @@ -0,0 +1,54 @@ +import Key from '../../shared/settings/Key'; + +export default class KeySequence { + constructor( + public readonly keys: Key[], + ) { + } + + push(key: Key): number { + return this.keys.push(key); + } + + length(): number { + return this.keys.length; + } + + startsWith(o: KeySequence): boolean { + if (this.keys.length < o.keys.length) { + return false; + } + for (let i = 0; i < o.keys.length; ++i) { + if (!this.keys[i].equals(o.keys[i])) { + return false; + } + } + return true; + } + + static fromMapKeys(keys: string): KeySequence { + const fromMapKeysRecursive = ( + remaining: string, mappedKeys: Key[], + ): Key[] => { + if (remaining.length === 0) { + return mappedKeys; + } + + let nextPos = 1; + if (remaining.startsWith('<')) { + let ltPos = remaining.indexOf('>'); + if (ltPos > 0) { + nextPos = ltPos + 1; + } + } + + return fromMapKeysRecursive( + remaining.slice(nextPos), + mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) + ); + }; + + let data = fromMapKeysRecursive(keys, []); + return new KeySequence(data); + } +} diff --git a/test/content/InputDriver.test.ts b/test/content/InputDriver.test.ts index b39312c..441d107 100644 --- a/test/content/InputDriver.test.ts +++ b/test/content/InputDriver.test.ts @@ -1,6 +1,6 @@ -import InputDriver from '../../src/content/InputDriver'; +import InputDriver, {keyFromKeyboardEvent} from '../../src/content/InputDriver'; import { expect } from 'chai'; -import Key from '../../src/content/domains/Key'; +import Key from '../../src/shared/settings/Key'; describe('InputDriver', () => { let target: HTMLElement; @@ -127,3 +127,50 @@ describe('InputDriver', () => { div.dispatchEvent(new KeyboardEvent('keydown', { key: 'x' })); }); }); + +describe("#keyFromKeyboardEvent", () => { + it('returns from keyboard input Ctrl+X', () => { + let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true, + })); + expect(k.key).to.equal('x'); + expect(k.shift).to.be.false; + expect(k.ctrl).to.be.true; + expect(k.alt).to.be.false; + expect(k.meta).to.be.true; + }); + + it('returns from keyboard input Shift+Esc', () => { + let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + key: 'Escape', shiftKey: true, ctrlKey: false, altKey: false, metaKey: true + })); + expect(k.key).to.equal('Esc'); + expect(k.shift).to.be.true; + expect(k.ctrl).to.be.false; + expect(k.alt).to.be.false; + expect(k.meta).to.be.true; + }); + + it('returns from keyboard input Ctrl+$', () => { + // $ required shift pressing on most keyboards + let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + key: '$', shiftKey: true, ctrlKey: true, altKey: false, metaKey: false + })); + expect(k.key).to.equal('$'); + expect(k.shift).to.be.false; + expect(k.ctrl).to.be.true; + expect(k.alt).to.be.false; + expect(k.meta).to.be.false; + }); + + it('returns from keyboard input Crtl+Space', () => { + let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + key: ' ', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false + })); + expect(k.key).to.equal('Space'); + expect(k.shift).to.be.false; + expect(k.ctrl).to.be.true; + expect(k.alt).to.be.false; + expect(k.meta).to.be.false; + }); +}); diff --git a/test/content/domains/Key.test.ts b/test/content/domains/Key.test.ts deleted file mode 100644 index 8e62f80..0000000 --- a/test/content/domains/Key.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import Key from '../../../src/content/domains/Key'; -import { expect } from 'chai' - -describe("Key", () => { - describe('fromKeyboardEvent', () => { - it('returns from keyboard input Ctrl+X', () => { - let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { - key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true, - })); - expect(k.key).to.equal('x'); - expect(k.shift).to.be.false; - expect(k.ctrl).to.be.true; - expect(k.alt).to.be.false; - expect(k.meta).to.be.true; - }); - - it('returns from keyboard input Shift+Esc', () => { - let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { - key: 'Escape', shiftKey: true, ctrlKey: false, altKey: false, metaKey: true - })); - expect(k.key).to.equal('Esc'); - expect(k.shift).to.be.true; - expect(k.ctrl).to.be.false; - expect(k.alt).to.be.false; - expect(k.meta).to.be.true; - }); - - it('returns from keyboard input Ctrl+$', () => { - // $ required shift pressing on most keyboards - let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { - key: '$', shiftKey: true, ctrlKey: true, altKey: false, metaKey: false - })); - expect(k.key).to.equal('$'); - expect(k.shift).to.be.false; - expect(k.ctrl).to.be.true; - expect(k.alt).to.be.false; - expect(k.meta).to.be.false; - }); - - it('returns from keyboard input Crtl+Space', () => { - let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { - key: ' ', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false - })); - expect(k.key).to.equal('Space'); - expect(k.shift).to.be.false; - expect(k.ctrl).to.be.true; - expect(k.alt).to.be.false; - expect(k.meta).to.be.false; - }); - }); - - describe('fromMapKey', () => { - it('return for X', () => { - let key = Key.fromMapKey('x'); - expect(key.key).to.equal('x'); - expect(key.shift).to.be.false; - expect(key.ctrl).to.be.false; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - - it('return for Shift+X', () => { - let key = Key.fromMapKey('X'); - expect(key.key).to.equal('X'); - expect(key.shift).to.be.true; - expect(key.ctrl).to.be.false; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - - it('return for Ctrl+X', () => { - let key = Key.fromMapKey(''); - expect(key.key).to.equal('x'); - expect(key.shift).to.be.false; - expect(key.ctrl).to.be.true; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - - it('returns for Ctrl+Meta+X', () => { - let key = Key.fromMapKey(''); - expect(key.key).to.equal('x'); - expect(key.shift).to.be.false; - expect(key.ctrl).to.be.true; - expect(key.alt).to.be.false; - expect(key.meta).to.be.true; - }); - - it('returns for Ctrl+Shift+x', () => { - let key = Key.fromMapKey(''); - expect(key.key).to.equal('X'); - expect(key.shift).to.be.true; - expect(key.ctrl).to.be.true; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - - it('returns for Shift+Esc', () => { - let key = Key.fromMapKey(''); - expect(key.key).to.equal('Esc'); - expect(key.shift).to.be.true; - expect(key.ctrl).to.be.false; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - - it('returns for Ctrl+Esc', () => { - let key = Key.fromMapKey(''); - expect(key.key).to.equal('Esc'); - expect(key.shift).to.be.false; - expect(key.ctrl).to.be.true; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - - it('returns for Ctrl+Esc', () => { - let key = Key.fromMapKey(''); - expect(key.key).to.equal('Space'); - expect(key.shift).to.be.false; - expect(key.ctrl).to.be.true; - expect(key.alt).to.be.false; - expect(key.meta).to.be.false; - }); - }); - - describe('equals', () => { - expect(new Key({ - key: 'x', shift: false, ctrl: true, alt: false, meta: false, - }).equals(new Key({ - key: 'x', shift: false, ctrl: true, alt: false, meta: false, - }))).to.be.true; - - expect(new Key({ - key: 'x', shift: false, ctrl: false, alt: false, meta: false, - }).equals(new Key({ - key: 'X', shift: true, ctrl: false, alt: false, meta: false, - }))).to.be.false; - }); -}); diff --git a/test/content/domains/KeySequence.test.ts b/test/content/domains/KeySequence.test.ts deleted file mode 100644 index 62af165..0000000 --- a/test/content/domains/KeySequence.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import KeySequence from '../../../src/content/domains/KeySequence'; -import Key from '../../../src/content/domains/Key'; -import { expect } from 'chai' - -describe("KeySequence", () => { - describe('#push', () => { - it('append a key to the sequence', () => { - let seq = new KeySequence([]); - seq.push(Key.fromMapKey('g')); - seq.push(Key.fromMapKey('')); - - expect(seq.keys[0].key).to.equal('g'); - expect(seq.keys[1].key).to.equal('U'); - expect(seq.keys[1].shift).to.be.true; - }) - }); - - describe('#startsWith', () => { - it('returns true if the key sequence starts with param', () => { - let seq = new KeySequence([ - Key.fromMapKey('g'), - Key.fromMapKey(''), - ]); - - expect(seq.startsWith(new KeySequence([ - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), Key.fromMapKey(''), - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), Key.fromMapKey(''), Key.fromMapKey('x'), - ]))).to.be.false; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('h'), - ]))).to.be.false; - }); - - it('returns true if the empty sequence starts with an empty sequence', () => { - let seq = new KeySequence([]); - - expect(seq.startsWith(new KeySequence([]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('h'), - ]))).to.be.false; - }) - }); - - describe('#fromMapKeys', () => { - it('returns mapped keys for Shift+Esc', () => { - let keys = KeySequence.fromMapKeys('').keys; - expect(keys).to.have.lengthOf(1); - expect(keys[0].key).to.equal('Esc'); - expect(keys[0].shift).to.be.true; - }); - - it('returns mapped keys for ad', () => { - let keys = KeySequence.fromMapKeys('ad').keys; - expect(keys).to.have.lengthOf(5); - expect(keys[0].key).to.equal('a'); - expect(keys[1].ctrl).to.be.true; - expect(keys[1].key).to.equal('b'); - expect(keys[2].alt).to.be.true; - expect(keys[2].key).to.equal('c'); - expect(keys[3].key).to.equal('d'); - expect(keys[4].meta).to.be.true; - expect(keys[4].key).to.equal('e'); - }); - }) -}); diff --git a/test/content/repositories/KeymapRepository.test.ts b/test/content/repositories/KeymapRepository.test.ts index da5624e..df013df 100644 --- a/test/content/repositories/KeymapRepository.test.ts +++ b/test/content/repositories/KeymapRepository.test.ts @@ -1,7 +1,7 @@ import KeymapRepository, { KeymapRepositoryImpl } from '../../../src/content/repositories/KeymapRepository'; -import Key from '../../../src/content/domains/Key' import { expect } from 'chai'; +import Key from "../../../src/shared/settings/Key"; describe('KeymapRepositoryImpl', () => { let sut: KeymapRepository; diff --git a/test/shared/settings/Key.test.ts b/test/shared/settings/Key.test.ts new file mode 100644 index 0000000..8222d5a --- /dev/null +++ b/test/shared/settings/Key.test.ts @@ -0,0 +1,92 @@ +import { expect } from 'chai' +import Key from '../../../src/shared/settings/Key'; + +describe("Key", () => { + describe('fromMapKey', () => { + it('return for X', () => { + let key = Key.fromMapKey('x'); + expect(key.key).to.equal('x'); + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + + it('return for Shift+X', () => { + let key = Key.fromMapKey('X'); + expect(key.key).to.equal('X'); + expect(key.shift).to.be.true; + expect(key.ctrl).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + + it('return for Ctrl+X', () => { + let key = Key.fromMapKey(''); + expect(key.key).to.equal('x'); + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + + it('returns for Ctrl+Meta+X', () => { + let key = Key.fromMapKey(''); + expect(key.key).to.equal('x'); + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.true; + }); + + it('returns for Ctrl+Shift+x', () => { + let key = Key.fromMapKey(''); + expect(key.key).to.equal('X'); + expect(key.shift).to.be.true; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + + it('returns for Shift+Esc', () => { + let key = Key.fromMapKey(''); + expect(key.key).to.equal('Esc'); + expect(key.shift).to.be.true; + expect(key.ctrl).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + + it('returns for Ctrl+Esc', () => { + let key = Key.fromMapKey(''); + expect(key.key).to.equal('Esc'); + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + + it('returns for Ctrl+Esc', () => { + let key = Key.fromMapKey(''); + expect(key.key).to.equal('Space'); + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; + }); + }); + + describe('equals', () => { + expect(new Key({ + key: 'x', shift: false, ctrl: true, alt: false, meta: false, + }).equals(new Key({ + key: 'x', shift: false, ctrl: true, alt: false, meta: false, + }))).to.be.true; + + expect(new Key({ + key: 'x', shift: false, ctrl: false, alt: false, meta: false, + }).equals(new Key({ + key: 'X', shift: true, ctrl: false, alt: false, meta: false, + }))).to.be.false; + }); +}); diff --git a/test/shared/settings/KeySequence.test.ts b/test/shared/settings/KeySequence.test.ts new file mode 100644 index 0000000..361cbd1 --- /dev/null +++ b/test/shared/settings/KeySequence.test.ts @@ -0,0 +1,72 @@ +import KeySequence from '../../../src/shared/settings/KeySequence'; +import { expect } from 'chai' +import Key from "../../../src/shared/settings/Key"; + +describe("KeySequence", () => { + describe('#push', () => { + it('append a key to the sequence', () => { + let seq = new KeySequence([]); + seq.push(Key.fromMapKey('g')); + seq.push(Key.fromMapKey('')); + + expect(seq.keys[0].key).to.equal('g'); + expect(seq.keys[1].key).to.equal('U'); + expect(seq.keys[1].shift).to.be.true; + }) + }); + + describe('#startsWith', () => { + it('returns true if the key sequence starts with param', () => { + let seq = new KeySequence([ + Key.fromMapKey('g'), + Key.fromMapKey(''), + ]); + + expect(seq.startsWith(new KeySequence([ + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), Key.fromMapKey(''), + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), Key.fromMapKey(''), Key.fromMapKey('x'), + ]))).to.be.false; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('h'), + ]))).to.be.false; + }); + + it('returns true if the empty sequence starts with an empty sequence', () => { + let seq = new KeySequence([]); + + expect(seq.startsWith(new KeySequence([]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('h'), + ]))).to.be.false; + }) + }); + + describe('#fromMapKeys', () => { + it('returns mapped keys for Shift+Esc', () => { + let keys = KeySequence.fromMapKeys('').keys; + expect(keys).to.have.lengthOf(1); + expect(keys[0].key).to.equal('Esc'); + expect(keys[0].shift).to.be.true; + }); + + it('returns mapped keys for ad', () => { + let keys = KeySequence.fromMapKeys('ad').keys; + expect(keys).to.have.lengthOf(5); + expect(keys[0].key).to.equal('a'); + expect(keys[1].ctrl).to.be.true; + expect(keys[1].key).to.equal('b'); + expect(keys[2].alt).to.be.true; + expect(keys[2].key).to.equal('c'); + expect(keys[3].key).to.equal('d'); + expect(keys[4].meta).to.be.true; + expect(keys[4].key).to.equal('e'); + }); + }) +}); -- cgit v1.2.3