From fbdec04786e28bad45021bef4a74e7077e34282f Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 4 Jan 2018 18:55:24 +0900 Subject: move settings validator --- test/shared/settings/validator.test.js | 82 ++++++++++++++++++++++++++++++++++ test/shared/validators/setting.test.js | 82 ---------------------------------- 2 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 test/shared/settings/validator.test.js delete mode 100644 test/shared/validators/setting.test.js (limited to 'test/shared') diff --git a/test/shared/settings/validator.test.js b/test/shared/settings/validator.test.js new file mode 100644 index 0000000..61d976a --- /dev/null +++ b/test/shared/settings/validator.test.js @@ -0,0 +1,82 @@ +import { expect } from "chai"; +import { validate } from 'shared/settings/validator'; + +describe("setting validator", () => { + describe("unknown top keys", () => { + it('throws an error for unknown settings', () => { + let settings = { keymaps: {}, poison: 123 }; + let fn = validate.bind(undefined, settings) + expect(fn).to.throw(Error, 'poison'); + }) + }); + + describe("keymaps settings", () => { + it('throws an error for unknown operation', () => { + let settings = { + keymaps: { + a: { 'type': 'scroll.home' }, + b: { 'type': 'poison.dressing' }, + } + }; + let fn = validate.bind(undefined, settings) + expect(fn).to.throw(Error, 'poison.dressing'); + }); + }); + + describe("search settings", () => { + it('throws an error for invalid search engine name', () => { + let settings = { + search: { + default: 'google', + engines: { + 'google': 'https://google.com/search?q={}', + 'cherry pie': 'https://cherypie.com/search?q={}', + } + } + }; + let fn = validate.bind(undefined, settings) + expect(fn).to.throw(Error, 'cherry pie'); + }); + + it('throws an error for no {}-placeholder', () => { + let settings = { + search: { + default: 'google', + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search', + } + } + }; + let fn = validate.bind(undefined, settings) + expect(fn).to.throw(Error, 'yahoo'); + }); + + it('throws an error for no default engines', () => { + let settings = { + search: { + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?q={}', + } + } + }; + let fn = validate.bind(undefined, settings) + expect(fn).to.throw(Error, 'Default engine'); + }); + + it('throws an error for invalid default engine', () => { + let settings = { + search: { + default: 'twitter', + engines: { + 'google': 'https://google.com/search?q={}', + 'yahoo': 'https://search.yahoo.com/search?q={}', + } + } + }; + let fn = validate.bind(undefined, settings) + expect(fn).to.throw(Error, 'twitter'); + }); + }); +}); diff --git a/test/shared/validators/setting.test.js b/test/shared/validators/setting.test.js deleted file mode 100644 index 15d6a10..0000000 --- a/test/shared/validators/setting.test.js +++ /dev/null @@ -1,82 +0,0 @@ -import { expect } from "chai"; -import { validate } from 'shared/validators/setting'; - -describe("setting validator", () => { - describe("unknown top keys", () => { - it('throws an error for unknown settings', () => { - let settings = { keymaps: {}, poison: 123 }; - let fn = validate.bind(undefined, settings) - expect(fn).to.throw(Error, 'poison'); - }) - }); - - describe("keymaps settings", () => { - it('throws an error for unknown operation', () => { - let settings = { - keymaps: { - a: { 'type': 'scroll.home' }, - b: { 'type': 'poison.dressing' }, - } - }; - let fn = validate.bind(undefined, settings) - expect(fn).to.throw(Error, 'poison.dressing'); - }); - }); - - describe("search settings", () => { - it('throws an error for invalid search engine name', () => { - let settings = { - search: { - default: 'google', - engines: { - 'google': 'https://google.com/search?q={}', - 'cherry pie': 'https://cherypie.com/search?q={}', - } - } - }; - let fn = validate.bind(undefined, settings) - expect(fn).to.throw(Error, 'cherry pie'); - }); - - it('throws an error for no {}-placeholder', () => { - let settings = { - search: { - default: 'google', - engines: { - 'google': 'https://google.com/search?q={}', - 'yahoo': 'https://search.yahoo.com/search', - } - } - }; - let fn = validate.bind(undefined, settings) - expect(fn).to.throw(Error, 'yahoo'); - }); - - it('throws an error for no default engines', () => { - let settings = { - search: { - engines: { - 'google': 'https://google.com/search?q={}', - 'yahoo': 'https://search.yahoo.com/search?q={}', - } - } - }; - let fn = validate.bind(undefined, settings) - expect(fn).to.throw(Error, 'Default engine'); - }); - - it('throws an error for invalid default engine', () => { - let settings = { - search: { - default: 'twitter', - engines: { - 'google': 'https://google.com/search?q={}', - 'yahoo': 'https://search.yahoo.com/search?q={}', - } - } - }; - let fn = validate.bind(undefined, settings) - expect(fn).to.throw(Error, 'twitter'); - }); - }); -}); -- cgit v1.2.3 From e19f89f16248ed4bf28deaa9ab4b5204061df5eb Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 4 Jan 2018 20:34:33 +0900 Subject: add property to settings --- src/shared/settings/property-types.js | 6 ++++++ src/shared/settings/validator.js | 17 ++++++++++++++++- src/shared/settings/values.js | 18 ++++++++++++------ test/shared/settings/values.test.js | 29 +++++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 src/shared/settings/property-types.js (limited to 'test/shared') diff --git a/src/shared/settings/property-types.js b/src/shared/settings/property-types.js new file mode 100644 index 0000000..bcfa809 --- /dev/null +++ b/src/shared/settings/property-types.js @@ -0,0 +1,6 @@ +export default { + // TODO describe property types here + // mystr: 'string', + // mynum: 'number', + // mybool: 'boolean', +}; diff --git a/src/shared/settings/validator.js b/src/shared/settings/validator.js index 949ab29..6fadac7 100644 --- a/src/shared/settings/validator.js +++ b/src/shared/settings/validator.js @@ -1,6 +1,7 @@ import operations from 'shared/operations'; +import propertyTypes from './property-types'; -const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist']; +const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties']; const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => { return operations[key]; }); @@ -48,6 +49,17 @@ const validateSearch = (search) => { } }; +const validateProperties = (properties) => { + for (let name of Object.keys(properties)) { + if (!propertyTypes[name]) { + throw new Error(`Unknown property name: "${name}"`); + } + if (typeof properties[name] !== propertyTypes[name]) { + throw new Error(`Invalid type for property: "${name}"`); + } + } +}; + const validate = (settings) => { validateInvalidTopKeys(settings); if (settings.keymaps) { @@ -56,6 +68,9 @@ const validate = (settings) => { if (settings.search) { validateSearch(settings.search); } + if (settings.properties) { + validateProperties(settings.properties); + } }; export { validate }; diff --git a/src/shared/settings/values.js b/src/shared/settings/values.js index 4e55fa0..5027ba5 100644 --- a/src/shared/settings/values.js +++ b/src/shared/settings/values.js @@ -44,9 +44,12 @@ const valueFromForm = (form) => { } } - let blacklist = form.blacklist; - - return { keymaps, search, blacklist }; + return { + keymaps, + search, + blacklist: form.blacklist, + properties: form.properties + }; }; const jsonFromValue = (value) => { @@ -78,9 +81,12 @@ const formFromValue = (value, allowedOps) => { } } - let blacklist = value.blacklist; - - return { keymaps, search, blacklist }; + return { + keymaps, + search, + blacklist: value.blacklist, + properties: value.properties, + }; }; const jsonFromForm = (form) => { diff --git a/test/shared/settings/values.test.js b/test/shared/settings/values.test.js index 2632cd7..62cfb5f 100644 --- a/test/shared/settings/values.test.js +++ b/test/shared/settings/values.test.js @@ -7,13 +7,21 @@ describe("settings values", () => { let json = `{ "keymaps": { "0": {"type": "scroll.home"}}, "search": { "default": "google", "engines": { "google": "https://google.com/search?q={}" }}, - "blacklist": [ "*.slack.com"] + "blacklist": [ "*.slack.com"], + "properties": { + "mystr": "value", + "mynum": 123, + "mybool": true + } }`; let value = values.valueFromJson(json); expect(value.keymaps).to.deep.equal({ 0: {type: "scroll.home"}}); expect(value.search).to.deep.equal({ default: "google", engines: { google: "https://google.com/search?q={}"} }); expect(value.blacklist).to.deep.equal(["*.slack.com"]); + expect(value.properties).to.have.property('mystr', 'value'); + expect(value.properties).to.have.property('mynum', 123); + expect(value.properties).to.have.property('mybool', true); }); }); @@ -29,6 +37,11 @@ describe("settings values", () => { engines: [['google', 'https://google.com/search?q={}']], }, blacklist: ['*.slack.com'], + "properties": { + "mystr": "value", + "mynum": 123, + "mybool": true, + } }; let value = values.valueFromForm(form); @@ -37,6 +50,9 @@ describe("settings values", () => { expect(JSON.stringify(value.search)).to.deep.equal(JSON.stringify({ default: "google", engines: { google: "https://google.com/search?q={}"} })); expect(value.search).to.deep.equal({ default: "google", engines: { google: "https://google.com/search?q={}"} }); expect(value.blacklist).to.deep.equal(["*.slack.com"]); + expect(value.properties).to.have.property('mystr', 'value'); + expect(value.properties).to.have.property('mynum', 123); + expect(value.properties).to.have.property('mybool', true); }); it('convert from empty form', () => { @@ -45,6 +61,7 @@ describe("settings values", () => { expect(value).to.not.have.key('keymaps'); expect(value).to.not.have.key('search'); expect(value).to.not.have.key('blacklist'); + expect(value).to.not.have.key('properties'); }); it('override keymaps', () => { @@ -96,7 +113,12 @@ describe("settings values", () => { 0: { type: 'scroll.home' }, }, search: { default: 'google', engines: { google: 'https://google.com/search?q={}' }}, - blacklist: [ '*.slack.com'] + blacklist: [ '*.slack.com'], + properties: { + "mystr": "value", + "mynum": 123, + "mybool": true, + } }; let allowed = ['scroll.vertically?{"count":1}', 'scroll.home' ]; let form = values.formFromValue(value, allowed); @@ -109,6 +131,9 @@ describe("settings values", () => { expect(form.search).to.have.deep.property('engines', [['google', 'https://google.com/search?q={}']]); expect(form.blacklist).to.have.lengthOf(1); expect(form.blacklist).to.include('*.slack.com'); + expect(form.properties).to.have.property('mystr', 'value'); + expect(form.properties).to.have.property('mynum', 123); + expect(form.properties).to.have.property('mybool', true); }); }); }); -- cgit v1.2.3 From 3dbdcdba074f020f88bd4d1f5554d49549973ccd Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Mon, 8 Jan 2018 18:37:29 +0900 Subject: property parser --- src/shared/commands/properties.js | 31 ++++++++++++++++++++++++++ test/shared/commands/property.test.js | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/shared/commands/properties.js create mode 100644 test/shared/commands/property.test.js (limited to 'test/shared') diff --git a/src/shared/commands/properties.js b/src/shared/commands/properties.js new file mode 100644 index 0000000..8a3213d --- /dev/null +++ b/src/shared/commands/properties.js @@ -0,0 +1,31 @@ +const mustNumber = (v) => { + let num = Number(v); + if (isNaN(num)) { + throw new Error('Not number: ' + v); + } + return num; +}; + +const parseProperty = (word, types) => { + let [key, value] = word.split('='); + if (!value) { + 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 { parseProperty }; diff --git a/test/shared/commands/property.test.js b/test/shared/commands/property.test.js new file mode 100644 index 0000000..d949482 --- /dev/null +++ b/test/shared/commands/property.test.js @@ -0,0 +1,41 @@ +import { expect } from "chai"; +import { parseProperty } from 'shared/commands/properties'; + +describe("shared/commands/properties", () => { + describe("#parseProperty", () => { + it('parse set string', () => { + let [key, value] = parseProperty('encoding=utf-8', { encoding: 'string' }); + expect(key).to.equal('encoding'); + expect(value).to.equal('utf-8'); + }); + + it('parse set string', () => { + let [key, value] = parseProperty('history=50', { history: 'number' }); + expect(key).to.equal('history'); + expect(value).to.equal(50); + }); + + it('parse set boolean', () => { + let [key, value] = parseProperty('paste', { paste: 'boolean' }); + expect(key).to.equal('paste'); + expect(value).to.be.true; + + [key, value] = parseProperty('nopaste', { paste: 'boolean' }); + expect(key).to.equal('paste'); + expect(value).to.be.false; + }); + + it('throws error on unknown property', () => { + expect(() => parseProperty('charset=utf-8', {})).to.throw(Error, 'Unknown'); + expect(() => parseProperty('smoothscroll', {})).to.throw(Error, 'Unknown'); + expect(() => parseProperty('nosmoothscroll', {})).to.throw(Error, 'Unknown'); + }) + + it('throws error on invalid property', () => { + expect(() => parseProperty('charset=utf-8', { charset: 'number' })).to.throw(Error, 'Not number'); + expect(() => parseProperty('charset=utf-8', { charset: 'boolean' })).to.throw(Error, 'Invalid'); + expect(() => parseProperty('smoothscroll', { smoothscroll: 'string' })).to.throw(Error, 'Invalid'); + expect(() => parseProperty('smoothscroll', { smoothscroll: 'number' })).to.throw(Error, 'Invalid'); + }) + }); +}); -- cgit v1.2.3 From dda4e7475cdd092d00441c7cd0ceb194ee5dee3d Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 11 Jan 2018 20:07:25 +0900 Subject: move commands to background action --- src/background/actions/command.js | 62 ++++++++++++++++++++++++ src/background/components/background.js | 3 +- src/shared/commands/exec.js | 85 --------------------------------- src/shared/commands/index.js | 3 +- src/shared/commands/parsers.js | 59 +++++++++++++++++++++++ src/shared/commands/properties.js | 31 ------------ test/shared/commands/parsers.test.js | 78 ++++++++++++++++++++++++++++++ test/shared/commands/property.test.js | 41 ---------------- 8 files changed, 202 insertions(+), 160 deletions(-) create mode 100644 src/background/actions/command.js delete mode 100644 src/shared/commands/exec.js create mode 100644 src/shared/commands/parsers.js delete mode 100644 src/shared/commands/properties.js create mode 100644 test/shared/commands/parsers.test.js delete mode 100644 test/shared/commands/property.test.js (limited to 'test/shared') diff --git a/src/background/actions/command.js b/src/background/actions/command.js new file mode 100644 index 0000000..f11c61b --- /dev/null +++ b/src/background/actions/command.js @@ -0,0 +1,62 @@ +import * as tabs from 'background/tabs'; +import * as parsers from 'shared/commands/parsers'; + +const openCommand = (url) => { + return browser.tabs.query({ + active: true, currentWindow: true + }).then((gotTabs) => { + if (gotTabs.length > 0) { + return browser.tabs.update(gotTabs[0].id, { url: url }); + } + }); +}; + +const tabopenCommand = (url) => { + return browser.tabs.create({ url: url }); +}; + +const winopenCommand = (url) => { + return browser.windows.create({ url }); +}; + +const bufferCommand = (keywords) => { + if (keywords.length === 0) { + return Promise.resolve([]); + } + let keywordsStr = keywords.join(' '); + return browser.tabs.query({ + active: true, currentWindow: true + }).then((gotTabs) => { + if (gotTabs.length > 0) { + if (isNaN(keywordsStr)) { + return tabs.selectByKeyword(gotTabs[0], keywordsStr); + } + let index = parseInt(keywordsStr, 10) - 1; + return tabs.selectAt(index); + } + }); +}; + +const exec = (line, settings) => { + let [name, args] = parsers.parseCommandLine(line); + + switch (name) { + case 'o': + case 'open': + return openCommand(parsers.normalizeUrl(args, settings.search)); + case 't': + case 'tabopen': + return tabopenCommand(parsers.normalizeUrl(args, settings.search)); + case 'w': + case 'winopen': + return winopenCommand(parsers.normalizeUrl(args, settings.search)); + case 'b': + case 'buffer': + return bufferCommand(args); + case '': + return Promise.resolve(); + } + throw new Error(name + ' command is not defined'); +}; + +export { exec }; diff --git a/src/background/components/background.js b/src/background/components/background.js index 22c6693..19bf27f 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -1,5 +1,6 @@ import messages from 'shared/messages'; import * as operationActions from 'background/actions/operation'; +import * as commandActions from 'background/actions/command'; import * as settingActions from 'background/actions/setting'; import * as tabActions from 'background/actions/tab'; import * as commands from 'shared/commands'; @@ -35,7 +36,7 @@ export default class BackgroundComponent { return this.store.dispatch( tabActions.openToTab(message.url, sender.tab), sender); case messages.CONSOLE_ENTER_COMMAND: - return commands.exec(message.text, settings.value).catch((e) => { + return commandActions.exec(message.text, settings.value).catch((e) => { return browser.tabs.sendMessage(sender.tab.id, { type: messages.CONSOLE_SHOW_ERROR, text: e.message, diff --git a/src/shared/commands/exec.js b/src/shared/commands/exec.js deleted file mode 100644 index 7248827..0000000 --- a/src/shared/commands/exec.js +++ /dev/null @@ -1,85 +0,0 @@ -import * as tabs from 'background/tabs'; -import * as histories from 'background/histories'; - -const normalizeUrl = (args, searchConfig) => { - let concat = args.join(' '); - try { - return new URL(concat).href; - } catch (e) { - if (concat.includes('.') && !concat.includes(' ')) { - return 'http://' + concat; - } - let query = concat; - let template = searchConfig.engines[ - searchConfig.default - ]; - for (let key in searchConfig.engines) { - if (args[0] === key) { - query = args.slice(1).join(' '); - template = searchConfig.engines[key]; - } - } - return template.replace('{}', encodeURIComponent(query)); - } -}; - -const openCommand = (url) => { - return browser.tabs.query({ - active: true, currentWindow: true - }).then((gotTabs) => { - if (gotTabs.length > 0) { - return browser.tabs.update(gotTabs[0].id, { url: url }); - } - }); -}; - -const tabopenCommand = (url) => { - return browser.tabs.create({ url: url }); -}; - -const winopenCommand = (url) => { - return browser.windows.create({ url }); -}; - -const bufferCommand = (keywords) => { - if (keywords.length === 0) { - return Promise.resolve([]); - } - let keywordsStr = keywords.join(' '); - return browser.tabs.query({ - active: true, currentWindow: true - }).then((gotTabs) => { - if (gotTabs.length > 0) { - if (isNaN(keywordsStr)) { - return tabs.selectByKeyword(gotTabs[0], keywordsStr); - } - let index = parseInt(keywordsStr, 10) - 1; - return tabs.selectAt(index); - } - }); -}; - -const exec = (line, settings) => { - let words = line.trim().split(/ +/); - let name = words.shift(); - - switch (name) { - case 'o': - case 'open': - return openCommand(normalizeUrl(words, settings.search)); - case 't': - case 'tabopen': - return tabopenCommand(normalizeUrl(words, settings.search)); - case 'w': - case 'winopen': - return winopenCommand(normalizeUrl(words, settings.search)); - case 'b': - case 'buffer': - return bufferCommand(words); - case '': - return Promise.resolve(); - } - throw new Error(name + ' command is not defined'); -}; - -export default exec; diff --git a/src/shared/commands/index.js b/src/shared/commands/index.js index c2cea3e..78cb4df 100644 --- a/src/shared/commands/index.js +++ b/src/shared/commands/index.js @@ -1,4 +1,3 @@ -import exec from './exec'; import complete from './complete'; -export { exec, complete }; +export { complete }; diff --git a/src/shared/commands/parsers.js b/src/shared/commands/parsers.js new file mode 100644 index 0000000..af51338 --- /dev/null +++ b/src/shared/commands/parsers.js @@ -0,0 +1,59 @@ +const normalizeUrl = (args, searchConfig) => { + let concat = args.join(' '); + try { + return new URL(concat).href; + } catch (e) { + if (concat.includes('.') && !concat.includes(' ')) { + return 'http://' + concat; + } + let query = concat; + let template = searchConfig.engines[ + searchConfig.default + ]; + for (let key in searchConfig.engines) { + if (args[0] === key) { + query = args.slice(1).join(' '); + template = searchConfig.engines[key]; + } + } + 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) { + 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]; + } +}; + +const parseCommandLine = (line) => { + let words = line.trim().split(/ +/); + let name = words.shift(); + return [name, words]; +}; + +export { normalizeUrl, parseCommandLine, parseSetOption }; diff --git a/src/shared/commands/properties.js b/src/shared/commands/properties.js deleted file mode 100644 index 8a3213d..0000000 --- a/src/shared/commands/properties.js +++ /dev/null @@ -1,31 +0,0 @@ -const mustNumber = (v) => { - let num = Number(v); - if (isNaN(num)) { - throw new Error('Not number: ' + v); - } - return num; -}; - -const parseProperty = (word, types) => { - let [key, value] = word.split('='); - if (!value) { - 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 { parseProperty }; diff --git a/test/shared/commands/parsers.test.js b/test/shared/commands/parsers.test.js new file mode 100644 index 0000000..200323c --- /dev/null +++ b/test/shared/commands/parsers.test.js @@ -0,0 +1,78 @@ +import { expect } from "chai"; +import * as parsers from 'shared/commands/parsers'; + +describe("shared/commands/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 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('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'); + }); + }); + + describe('#parseCommandLine', () => { + it('parse command line as name and args', () => { + expect(parsers.parseCommandLine('open google apple')).to.deep.equal(['open', ['google', 'apple']]); + expect(parsers.parseCommandLine(' open google apple ')).to.deep.equal(['open', ['google', 'apple']]); + expect(parsers.parseCommandLine('')).to.deep.equal(['', []]); + expect(parsers.parseCommandLine(' ')).to.deep.equal(['', []]); + expect(parsers.parseCommandLine('exit')).to.deep.equal(['exit', []]); + expect(parsers.parseCommandLine(' exit ')).to.deep.equal(['exit', []]); + }); + }); +}); diff --git a/test/shared/commands/property.test.js b/test/shared/commands/property.test.js deleted file mode 100644 index d949482..0000000 --- a/test/shared/commands/property.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import { expect } from "chai"; -import { parseProperty } from 'shared/commands/properties'; - -describe("shared/commands/properties", () => { - describe("#parseProperty", () => { - it('parse set string', () => { - let [key, value] = parseProperty('encoding=utf-8', { encoding: 'string' }); - expect(key).to.equal('encoding'); - expect(value).to.equal('utf-8'); - }); - - it('parse set string', () => { - let [key, value] = parseProperty('history=50', { history: 'number' }); - expect(key).to.equal('history'); - expect(value).to.equal(50); - }); - - it('parse set boolean', () => { - let [key, value] = parseProperty('paste', { paste: 'boolean' }); - expect(key).to.equal('paste'); - expect(value).to.be.true; - - [key, value] = parseProperty('nopaste', { paste: 'boolean' }); - expect(key).to.equal('paste'); - expect(value).to.be.false; - }); - - it('throws error on unknown property', () => { - expect(() => parseProperty('charset=utf-8', {})).to.throw(Error, 'Unknown'); - expect(() => parseProperty('smoothscroll', {})).to.throw(Error, 'Unknown'); - expect(() => parseProperty('nosmoothscroll', {})).to.throw(Error, 'Unknown'); - }) - - it('throws error on invalid property', () => { - expect(() => parseProperty('charset=utf-8', { charset: 'number' })).to.throw(Error, 'Not number'); - expect(() => parseProperty('charset=utf-8', { charset: 'boolean' })).to.throw(Error, 'Invalid'); - expect(() => parseProperty('smoothscroll', { smoothscroll: 'string' })).to.throw(Error, 'Invalid'); - expect(() => parseProperty('smoothscroll', { smoothscroll: 'number' })).to.throw(Error, 'Invalid'); - }) - }); -}); -- cgit v1.2.3 From befcff973aa0fd9b0f3a73932a39d36e9eb85bf2 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 11 Jan 2018 20:14:56 +0900 Subject: fix property parser --- src/shared/commands/parsers.js | 2 +- test/shared/commands/parsers.test.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'test/shared') diff --git a/src/shared/commands/parsers.js b/src/shared/commands/parsers.js index af51338..fb37d2a 100644 --- a/src/shared/commands/parsers.js +++ b/src/shared/commands/parsers.js @@ -30,7 +30,7 @@ const mustNumber = (v) => { const parseSetOption = (word, types) => { let [key, value] = word.split('='); - if (!value) { + if (value === undefined) { value = !key.startsWith('no'); key = value ? key : key.slice(2); } diff --git a/test/shared/commands/parsers.test.js b/test/shared/commands/parsers.test.js index 200323c..0a1960c 100644 --- a/test/shared/commands/parsers.test.js +++ b/test/shared/commands/parsers.test.js @@ -9,6 +9,12 @@ describe("shared/commands/parsers", () => { 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'); @@ -34,6 +40,7 @@ describe("shared/commands/parsers", () => { 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'); }) -- cgit v1.2.3