diff options
-rw-r--r-- | mitmproxy/tools/web/app.py | 12 | ||||
-rw-r--r-- | mitmproxy/tools/web/master.py | 1 | ||||
-rw-r--r-- | test/mitmproxy/tools/web/test_app.py | 4 | ||||
-rw-r--r-- | web/src/js/__tests__/components/Modal/OptionModalSpec.js | 54 | ||||
-rw-r--r-- | web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap | 35 | ||||
-rw-r--r-- | web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap | 61 | ||||
-rw-r--r-- | web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap | 2 | ||||
-rw-r--r-- | web/src/js/__tests__/ducks/optionsSpec.js | 15 | ||||
-rw-r--r-- | web/src/js/components/Modal/Option.jsx | 2 | ||||
-rw-r--r-- | web/src/js/components/Modal/OptionModal.jsx | 33 | ||||
-rw-r--r-- | web/src/js/ducks/options.js | 4 |
11 files changed, 217 insertions, 6 deletions
diff --git a/mitmproxy/tools/web/app.py b/mitmproxy/tools/web/app.py index c6fb2ef6..9a447fe7 100644 --- a/mitmproxy/tools/web/app.py +++ b/mitmproxy/tools/web/app.py @@ -18,6 +18,7 @@ from mitmproxy import io from mitmproxy import log from mitmproxy import version from mitmproxy import optmanager +from mitmproxy.tools.cmdline import CONFIG_PATH import mitmproxy.tools.web.master # noqa @@ -451,6 +452,14 @@ class Options(RequestHandler): raise APIError(400, "{}".format(err)) +class SaveOptions(RequestHandler): + def post(self): + try: + optmanager.save(self.master.options, CONFIG_PATH, True) + except Exception as err: + raise APIError(400, "{}".format(err)) + + class Application(tornado.web.Application): def __init__(self, master, debug): self.master = master @@ -475,7 +484,8 @@ class Application(tornado.web.Application): FlowContentView), (r"/settings", Settings), (r"/clear", ClearAll), - (r"/options", Options) + (r"/options", Options), + (r"/options/save", SaveOptions) ] settings = dict( template_path=os.path.join(os.path.dirname(__file__), "templates"), diff --git a/mitmproxy/tools/web/master.py b/mitmproxy/tools/web/master.py index 8c2433ec..dc5b2627 100644 --- a/mitmproxy/tools/web/master.py +++ b/mitmproxy/tools/web/master.py @@ -125,7 +125,6 @@ class WebMaster(master.Master): "No web browser found. Please open a browser and point it to {}".format(web_url), "info" ) - try: iol.start() except KeyboardInterrupt: diff --git a/test/mitmproxy/tools/web/test_app.py b/test/mitmproxy/tools/web/test_app.py index bb439b34..4d290284 100644 --- a/test/mitmproxy/tools/web/test_app.py +++ b/test/mitmproxy/tools/web/test_app.py @@ -263,6 +263,9 @@ class TestApp(tornado.testing.AsyncHTTPTestCase): assert self.put_json("/options", {"wtf": True}).code == 400 assert self.put_json("/options", {"anticache": "foo"}).code == 400 + def test_option_save(self): + assert self.fetch("/options/save", method="POST").code == 200 + def test_err(self): with mock.patch("mitmproxy.tools.web.app.IndexHandler.get") as f: f.side_effect = RuntimeError @@ -279,7 +282,6 @@ class TestApp(tornado.testing.AsyncHTTPTestCase): r2 = yield ws_client.read_message() j1 = _json.loads(r1) j2 = _json.loads(r2) - print(j1) response = dict() response[j1['resource']] = j1 response[j2['resource']] = j2 diff --git a/web/src/js/__tests__/components/Modal/OptionModalSpec.js b/web/src/js/__tests__/components/Modal/OptionModalSpec.js new file mode 100644 index 00000000..dd4e70a2 --- /dev/null +++ b/web/src/js/__tests__/components/Modal/OptionModalSpec.js @@ -0,0 +1,54 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import { PureOptionDefault } from '../../../components/Modal/OptionModal' + +describe('PureOptionDefault Component', () => { + + it('should return null when the value is default', () => { + let pureOptionDefault = renderer.create( + <PureOptionDefault value="foo" defaultVal="foo"/> + ), + tree = pureOptionDefault.toJSON() + expect(tree).toMatchSnapshot() + }) + + it('should handle boolean type', () => { + let pureOptionDefault = renderer.create( + <PureOptionDefault value={true} defaultVal={false}/> + ), + tree = pureOptionDefault.toJSON() + expect(tree).toMatchSnapshot() + }) + + it('should handle array', () => { + let a = [""], b = [], c = ['c'], + pureOptionDefault = renderer.create( + <PureOptionDefault value={a} defaultVal={b}/> + ), + tree = pureOptionDefault.toJSON() + expect(tree).toMatchSnapshot() + + pureOptionDefault = renderer.create( + <PureOptionDefault value={a} defaultVal={c}/> + ) + tree = pureOptionDefault.toJSON() + expect(tree).toMatchSnapshot() + }) + + it('should handle string', () => { + let pureOptionDefault = renderer.create( + <PureOptionDefault value="foo" defaultVal=""/> + ), + tree = pureOptionDefault.toJSON() + expect(tree).toMatchSnapshot() + }) + + it('should handle null value', () => { + let pureOptionDefault = renderer.create( + <PureOptionDefault value="foo" defaultVal={null}/> + ), + tree = pureOptionDefault.toJSON() + expect(tree).toMatchSnapshot() + }) + +}) diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap index bfd855bd..8d9271f1 100644 --- a/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap +++ b/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap @@ -117,7 +117,7 @@ exports[`Modal Component should render correctly 2`] = ` name="choiceOption" onChange={[Function]} onKeyDown={[Function]} - selected="b" + value="b" > <option value="a" @@ -136,6 +136,17 @@ exports[`Modal Component should render correctly 2`] = ` </option> </select> </div> + <div + className="small" + > + Default: + <strong> + + a + + </strong> + + </div> </div> </div> <div @@ -170,6 +181,17 @@ exports[`Modal Component should render correctly 2`] = ` value={1} /> </div> + <div + className="small" + > + Default: + <strong> + + 0 + + </strong> + + </div> </div> </div> <div @@ -207,6 +229,17 @@ exports[`Modal Component should render correctly 2`] = ` <div className="small text-danger" /> + <div + className="small" + > + Default: + <strong> + + null + + </strong> + + </div> </div> </div> </div> diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap new file mode 100644 index 00000000..68f1c9fc --- /dev/null +++ b/web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PureOptionDefault Component should handle array 1`] = `null`; + +exports[`PureOptionDefault Component should handle array 2`] = ` +<div + className="small" +> + Default: + <strong> + + [ ] + + </strong> + +</div> +`; + +exports[`PureOptionDefault Component should handle boolean type 1`] = ` +<div + className="small" +> + Default: + <strong> + + false + + </strong> + +</div> +`; + +exports[`PureOptionDefault Component should handle null value 1`] = ` +<div + className="small" +> + Default: + <strong> + + null + + </strong> + +</div> +`; + +exports[`PureOptionDefault Component should handle string 1`] = ` +<div + className="small" +> + Default: + <strong> + + "" + + </strong> + +</div> +`; + +exports[`PureOptionDefault Component should return null when the value is default 1`] = `null`; diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap index 514e0eb5..257bddce 100644 --- a/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap +++ b/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap @@ -18,7 +18,7 @@ exports[`BooleanOption Component should render correctly 1`] = ` exports[`ChoiceOption Component should render correctly 1`] = ` <select onChange={[Function]} - selected="a" + value="a" > <option value="a" diff --git a/web/src/js/__tests__/ducks/optionsSpec.js b/web/src/js/__tests__/ducks/optionsSpec.js index 0925fcc1..9178c14e 100644 --- a/web/src/js/__tests__/ducks/optionsSpec.js +++ b/web/src/js/__tests__/ducks/optionsSpec.js @@ -49,3 +49,18 @@ describe('sendUpdate', () => { ]) }) }) + +describe('save', () => { + + it('should dump options', () => { + global.fetch = jest.fn() + store.dispatch(OptionsActions.save()) + expect(fetch).toBeCalledWith( + '/options/save?_xsrf=undefined', + { + credentials: "same-origin", + method: "POST" + } + ) + }) +}) diff --git a/web/src/js/components/Modal/Option.jsx b/web/src/js/components/Modal/Option.jsx index 58b863d1..38e2f239 100644 --- a/web/src/js/components/Modal/Option.jsx +++ b/web/src/js/components/Modal/Option.jsx @@ -74,7 +74,7 @@ export function ChoicesOption({ value, onChange, choices, ...props }) { return ( <select onChange={(e) => onChange(e.target.value)} - selected={value} + value={value} {...props} > { choices.map( diff --git a/web/src/js/components/Modal/OptionModal.jsx b/web/src/js/components/Modal/OptionModal.jsx index 5741ee8c..82ef8350 100644 --- a/web/src/js/components/Modal/OptionModal.jsx +++ b/web/src/js/components/Modal/OptionModal.jsx @@ -1,7 +1,9 @@ import React, { Component } from "react" import { connect } from "react-redux" import * as modalAction from "../../ducks/ui/modal" +import * as optionAction from "../../ducks/options" import Option from "./Option" +import _ from "lodash" function PureOptionHelp({help}){ return <div className="help-block small">{help}</div>; @@ -18,6 +20,31 @@ const OptionError = connect((state, {name}) => ({ error: state.ui.optionsEditor[name] && state.ui.optionsEditor[name].error }))(PureOptionError); +export function PureOptionDefault({value, defaultVal}){ + if( value === defaultVal ) { + return null + } else { + if (typeof(defaultVal) === 'boolean') { + defaultVal = defaultVal ? 'true' : 'false' + } else if (Array.isArray(defaultVal)){ + if (_.isEmpty(_.compact(value)) && // filter the empty string in array + _.isEmpty(defaultVal)){ + return null + } + defaultVal = '[ ]' + } else if (defaultVal === ''){ + defaultVal = '\"\"' + } else if (defaultVal === null){ + defaultVal = 'null' + } + return <div className="small">Default: <strong> {defaultVal} </strong> </div> + } +} +const OptionDefault = connect((state, {name}) => ({ + value: state.options[name].value, + defaultVal: state.options[name].default +}))(PureOptionDefault) + class PureOptionModal extends Component { constructor(props, context) { @@ -25,6 +52,10 @@ class PureOptionModal extends Component { this.state = { title: 'Options' } } + componentWillUnmount(){ + this.props.save() + } + render() { const { hideModal, options } = this.props const { title } = this.state @@ -53,6 +84,7 @@ class PureOptionModal extends Component { <div className="col-xs-6"> <Option name={name}/> <OptionError name={name}/> + <OptionDefault name={name}/> </div> </div> ) @@ -73,5 +105,6 @@ export default connect( }), { hideModal: modalAction.hideModal, + save: optionAction.save, } )(PureOptionModal) diff --git a/web/src/js/ducks/options.js b/web/src/js/ducks/options.js index 06144a3c..0da0fb8c 100644 --- a/web/src/js/ducks/options.js +++ b/web/src/js/ducks/options.js @@ -44,3 +44,7 @@ export function update(option, value) { sendUpdate(option, value, dispatch); } } + +export function save() { + return dispatch => fetchApi('/options/save', { method: 'POST' }) +} |