Make Keymap class

jh-changes
Shin'ya UEOKA 5 years ago
parent b496cea582
commit 410ffbb037
  1. 5
      src/background/infrastructures/ContentMessageListener.ts
  2. 8
      src/background/repositories/SettingRepository.ts
  3. 4
      src/content/client/SettingClient.ts
  4. 21
      src/content/usecases/KeymapUseCase.ts
  5. 8
      src/settings/actions/index.ts
  6. 6
      src/settings/actions/setting.ts
  7. 12
      src/settings/components/index.tsx
  8. 6
      src/settings/reducers/setting.ts
  9. 53
      src/shared/SettingData.ts
  10. 27
      src/shared/Settings.ts
  11. 37
      src/shared/settings/Keymaps.ts
  12. 6
      test/content/repositories/SettingRepository.test.ts
  13. 48
      test/shared/SettingData.test.ts
  14. 45
      test/shared/Settings.test.ts
  15. 66
      test/shared/settings/Keymaps.test.ts

@ -8,6 +8,7 @@ import AddonEnabledController from '../controllers/AddonEnabledController';
import LinkController from '../controllers/LinkController'; import LinkController from '../controllers/LinkController';
import OperationController from '../controllers/OperationController'; import OperationController from '../controllers/OperationController';
import MarkController from '../controllers/MarkController'; import MarkController from '../controllers/MarkController';
import { toJSON } from '../../shared/Settings';
@injectable() @injectable()
export default class ContentMessageListener { export default class ContentMessageListener {
@ -101,8 +102,8 @@ export default class ContentMessageListener {
return this.commandController.exec(text); return this.commandController.exec(text);
} }
onSettingsQuery(): Promise<any> { async onSettingsQuery(): Promise<any> {
return this.settingController.getSetting(); return toJSON(await this.settingController.getSetting());
} }
onFindGetKeyword(): Promise<string> { onFindGetKeyword(): Promise<string> {

@ -1,6 +1,6 @@
import { injectable } from 'tsyringe'; import { injectable } from 'tsyringe';
import MemoryStorage from '../infrastructures/MemoryStorage'; import MemoryStorage from '../infrastructures/MemoryStorage';
import Settings from '../../shared/Settings'; import Settings, { valueOf, toJSON } from '../../shared/Settings';
import * as PropertyDefs from '../../shared/property-defs'; import * as PropertyDefs from '../../shared/property-defs';
const CACHED_SETTING_KEY = 'setting'; const CACHED_SETTING_KEY = 'setting';
@ -14,11 +14,13 @@ export default class SettingRepository {
} }
get(): Promise<Settings> { get(): Promise<Settings> {
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 { 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( async setProperty(

@ -1,4 +1,4 @@
import Settings from '../../shared/Settings'; import Settings, { valueOf } from '../../shared/Settings';
import * as messages from '../../shared/messages'; import * as messages from '../../shared/messages';
export default interface SettingClient { export default interface SettingClient {
@ -10,6 +10,6 @@ export class SettingClientImpl {
let settings = await browser.runtime.sendMessage({ let settings = await browser.runtime.sendMessage({
type: messages.SETTINGS_QUERY, type: messages.SETTINGS_QUERY,
}); });
return settings as Settings; return valueOf(settings);
} }
} }

@ -3,16 +3,16 @@ import KeymapRepository from '../repositories/KeymapRepository';
import SettingRepository from '../repositories/SettingRepository'; import SettingRepository from '../repositories/SettingRepository';
import AddonEnabledRepository from '../repositories/AddonEnabledRepository'; import AddonEnabledRepository from '../repositories/AddonEnabledRepository';
import * as operations from '../../shared/operations'; import * as operations from '../../shared/operations';
import { Keymaps } from '../../shared/Settings';
import Key from '../domains/Key'; import Key from '../domains/Key';
import KeySequence from '../domains/KeySequence'; import KeySequence from '../domains/KeySequence';
import Keymaps from '../../shared/settings/Keymaps';
type KeymapEntityMap = Map<KeySequence, operations.Operation>; type KeymapEntityMap = Map<KeySequence, operations.Operation>;
const reservedKeymaps: Keymaps = { const reservedKeymaps = Keymaps.fromJSON({
'<Esc>': { type: operations.CANCEL }, '<Esc>': { type: operations.CANCEL },
'<C-[>': { type: operations.CANCEL }, '<C-[>': { type: operations.CANCEL },
}; });
@injectable() @injectable()
export default class KeymapUseCase { export default class KeymapUseCase {
@ -65,16 +65,11 @@ export default class KeymapUseCase {
} }
private keymapEntityMap(): KeymapEntityMap { private keymapEntityMap(): KeymapEntityMap {
let keymaps = { let keymaps = this.settingRepository.get().keymaps.combine(reservedKeymaps);
...this.settingRepository.get().keymaps, let entries = keymaps.entries().map(entry => [
...reservedKeymaps, KeySequence.fromMapKeys(entry[0]),
}; entry[1],
let entries = Object.entries(keymaps).map((entry) => { ]) as [KeySequence, operations.Operation][];
return [
KeySequence.fromMapKeys(entry[0]),
entry[1],
];
}) as [KeySequence, operations.Operation][];
return new Map<KeySequence, operations.Operation>(entries); return new Map<KeySequence, operations.Operation>(entries);
} }
} }

@ -1,5 +1,5 @@
import { import {
JSONSettings, FormSettings, SettingSource, JSONTextSettings, FormSettings, SettingSource,
} from '../../shared/SettingData'; } from '../../shared/SettingData';
// Settings // Settings
@ -11,14 +11,14 @@ export const SETTING_SWITCH_TO_JSON = 'setting.switch.to.json';
interface SettingSetSettingsAcion { interface SettingSetSettingsAcion {
type: typeof SETTING_SET_SETTINGS; type: typeof SETTING_SET_SETTINGS;
source: SettingSource; source: SettingSource;
json?: JSONSettings; json?: JSONTextSettings;
form?: FormSettings; form?: FormSettings;
} }
interface SettingShowErrorAction { interface SettingShowErrorAction {
type: typeof SETTING_SHOW_ERROR; type: typeof SETTING_SHOW_ERROR;
error: string; error: string;
json: JSONSettings; json: JSONTextSettings;
} }
interface SettingSwitchToFormAction { interface SettingSwitchToFormAction {
@ -28,7 +28,7 @@ interface SettingSwitchToFormAction {
interface SettingSwitchToJsonAction { interface SettingSwitchToJsonAction {
type: typeof SETTING_SWITCH_TO_JSON; type: typeof SETTING_SWITCH_TO_JSON;
json: JSONSettings, json: JSONTextSettings,
} }
export type SettingAction = export type SettingAction =

@ -1,7 +1,7 @@
import * as actions from './index'; import * as actions from './index';
import * as storages from '../storage'; import * as storages from '../storage';
import SettingData, { import SettingData, {
JSONSettings, FormSettings, SettingSource, JSONTextSettings, FormSettings, SettingSource,
} from '../../shared/SettingData'; } from '../../shared/SettingData';
const load = async(): Promise<actions.SettingAction> => { const load = async(): Promise<actions.SettingAction> => {
@ -26,7 +26,7 @@ const save = async(data: SettingData): Promise<actions.SettingAction> => {
return set(data); return set(data);
}; };
const switchToForm = (json: JSONSettings): actions.SettingAction => { const switchToForm = (json: JSONTextSettings): actions.SettingAction => {
try { try {
// toSettings exercise validation // toSettings exercise validation
let form = FormSettings.fromSettings(json.toSettings()); let form = FormSettings.fromSettings(json.toSettings());
@ -44,7 +44,7 @@ const switchToForm = (json: JSONSettings): actions.SettingAction => {
}; };
const switchToJson = (form: FormSettings): actions.SettingAction => { const switchToJson = (form: FormSettings): actions.SettingAction => {
let json = JSONSettings.fromSettings(form.toSettings()); let json = JSONTextSettings.fromSettings(form.toSettings());
return { return {
type: actions.SETTING_SWITCH_TO_JSON, type: actions.SETTING_SWITCH_TO_JSON,
json, json,

@ -8,7 +8,7 @@ import BlacklistForm from './form/BlacklistForm';
import PropertiesForm from './form/PropertiesForm'; import PropertiesForm from './form/PropertiesForm';
import * as settingActions from '../../settings/actions/setting'; import * as settingActions from '../../settings/actions/setting';
import SettingData, { import SettingData, {
JSONSettings, FormKeymaps, FormSearch, FormSettings, JSONTextSettings, FormKeymaps, FormSearch, FormSettings,
} from '../../shared/SettingData'; } from '../../shared/SettingData';
import { State as AppState } from '../reducers/setting'; import { State as AppState } from '../reducers/setting';
import * as settings from '../../shared/Settings'; import * as settings from '../../shared/Settings';
@ -75,7 +75,7 @@ class SettingsComponent extends React.Component<Props> {
</div>; </div>;
} }
renderJsonFields(json: JSONSettings, error: string) { renderJsonFields(json: JSONTextSettings, error: string) {
return <div> return <div>
<Input <Input
type='textarea' type='textarea'
@ -85,7 +85,7 @@ class SettingsComponent extends React.Component<Props> {
error={error} error={error}
onValueChange={this.bindJson.bind(this)} onValueChange={this.bindJson.bind(this)}
onBlur={this.save.bind(this)} onBlur={this.save.bind(this)}
value={json.toJSON()} value={json.toJSONText()}
/> />
</div>; </div>;
} }
@ -97,7 +97,7 @@ class SettingsComponent extends React.Component<Props> {
fields = this.renderFormFields(this.props.form); fields = this.renderFormFields(this.props.form);
} else if (this.props.source === 'json') { } else if (this.props.source === 'json') {
fields = this.renderJsonFields( fields = this.renderJsonFields(
this.props.json as JSONSettings, this.props.error); this.props.json as JSONTextSettings, this.props.error);
} }
return ( return (
<div> <div>
@ -165,7 +165,7 @@ class SettingsComponent extends React.Component<Props> {
bindJson(_name: string, value: string) { bindJson(_name: string, value: string) {
let data = new SettingData({ let data = new SettingData({
source: this.props.source, source: this.props.source,
json: JSONSettings.valueOf(value), json: JSONTextSettings.fromText(value),
}); });
this.props.dispatch(settingActions.set(data)); this.props.dispatch(settingActions.set(data));
} }
@ -183,7 +183,7 @@ class SettingsComponent extends React.Component<Props> {
return; return;
} }
this.props.dispatch( this.props.dispatch(
settingActions.switchToForm(this.props.json as JSONSettings)); settingActions.switchToForm(this.props.json as JSONTextSettings));
this.save(); this.save();
} }
} }

@ -1,18 +1,18 @@
import * as actions from '../actions'; import * as actions from '../actions';
import { import {
JSONSettings, FormSettings, SettingSource, JSONTextSettings, FormSettings, SettingSource,
} from '../../shared/SettingData'; } from '../../shared/SettingData';
export interface State { export interface State {
source: SettingSource; source: SettingSource;
json?: JSONSettings; json?: JSONTextSettings;
form?: FormSettings; form?: FormSettings;
error: string; error: string;
} }
const defaultState: State = { const defaultState: State = {
source: SettingSource.JSON, source: SettingSource.JSON,
json: JSONSettings.valueOf(''), json: JSONTextSettings.fromText(''),
error: '', error: '',
}; };

@ -1,5 +1,6 @@
import * as operations from './operations'; import * as operations from './operations';
import Settings, * as settings from './Settings'; import Settings, * as settings from './Settings';
import Keymaps from './settings/Keymaps';
export class FormKeymaps { export class FormKeymaps {
private data: {[op: string]: string}; private data: {[op: string]: string};
@ -8,8 +9,8 @@ export class FormKeymaps {
this.data = data; this.data = data;
} }
toKeymaps(): settings.Keymaps { toKeymaps(): Keymaps {
let keymaps: settings.Keymaps = {}; let keymaps: { [key: string]: operations.Operation } = {};
for (let name of Object.keys(this.data)) { for (let name of Object.keys(this.data)) {
let [type, argStr] = name.split('?'); let [type, argStr] = name.split('?');
let args = {}; let args = {};
@ -19,7 +20,7 @@ export class FormKeymaps {
let key = this.data[name]; let key = this.data[name];
keymaps[key] = operations.valueOf({ type, ...args }); keymaps[key] = operations.valueOf({ type, ...args });
} }
return keymaps; return Keymaps.fromJSON(keymaps);
} }
toJSON(): {[op: string]: string} { toJSON(): {[op: string]: string} {
@ -42,10 +43,11 @@ export class FormKeymaps {
return new FormKeymaps(data); return new FormKeymaps(data);
} }
static fromKeymaps(keymaps: settings.Keymaps): FormKeymaps { static fromKeymaps(keymaps: Keymaps): FormKeymaps {
let json = keymaps.toJSON();
let data: {[op: string]: string} = {}; let data: {[op: string]: string} = {};
for (let key of Object.keys(keymaps)) { for (let key of Object.keys(json)) {
let op = keymaps[key]; let op = json[key];
let args = { ...op }; let args = { ...op };
delete args.type; delete args.type;
@ -109,27 +111,32 @@ export class FormSearch {
} }
} }
export class JSONSettings { export class JSONTextSettings {
private json: string; constructor(
private json: string,
constructor(json: any) { ) {
this.json = json;
} }
toSettings(): Settings { toSettings(): Settings {
return settings.valueOf(JSON.parse(this.json)); return settings.valueOf(JSON.parse(this.json));
} }
toJSON(): string { toJSONText(): string {
return this.json; return this.json;
} }
static valueOf(o: ReturnType<JSONSettings['toJSON']>): JSONSettings { static fromText(o: string): JSONTextSettings {
return new JSONSettings(o); return new JSONTextSettings(o);
} }
static fromSettings(data: Settings): JSONSettings { static fromSettings(data: Settings): JSONTextSettings {
return new JSONSettings(JSON.stringify(data, undefined, 2)); 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 { toSettings(): Settings {
return settings.valueOf({ return settings.valueOf({
keymaps: this.keymaps.toKeymaps(), keymaps: this.keymaps.toKeymaps().toJSON(),
search: this.search.toSearchSettings(), search: this.search.toSearchSettings(),
properties: this.properties, properties: this.properties,
blacklist: this.blacklist, blacklist: this.blacklist,
@ -244,7 +251,7 @@ export enum SettingSource {
export default class SettingData { export default class SettingData {
private source: SettingSource; private source: SettingSource;
private json?: JSONSettings; private json?: JSONTextSettings;
private form?: FormSettings; private form?: FormSettings;
@ -252,7 +259,7 @@ export default class SettingData {
source, json, form source, json, form
}: { }: {
source: SettingSource, source: SettingSource,
json?: JSONSettings, json?: JSONTextSettings,
form?: FormSettings, form?: FormSettings,
}) { }) {
this.source = source; this.source = source;
@ -264,7 +271,7 @@ export default class SettingData {
return this.source; return this.source;
} }
getJSON(): JSONSettings { getJSON(): JSONTextSettings {
if (!this.json) { if (!this.json) {
throw new TypeError('json settings not set'); throw new TypeError('json settings not set');
} }
@ -283,7 +290,7 @@ export default class SettingData {
case SettingSource.JSON: case SettingSource.JSON:
return { return {
source: this.source, source: this.source,
json: (this.json as JSONSettings).toJSON(), json: (this.json as JSONTextSettings).toJSONText(),
}; };
case SettingSource.Form: case SettingSource.Form:
return { return {
@ -313,8 +320,8 @@ export default class SettingData {
case SettingSource.JSON: case SettingSource.JSON:
return new SettingData({ return new SettingData({
source: o.source, source: o.source,
json: JSONSettings.valueOf( json: JSONTextSettings.fromText(
o.json as ReturnType<JSONSettings['toJSON']>), o.json as ReturnType<JSONTextSettings['toJSONText']>),
}); });
case SettingSource.Form: case SettingSource.Form:
return new SettingData({ return new SettingData({

@ -1,7 +1,5 @@
import * as operations from './operations';
import * as PropertyDefs from './property-defs'; import * as PropertyDefs from './property-defs';
import Keymaps from './settings/Keymaps';
export type Keymaps = {[key: string]: operations.Operation};
export interface Search { export interface Search {
default: string; default: string;
@ -21,14 +19,6 @@ export default interface Settings {
blacklist: string[]; 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 => { export const searchValueOf = (o: any): Search => {
if (typeof o.default !== 'string') { if (typeof o.default !== 'string') {
throw new TypeError('string field "default" not set"'); 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)) { for (let key of Object.keys(o)) {
switch (key) { switch (key) {
case 'keymaps': case 'keymaps':
settings.keymaps = keymapsValueOf(o.keymaps); settings.keymaps = Keymaps.fromJSON(o.keymaps);
break; break;
case 'search': case 'search':
settings.search = searchValueOf(o.search); settings.search = searchValueOf(o.search);
@ -115,8 +105,17 @@ export const valueOf = (o: any): Settings => {
return 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 = { export const DefaultSetting: Settings = {
keymaps: { keymaps: Keymaps.fromJSON({
'0': { 'type': 'scroll.home' }, '0': { 'type': 'scroll.home' },
':': { 'type': 'command.show' }, ':': { 'type': 'command.show' },
'o': { 'type': 'command.show.open', 'alter': false }, 'o': { 'type': 'command.show.open', 'alter': false },
@ -179,7 +178,7 @@ export const DefaultSetting: Settings = {
'N': { 'type': 'find.prev' }, 'N': { 'type': 'find.prev' },
'.': { 'type': 'repeat.last' }, '.': { 'type': 'repeat.last' },
'<S-Esc>': { 'type': 'addon.toggle.enabled' } '<S-Esc>': { 'type': 'addon.toggle.enabled' }
}, }),
search: { search: {
default: 'google', default: 'google',
engines: { engines: {

@ -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);
}
}

@ -1,12 +1,13 @@
import { SettingRepositoryImpl } from '../../../src/content/repositories/SettingRepository'; import { SettingRepositoryImpl } from '../../../src/content/repositories/SettingRepository';
import { expect } from 'chai'; import { expect } from 'chai';
import Keymaps from '../../../src/shared/settings/Keymaps';
describe('SettingRepositoryImpl', () => { describe('SettingRepositoryImpl', () => {
it('updates and gets current value', () => { it('updates and gets current value', () => {
let sut = new SettingRepositoryImpl(); let sut = new SettingRepositoryImpl();
let settings = { let settings = {
keymaps: {}, keymaps: Keymaps.fromJSON({}),
search: { search: {
default: 'google', default: 'google',
engines: { engines: {
@ -19,7 +20,7 @@ describe('SettingRepositoryImpl', () => {
complete: 'sbh', complete: 'sbh',
}, },
blacklist: [], blacklist: [],
} };
sut.set(settings); sut.set(settings);
@ -27,4 +28,3 @@ describe('SettingRepositoryImpl', () => {
expect(actual.properties.hintchars).to.equal('abcd1234'); expect(actual.properties.hintchars).to.equal('abcd1234');
}); });
}); });

@ -1,8 +1,9 @@
import SettingData, { import SettingData, {
FormKeymaps, JSONSettings, FormSettings, FormKeymaps, JSONTextSettings, FormSettings,
} from '../../src/shared/SettingData'; } from '../../src/shared/SettingData';
import Settings, { Keymaps } from '../../src/shared/Settings'; import Settings from '../../src/shared/Settings';
import { expect } from 'chai'; import { expect } from 'chai';
import Keymaps from '../../src/shared/settings/Keymaps';
describe('shared/SettingData', () => { describe('shared/SettingData', () => {
describe('FormKeymaps', () => { describe('FormKeymaps', () => {
@ -11,9 +12,9 @@ describe('shared/SettingData', () => {
let data = { let data = {
'scroll.vertically?{"count":1}': 'j', 'scroll.vertically?{"count":1}': 'j',
'scroll.home': '0', 'scroll.home': '0',
} };
let keymaps = FormKeymaps.valueOf(data).toKeymaps(); let keymaps = FormKeymaps.valueOf(data).toKeymaps().toJSON();
expect(keymaps).to.deep.equal({ expect(keymaps).to.deep.equal({
'j': { type: 'scroll.vertically', count: 1 }, 'j': { type: 'scroll.vertically', count: 1 },
'0': { type: 'scroll.home' }, '0': { type: 'scroll.home' },
@ -23,13 +24,13 @@ describe('shared/SettingData', () => {
describe('#fromKeymaps to #toJSON', () => { describe('#fromKeymaps to #toJSON', () => {
it('create from a Keymaps and create a JSON object', () => { it('create from a Keymaps and create a JSON object', () => {
let data: Keymaps = { let keymaps: Keymaps = Keymaps.fromJSON({
'j': { type: 'scroll.vertically', count: 1 }, 'j': { type: 'scroll.vertically', count: 1 },
'0': { type: 'scroll.home' }, '0': { type: 'scroll.home' },
} });
let keymaps = FormKeymaps.fromKeymaps(data).toJSON(); let form = FormKeymaps.fromKeymaps(keymaps).toJSON();
expect(keymaps).to.deep.equal({ expect(form).to.deep.equal({
'scroll.vertically?{"count":1}': 'j', 'scroll.vertically?{"count":1}': 'j',
'scroll.home': '0', 'scroll.home': '0',
}); });
@ -56,15 +57,20 @@ describe('shared/SettingData', () => {
"blacklist": [] "blacklist": []
}`; }`;
let settings = JSONSettings.valueOf(o).toSettings(); let settings = JSONTextSettings.fromText(o).toSettings();
expect(settings).to.deep.equal(JSON.parse(o)); expect({
keymaps: settings.keymaps.toJSON(),
search: settings.search,
properties: settings.properties,
blacklist: settings.blacklist,
}).to.deep.equal(JSON.parse(o));
}); });
}); });
describe('#fromSettings to #toJSON', () => { describe('#fromSettings to #toJSON', () => {
it('create from a Settings and create a JSON string', () => { it('create from a Settings and create a JSON string', () => {
let o = { let o = {
keymaps: {}, keymaps: Keymaps.fromJSON({}),
search: { search: {
default: "google", default: "google",
engines: { engines: {
@ -79,8 +85,13 @@ describe('shared/SettingData', () => {
blacklist: [], blacklist: [],
}; };
let json = JSONSettings.fromSettings(o).toJSON(); let json = JSONTextSettings.fromSettings(o).toJSONText();
expect(JSON.parse(json)).to.deep.equal(o); 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(); 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: { keymaps: {
'j': { type: 'scroll.vertically', count: 1 }, 'j': { type: 'scroll.vertically', count: 1 },
'0': { type: 'scroll.home' }, '0': { type: 'scroll.home' },
@ -132,10 +148,10 @@ describe('shared/SettingData', () => {
describe('#fromSettings to #toJSON', () => { describe('#fromSettings to #toJSON', () => {
it('create from a Settings and create a JSON string', () => { it('create from a Settings and create a JSON string', () => {
let data: Settings = { let data: Settings = {
keymaps: { keymaps: Keymaps.fromJSON({
'j': { type: 'scroll.vertically', count: 1 }, 'j': { type: 'scroll.vertically', count: 1 },
'0': { type: 'scroll.home' }, '0': { type: 'scroll.home' },
}, }),
search: { search: {
default: "google", default: "google",
engines: { engines: {

@ -1,31 +1,7 @@
import * as settings from '../../src/shared/Settings'; import * as settings from '../../src/shared/Settings';
import { expect } from 'chai'; import {expect} from 'chai';
describe('Settings', () => { 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', () => { describe('#searchValueOf', () => {
it('returns search settings by valid settings', () => { it('returns search settings by valid settings', () => {
let search = settings.searchValueOf({ let search = settings.searchValueOf({
@ -110,16 +86,6 @@ describe('Settings', () => {
complete: "sbh" 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', () => { describe('#blacklistValueOf', () => {
@ -161,7 +127,12 @@ describe('Settings', () => {
"blacklist": [] "blacklist": []
}); });
expect(x).to.deep.equal({ expect({
keymaps: x.keymaps.toJSON(),
search: x.search,
properties: x.properties,
blacklist: x.blacklist,
}).to.deep.equal({
keymaps: {}, keymaps: {},
search: { search: {
default: "google", default: "google",
@ -180,7 +151,7 @@ describe('Settings', () => {
it('sets default settings', () => { it('sets default settings', () => {
let value = settings.valueOf({}); 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.properties).to.not.be.empty;
expect(value.search.default).to.be.a('string'); expect(value.search.default).to.be.a('string');
expect(value.search.engines).to.be.an('object'); expect(value.search.engines).to.be.an('object');

@ -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" }],
]);
});
});
});