diff --git a/src/background/components/background.js b/src/background/components/background.js index 86e96fa..23131d2 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -21,11 +21,6 @@ export default class BackgroundComponent { let settings = this.store.getState().setting; switch (message.type) { - case messages.CONSOLE_ENTER_COMMAND: - this.store.dispatch( - commandActions.exec(sender.tab, message.text, settings.value), - ); - return this.broadcastSettingsChanged(); } } diff --git a/src/background/repositories/setting.js b/src/background/repositories/setting.js index d9c481d..6d48525 100644 --- a/src/background/repositories/setting.js +++ b/src/background/repositories/setting.js @@ -14,4 +14,10 @@ export default class SettingRepository { update(value) { return this.cache.set(CACHED_SETTING_KEY, value); } + + async setProperty(name, value) { + let current = await this.get(); + current.properties[name] = value; + return this.update(current); + } } diff --git a/src/background/usecases/command.js b/src/background/usecases/command.js index 1d4744e..7887509 100644 --- a/src/background/usecases/command.js +++ b/src/background/usecases/command.js @@ -1,8 +1,11 @@ +import * as parsers from './parsers'; import TabPresenter from '../presenters/tab'; import WindowPresenter from '../presenters/window'; import SettingRepository from '../repositories/setting'; import BookmarkRepository from '../repositories/bookmark'; import ConsolePresenter from '../presenters/console'; +import ContentMessageClient from '../infrastructures/content-message-client'; +import * as properties from 'shared/settings/properties'; export default class CommandIndicator { constructor() { @@ -11,6 +14,8 @@ export default class CommandIndicator { this.settingRepository = new SettingRepository(); this.bookmarkRepository = new BookmarkRepository(); this.consolePresenter = new ConsolePresenter(); + + this.contentMessageClient = new ContentMessageClient(); } async open(keywords) { @@ -86,29 +91,18 @@ export default class CommandIndicator { return this.consolePresenter.showInfo(tab.id, message); } - set(keywords) { - // TODO implement set command + async set(keywords) { + if (keywords.length === 0) { + return; + } + let [name, value] = parsers.parseSetOption(keywords, properties.types); + await this.settingRepository.setProperty(name, value); + + return this.contentMessageClient.broadcastSettingsChanged(); } async urlOrSearch(keywords) { - try { - return new URL(keywords).href; - } catch (e) { - if (keywords.includes('.') && !keywords.includes(' ')) { - return 'http://' + keywords; - } - let settings = await this.settingRepository.get(); - let engines = settings.search.engines; - - let template = engines[settings.search.default]; - let query = keywords; - - let first = keywords.trimStart().split(' ')[0]; - if (Object.keys(engines).includes(first)) { - template = engines[first]; - query = keywords.trimStart().slice(first.length).trimStart(); - } - return template.replace('{}', encodeURIComponent(query)); - } + let settings = await this.settingRepository.get(); + return parsers.normalizeUrl(keywords, settings.search); } } diff --git a/src/background/usecases/parsers.js b/src/background/usecases/parsers.js new file mode 100644 index 0000000..650ccd0 --- /dev/null +++ b/src/background/usecases/parsers.js @@ -0,0 +1,50 @@ +const normalizeUrl = (keywords, searchSettings) => { + try { + return new URL(keywords).href; + } catch (e) { + if (keywords.includes('.') && !keywords.includes(' ')) { + return 'http://' + keywords; + } + let template = searchSettings.engines[searchSettings.default]; + let query = keywords; + + let first = keywords.trimStart().split(' ')[0]; + if (Object.keys(searchSettings.engines).includes(first)) { + template = searchSettings.engines[first]; + query = keywords.trimStart().slice(first.length).trimStart(); + } + return template.replace('{}', encodeURIComponent(query)); + } +}; + +const mustNumber = (v) => { + let num = Number(v); + if (isNaN(num)) { + throw new Error('Not number: ' + v); + } + return num; +}; + +const parseSetOption = (word, types) => { + let [key, value] = word.split('='); + if (value === undefined) { + value = !key.startsWith('no'); + key = value ? key : key.slice(2); + } + let type = types[key]; + if (!type) { + throw new Error('Unknown property: ' + key); + } + if (type === 'boolean' && typeof value !== 'boolean' || + type !== 'boolean' && typeof value === 'boolean') { + throw new Error('Invalid argument: ' + word); + } + + switch (type) { + case 'string': return [key, value]; + case 'number': return [key, mustNumber(value)]; + case 'boolean': return [key, value]; + } +}; + +export { normalizeUrl, parseSetOption }; diff --git a/test/background/usecases/parsers.test.js b/test/background/usecases/parsers.test.js new file mode 100644 index 0000000..fa0e757 --- /dev/null +++ b/test/background/usecases/parsers.test.js @@ -0,0 +1,74 @@ +import * as parsers from 'background/usecases/parsers'; + +describe("background/usecases/parsers", () => { + describe("#parsers.parseSetOption", () => { + it('parse set string', () => { + let [key, value] = parsers.parseSetOption('encoding=utf-8', { encoding: 'string' }); + expect(key).to.equal('encoding'); + expect(value).to.equal('utf-8'); + }); + + it('parse set empty string', () => { + let [key, value] = parsers.parseSetOption('encoding=', { encoding: 'string' }); + expect(key).to.equal('encoding'); + expect(value).to.equal(''); + }); + + it('parse set string', () => { + let [key, value] = parsers.parseSetOption('history=50', { history: 'number' }); + expect(key).to.equal('history'); + expect(value).to.equal(50); + }); + + it('parse set boolean', () => { + let [key, value] = parsers.parseSetOption('paste', { paste: 'boolean' }); + expect(key).to.equal('paste'); + expect(value).to.be.true; + + [key, value] = parsers.parseSetOption('nopaste', { paste: 'boolean' }); + expect(key).to.equal('paste'); + expect(value).to.be.false; + }); + + it('throws error on unknown property', () => { + expect(() => parsers.parseSetOption('charset=utf-8', {})).to.throw(Error, 'Unknown'); + expect(() => parsers.parseSetOption('smoothscroll', {})).to.throw(Error, 'Unknown'); + expect(() => parsers.parseSetOption('nosmoothscroll', {})).to.throw(Error, 'Unknown'); + }) + + it('throws error on invalid property', () => { + expect(() => parsers.parseSetOption('charset=utf-8', { charset: 'number' })).to.throw(Error, 'Not number'); + expect(() => parsers.parseSetOption('charset=utf-8', { charset: 'boolean' })).to.throw(Error, 'Invalid'); + expect(() => parsers.parseSetOption('charset=', { charset: 'boolean' })).to.throw(Error, 'Invalid'); + expect(() => parsers.parseSetOption('smoothscroll', { smoothscroll: 'string' })).to.throw(Error, 'Invalid'); + expect(() => parsers.parseSetOption('smoothscroll', { smoothscroll: 'number' })).to.throw(Error, 'Invalid'); + }) + }); + + describe('#normalizeUrl', () => { + const config = { + default: 'google', + engines: { + google: 'https://google.com/search?q={}', + yahoo: 'https://yahoo.com/search?q={}', + } + }; + + it('convertes search url', () => { + expect(parsers.normalizeUrl('google apple', config)) + .to.equal('https://google.com/search?q=apple'); + expect(parsers.normalizeUrl('yahoo apple', config)) + .to.equal('https://yahoo.com/search?q=apple'); + expect(parsers.normalizeUrl('google apple banana', config)) + .to.equal('https://google.com/search?q=apple%20banana'); + expect(parsers.normalizeUrl('yahoo C++CLI', config)) + .to.equal('https://yahoo.com/search?q=C%2B%2BCLI'); + }); + + it('user default search engine', () => { + expect(parsers.normalizeUrl('apple banana', config)) + .to.equal('https://google.com/search?q=apple%20banana'); + }); + }); +}); +