commit
f5dfdb0bd7
25 changed files with 691 additions and 201 deletions
@ -0,0 +1,79 @@ |
||||
import actions from '../actions'; |
||||
import * as tabs from 'background/tabs'; |
||||
import * as parsers from 'shared/commands/parsers'; |
||||
import * as properties from 'shared/settings/properties'; |
||||
|
||||
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 setCommand = (args) => { |
||||
if (!args[0]) { |
||||
return Promise.resolve(); |
||||
} |
||||
|
||||
let [name, value] = parsers.parseSetOption(args[0], properties.types); |
||||
return { |
||||
type: actions.SETTING_SET_PROPERTY, |
||||
name, |
||||
value |
||||
}; |
||||
}; |
||||
|
||||
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 'set': |
||||
return setCommand(args); |
||||
case '': |
||||
return Promise.resolve(); |
||||
} |
||||
throw new Error(name + ' command is not defined'); |
||||
}; |
||||
|
||||
export { exec }; |
@ -0,0 +1,5 @@ |
||||
export default { |
||||
// Settings
|
||||
SETTING_SET_SETTINGS: 'setting.set.settings', |
||||
SETTING_SET_PROPERTY: 'setting.set.property', |
||||
}; |
@ -0,0 +1,21 @@ |
||||
import actions from '../actions'; |
||||
import * as settingsStorage from 'shared/settings/storage'; |
||||
|
||||
const load = () => { |
||||
return settingsStorage.loadValue().then((value) => { |
||||
return { |
||||
type: actions.SETTING_SET_SETTINGS, |
||||
value, |
||||
}; |
||||
}); |
||||
}; |
||||
|
||||
const setProperty = (name, value) => { |
||||
return { |
||||
type: actions.SETTING_SET_PROPERTY, |
||||
name, |
||||
value, |
||||
}; |
||||
}; |
||||
|
||||
export { load, setProperty }; |
@ -0,0 +1,24 @@ |
||||
import actions from 'background/actions'; |
||||
|
||||
const defaultState = { |
||||
value: {}, |
||||
}; |
||||
|
||||
export default function reducer(state = defaultState, action = {}) { |
||||
switch (action.type) { |
||||
case actions.SETTING_SET_SETTINGS: |
||||
return { |
||||
value: action.value, |
||||
}; |
||||
case actions.SETTING_SET_PROPERTY: |
||||
return { |
||||
value: Object.assign({}, state.value, { |
||||
properties: Object.assign({}, state.value.properties, |
||||
{ [action.name]: action.value }) |
||||
}) |
||||
}; |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,60 @@ |
||||
import './properties-form.scss'; |
||||
import { h, Component } from 'preact'; |
||||
|
||||
class PropertiesForm extends Component { |
||||
|
||||
render() { |
||||
let types = this.props.types; |
||||
let value = this.props.value; |
||||
if (!value) { |
||||
value = {}; |
||||
} |
||||
|
||||
return <div className='form-properties-form'> |
||||
{ |
||||
Object.keys(types).map((name) => { |
||||
let type = types[name]; |
||||
let inputType = null; |
||||
if (type === 'string') { |
||||
inputType = 'text'; |
||||
} else if (type === 'number') { |
||||
inputType = 'number'; |
||||
} else if (type === 'boolean') { |
||||
inputType = 'checkbox'; |
||||
} |
||||
return <div key={name} className='form-properties-form-row'> |
||||
<label> |
||||
<span className='column-name'>{name}</span> |
||||
<input type={inputType} name={name} |
||||
className='column-input' |
||||
value={value[name] ? value[name] : ''} |
||||
onChange={this.bindValue.bind(this)} |
||||
checked={value[name]} |
||||
/> |
||||
</label> |
||||
</div>; |
||||
}) |
||||
} |
||||
</div>; |
||||
} |
||||
|
||||
bindValue(e) { |
||||
if (!this.props.onChange) { |
||||
return; |
||||
} |
||||
|
||||
let name = e.target.name; |
||||
let next = Object.assign({}, this.props.value); |
||||
if (e.target.type.toLowerCase() === 'checkbox') { |
||||
next[name] = e.target.checked; |
||||
} else if (e.target.type.toLowerCase() === 'number') { |
||||
next[name] = Number(e.target.value); |
||||
} else { |
||||
next[name] = e.target.value; |
||||
} |
||||
|
||||
this.props.onChange(next); |
||||
} |
||||
} |
||||
|
||||
export default PropertiesForm; |
@ -0,0 +1,12 @@ |
||||
.form-properties-form { |
||||
&-row { |
||||
.column-name { |
||||
display: inline-block; |
||||
min-width: 5rem; |
||||
font-weight: bold; |
||||
} |
||||
.column-input { |
||||
line-height: 2.2rem; |
||||
} |
||||
} |
||||
} |
@ -1,169 +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 getOpenCompletions = (command, keywords, searchConfig) => { |
||||
return histories.getCompletions(keywords).then((pages) => { |
||||
let historyItems = pages.map((page) => { |
||||
return { |
||||
caption: page.title, |
||||
content: command + ' ' + page.url, |
||||
url: page.url |
||||
}; |
||||
}); |
||||
let engineNames = Object.keys(searchConfig.engines); |
||||
let engineItems = engineNames.filter(name => name.startsWith(keywords)) |
||||
.map(name => ({ |
||||
caption: name, |
||||
content: command + ' ' + name |
||||
})); |
||||
|
||||
let completions = []; |
||||
if (engineItems.length > 0) { |
||||
completions.push({ |
||||
name: 'Search Engines', |
||||
items: engineItems |
||||
}); |
||||
} |
||||
if (historyItems.length > 0) { |
||||
completions.push({ |
||||
name: 'History', |
||||
items: historyItems |
||||
}); |
||||
} |
||||
return completions; |
||||
}); |
||||
}; |
||||
|
||||
const doCommand = (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'); |
||||
}; |
||||
|
||||
const getCompletions = (line, settings) => { |
||||
let typedWords = line.trim().split(/ +/); |
||||
let typing = ''; |
||||
if (!line.endsWith(' ')) { |
||||
typing = typedWords.pop(); |
||||
} |
||||
|
||||
if (typedWords.length === 0) { |
||||
return Promise.resolve([]); |
||||
} |
||||
let name = typedWords.shift(); |
||||
let keywords = typedWords.concat(typing).join(' '); |
||||
|
||||
switch (name) { |
||||
case 'o': |
||||
case 'open': |
||||
case 't': |
||||
case 'tabopen': |
||||
case 'w': |
||||
case 'winopen': |
||||
return getOpenCompletions(name, keywords, settings.search); |
||||
case 'b': |
||||
case 'buffer': |
||||
return tabs.getCompletions(keywords).then((gotTabs) => { |
||||
let items = gotTabs.map((tab) => { |
||||
return { |
||||
caption: tab.title, |
||||
content: name + ' ' + tab.title, |
||||
url: tab.url, |
||||
icon: tab.favIconUrl |
||||
}; |
||||
}); |
||||
return [ |
||||
{ |
||||
name: 'Buffers', |
||||
items: items |
||||
} |
||||
]; |
||||
}); |
||||
} |
||||
return Promise.resolve([]); |
||||
}; |
||||
|
||||
const exec = (line, settings) => { |
||||
return doCommand(line, settings); |
||||
}; |
||||
|
||||
const complete = (line, settings) => { |
||||
return getCompletions(line, settings); |
||||
}; |
||||
|
||||
export { exec, complete }; |
@ -0,0 +1,84 @@ |
||||
import * as tabs from 'background/tabs'; |
||||
import * as histories from 'background/histories'; |
||||
|
||||
const getOpenCompletions = (command, keywords, searchConfig) => { |
||||
return histories.getCompletions(keywords).then((pages) => { |
||||
let historyItems = pages.map((page) => { |
||||
return { |
||||
caption: page.title, |
||||
content: command + ' ' + page.url, |
||||
url: page.url |
||||
}; |
||||
}); |
||||
let engineNames = Object.keys(searchConfig.engines); |
||||
let engineItems = engineNames.filter(name => name.startsWith(keywords)) |
||||
.map(name => ({ |
||||
caption: name, |
||||
content: command + ' ' + name |
||||
})); |
||||
|
||||
let completions = []; |
||||
if (engineItems.length > 0) { |
||||
completions.push({ |
||||
name: 'Search Engines', |
||||
items: engineItems |
||||
}); |
||||
} |
||||
if (historyItems.length > 0) { |
||||
completions.push({ |
||||
name: 'History', |
||||
items: historyItems |
||||
}); |
||||
} |
||||
return completions; |
||||
}); |
||||
}; |
||||
|
||||
const getCompletions = (line, settings) => { |
||||
let typedWords = line.trim().split(/ +/); |
||||
let typing = ''; |
||||
if (!line.endsWith(' ')) { |
||||
typing = typedWords.pop(); |
||||
} |
||||
|
||||
if (typedWords.length === 0) { |
||||
return Promise.resolve([]); |
||||
} |
||||
let name = typedWords.shift(); |
||||
let keywords = typedWords.concat(typing).join(' '); |
||||
|
||||
switch (name) { |
||||
case 'o': |
||||
case 'open': |
||||
case 't': |
||||
case 'tabopen': |
||||
case 'w': |
||||
case 'winopen': |
||||
return getOpenCompletions(name, keywords, settings.search); |
||||
case 'b': |
||||
case 'buffer': |
||||
return tabs.getCompletions(keywords).then((gotTabs) => { |
||||
let items = gotTabs.map((tab) => { |
||||
return { |
||||
caption: tab.title, |
||||
content: name + ' ' + tab.title, |
||||
url: tab.url, |
||||
icon: tab.favIconUrl |
||||
}; |
||||
}); |
||||
return [ |
||||
{ |
||||
name: 'Buffers', |
||||
items: items |
||||
} |
||||
]; |
||||
}); |
||||
} |
||||
return Promise.resolve([]); |
||||
}; |
||||
|
||||
const complete = (line, settings) => { |
||||
return getCompletions(line, settings); |
||||
}; |
||||
|
||||
export default complete; |
@ -0,0 +1,3 @@ |
||||
import complete from './complete'; |
||||
|
||||
export { complete }; |
@ -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 === 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]; |
||||
} |
||||
}; |
||||
|
||||
const parseCommandLine = (line) => { |
||||
let words = line.trim().split(/ +/); |
||||
let name = words.shift(); |
||||
return [name, words]; |
||||
}; |
||||
|
||||
export { normalizeUrl, parseCommandLine, parseSetOption }; |
@ -0,0 +1,15 @@ |
||||
const types = { |
||||
// TODO describe property types here
|
||||
// mystr: 'string',
|
||||
// mynum: 'number',
|
||||
// mybool: 'boolean',
|
||||
}; |
||||
|
||||
const defaults = { |
||||
// TODO describe property defaults values
|
||||
// mystr: 'hello',
|
||||
// mynum: 123,
|
||||
// mybool: true,
|
||||
}; |
||||
|
||||
export { types, defaults }; |
@ -0,0 +1,31 @@ |
||||
import DefaultSettings from './default'; |
||||
import * as settingsValues from './values'; |
||||
|
||||
const loadRaw = () => { |
||||
return browser.storage.local.get('settings').then(({ settings }) => { |
||||
if (!settings) { |
||||
return DefaultSettings; |
||||
} |
||||
return Object.assign({}, DefaultSettings, settings); |
||||
}); |
||||
}; |
||||
|
||||
const loadValue = () => { |
||||
return loadRaw().then((settings) => { |
||||
let value = JSON.parse(DefaultSettings.json); |
||||
if (settings.source === 'json') { |
||||
value = settingsValues.valueFromJson(settings.json); |
||||
} else if (settings.source === 'form') { |
||||
value = settingsValues.valueFromForm(settings.form); |
||||
} |
||||
return value; |
||||
}); |
||||
}; |
||||
|
||||
const save = (settings) => { |
||||
return browser.storage.local.set({ |
||||
settings, |
||||
}); |
||||
}; |
||||
|
||||
export { loadRaw, loadValue, save }; |
@ -0,0 +1,37 @@ |
||||
import { expect } from "chai"; |
||||
import actions from 'background/actions'; |
||||
import settingReducer from 'background/reducers/setting'; |
||||
|
||||
describe("setting reducer", () => { |
||||
it('return the initial state', () => { |
||||
let state = settingReducer(undefined, {}); |
||||
expect(state).to.have.deep.property('value', {}); |
||||
}); |
||||
|
||||
it('return next state for SETTING_SET_SETTINGS', () => { |
||||
let action = { |
||||
type: actions.SETTING_SET_SETTINGS, |
||||
value: { key: 123 }, |
||||
}; |
||||
let state = settingReducer(undefined, action); |
||||
expect(state).to.have.deep.property('value', { key: 123 }); |
||||
}); |
||||
|
||||
it('return next state for SETTING_SET_PROPERTY', () => { |
||||
let state = { |
||||
value: { |
||||
properties: { smoothscroll: true } |
||||
} |
||||
} |
||||
let action = { |
||||
type: actions.SETTING_SET_PROPERTY, |
||||
name: 'encoding', |
||||
value: 'utf-8', |
||||
}; |
||||
state = settingReducer(state, action); |
||||
|
||||
console.log(state); |
||||
expect(state.value.properties).to.have.property('smoothscroll', true); |
||||
expect(state.value.properties).to.have.property('encoding', 'utf-8'); |
||||
}); |
||||
}); |
@ -0,0 +1,86 @@ |
||||
import { expect } from 'chai'; |
||||
import { h, render } from 'preact'; |
||||
import PropertiesForm from 'settings/components/form/properties-form' |
||||
|
||||
describe("settings/form/PropertiesForm", () => { |
||||
beforeEach(() => { |
||||
document.body.innerHTML = ''; |
||||
}); |
||||
|
||||
describe('render', () => { |
||||
it('renders PropertiesForm', () => { |
||||
let types = { |
||||
mystr: 'string', |
||||
mynum: 'number', |
||||
mybool: 'boolean', |
||||
empty: 'string', |
||||
} |
||||
let value = { |
||||
mystr: 'abc', |
||||
mynum: 123, |
||||
mybool: true, |
||||
}; |
||||
render(<PropertiesForm types={types} value={value} />, document.body); |
||||
|
||||
let strInput = document.querySelector('input[name=mystr]'); |
||||
let numInput = document.querySelector('input[name=mynum]'); |
||||
let boolInput = document.querySelector('input[name=mybool]'); |
||||
let emptyInput = document.querySelector('input[name=empty]'); |
||||
|
||||
expect(strInput.type).to.equals('text'); |
||||
expect(strInput.value).to.equal('abc'); |
||||
expect(numInput.type).to.equals('number'); |
||||
expect(numInput.value).to.equal('123'); |
||||
expect(boolInput.type).to.equals('checkbox'); |
||||
expect(boolInput.checked).to.be.true; |
||||
expect(emptyInput.type).to.equals('text'); |
||||
expect(emptyInput.value).to.be.empty; |
||||
}); |
||||
}); |
||||
|
||||
describe('onChange', () => { |
||||
it('invokes onChange event on text changed', (done) => { |
||||
render(<PropertiesForm |
||||
types={{ 'myvalue': 'string' }} |
||||
value={{ 'myvalue': 'abc' }} |
||||
onChange={value => { |
||||
expect(value).to.have.property('myvalue', 'abcd'); |
||||
done(); |
||||
}} |
||||
/>, document.body); |
||||
|
||||
let input = document.querySelector('input[name=myvalue]'); |
||||
input.value = 'abcd' |
||||
input.dispatchEvent(new Event('change')) |
||||
}); |
||||
|
||||
it('invokes onChange event on number changeed', (done) => { |
||||
render(<PropertiesForm |
||||
types={{ 'myvalue': 'number' }} |
||||
value={{ '': 123 }} |
||||
onChange={value => { |
||||
expect(value).to.have.property('myvalue', 1234); |
||||
done(); |
||||
}} |
||||
/>, document.body); |
||||
|
||||
let input = document.querySelector('input[name=myvalue]'); |
||||
input.value = '1234' |
||||
input.dispatchEvent(new Event('change')) |
||||
}); |
||||
|
||||
it('invokes onChange event on checkbox changed', (done) => { |
||||
render(<PropertiesForm |
||||
types={{ 'myvalue': 'boolean' }} |
||||
value={{ 'myvalue': false }} |
||||
onChange={value => { |
||||
expect(value).to.have.property('myvalue', true); |
||||
done(); |
||||
}} |
||||
/>, document.body); |
||||
|
||||
let input = document.querySelector('input[name=myvalue]'); |
||||
input.click(); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,85 @@ |
||||
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 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'); |
||||
}); |
||||
}); |
||||
|
||||
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', []]); |
||||
}); |
||||
}); |
||||
}); |
@ -1,5 +1,5 @@ |
||||
import { expect } from "chai"; |
||||
import { validate } from 'shared/validators/setting'; |
||||
import { validate } from 'shared/settings/validator'; |
||||
|
||||
describe("setting validator", () => { |
||||
describe("unknown top keys", () => { |
Reference in new issue