aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@corte.si>2017-05-03 15:46:56 +1200
committerGitHub <noreply@github.com>2017-05-03 15:46:56 +1200
commit822797c7e0588695ecf318b7e7c424eda393405c (patch)
tree42fb406c2918a9a1934b4b8114cc56845b3f8921
parente24ff261e76f5c05e35375277f6ddb1675fc90a8 (diff)
parent2659b522094c325f1ee4138f6cf793373d8c9c52 (diff)
downloadmitmproxy-822797c7e0588695ecf318b7e7c424eda393405c.tar.gz
mitmproxy-822797c7e0588695ecf318b7e7c424eda393405c.tar.bz2
mitmproxy-822797c7e0588695ecf318b7e7c424eda393405c.zip
Merge pull request #2307 from cortesi/layouts
console: add a two-pane layout
-rw-r--r--mitmproxy/options.py10
-rw-r--r--mitmproxy/tools/cmdline.py2
-rw-r--r--mitmproxy/tools/console/eventlog.py47
-rw-r--r--mitmproxy/tools/console/flowlist.py62
-rw-r--r--mitmproxy/tools/console/master.py96
-rw-r--r--mitmproxy/tools/console/overlay.py2
-rw-r--r--mitmproxy/tools/console/signals.py3
-rw-r--r--mitmproxy/tools/console/statusbar.py4
-rw-r--r--mitmproxy/tools/console/window.py197
-rw-r--r--test/mitmproxy/console/test_flowlist.py29
10 files changed, 259 insertions, 193 deletions
diff --git a/mitmproxy/options.py b/mitmproxy/options.py
index e477bed5..5667f39f 100644
--- a/mitmproxy/options.py
+++ b/mitmproxy/options.py
@@ -21,6 +21,11 @@ view_orders = [
"url",
"size",
]
+console_layouts = [
+ "single",
+ "vertical",
+ "horizontal",
+]
APP_HOST = "mitm.it"
APP_PORT = 80
@@ -371,8 +376,9 @@ class Options(optmanager.OptManager):
# Console options
self.add_option(
- "console_eventlog", bool, False,
- "Show event log."
+ "console_layout", str, "single",
+ "Console layout.",
+ choices=sorted(console_layouts),
)
self.add_option(
"console_focus_follow", bool, False,
diff --git a/mitmproxy/tools/cmdline.py b/mitmproxy/tools/cmdline.py
index 73ec04c7..5711ce73 100644
--- a/mitmproxy/tools/cmdline.py
+++ b/mitmproxy/tools/cmdline.py
@@ -108,7 +108,7 @@ def mitmproxy(opts):
parser = argparse.ArgumentParser(usage="%(prog)s [options]")
common_options(parser, opts)
- opts.make_parser(parser, "console_eventlog")
+ opts.make_parser(parser, "console_layout")
group = parser.add_argument_group(
"Filters",
"See help in mitmproxy for filter expression syntax."
diff --git a/mitmproxy/tools/console/eventlog.py b/mitmproxy/tools/console/eventlog.py
new file mode 100644
index 00000000..0b8a3f8c
--- /dev/null
+++ b/mitmproxy/tools/console/eventlog.py
@@ -0,0 +1,47 @@
+import urwid
+from mitmproxy.tools.console import signals
+
+EVENTLOG_SIZE = 10000
+
+
+class LogBufferWalker(urwid.SimpleListWalker):
+ pass
+
+
+class EventLog(urwid.ListBox):
+ keyctx = "eventlog"
+
+ def __init__(self, master):
+ self.walker = LogBufferWalker([])
+ self.master = master
+ urwid.ListBox.__init__(self, self.walker)
+ signals.sig_add_log.connect(self.sig_add_log)
+
+ def set_focus(self, index):
+ if 0 <= index < len(self.walker):
+ super().set_focus(index)
+
+ def keypress(self, size, key):
+ if key == "z":
+ self.master.clear_events()
+ key = None
+ elif key == "m_end":
+ self.set_focus(len(self.walker) - 1)
+ elif key == "m_start":
+ self.set_focus(0)
+ return urwid.ListBox.keypress(self, size, key)
+
+ def sig_add_log(self, sender, e, level):
+ txt = "%s: %s" % (level, str(e))
+ if level in ("error", "warn"):
+ e = urwid.Text((level, txt))
+ else:
+ e = urwid.Text(txt)
+ self.walker.append(e)
+ if len(self.walker) > EVENTLOG_SIZE:
+ self.walker.pop(0)
+ if self.master.options.console_focus_follow:
+ self.walker.set_focus(len(self.walker) - 1)
+
+ def clear_events(self):
+ self.walker[:] = []
diff --git a/mitmproxy/tools/console/flowlist.py b/mitmproxy/tools/console/flowlist.py
index 8e28ff0f..4184eeb4 100644
--- a/mitmproxy/tools/console/flowlist.py
+++ b/mitmproxy/tools/console/flowlist.py
@@ -47,68 +47,6 @@ footer = [
]
-class LogBufferBox(urwid.ListBox):
-
- def __init__(self, master):
- self.master = master
- urwid.ListBox.__init__(self, master.logbuffer)
-
- def set_focus(self, index):
- if 0 <= index < len(self.master.logbuffer):
- super().set_focus(index)
-
- def keypress(self, size, key):
- if key == "z":
- self.master.clear_events()
- key = None
- elif key == "m_end":
- self.set_focus(len(self.master.logbuffer) - 1)
- elif key == "m_start":
- self.set_focus(0)
- return urwid.ListBox.keypress(self, size, key)
-
-
-class BodyPile(urwid.Pile):
-
- def __init__(self, master):
- h = urwid.Text("Event log")
- h = urwid.Padding(h, align="left", width=("relative", 100))
-
- self.inactive_header = urwid.AttrWrap(h, "heading_inactive")
- self.active_header = urwid.AttrWrap(h, "heading")
-
- urwid.Pile.__init__(
- self,
- [
- FlowListBox(master),
- urwid.Frame(
- LogBufferBox(master),
- header = self.inactive_header
- )
- ]
- )
- self.master = master
-
- def keypress(self, size, key):
- if key == "tab":
- self.focus_position = (
- self.focus_position + 1) % len(self.widget_list)
- if self.focus_position == 1:
- self.widget_list[1].header = self.active_header
- else:
- self.widget_list[1].header = self.inactive_header
- key = None
-
- # This is essentially a copypasta from urwid.Pile's keypress handler.
- # So much for "closed for modification, but open for extension".
- item_rows = None
- if len(size) == 2:
- item_rows = self.get_item_rows(size, focus = True)
- i = self.widget_list.index(self.focus_item)
- tsize = self.get_item_size(size, i, True, item_rows)
- return self.focus_item.keypress(tsize, key)
-
-
class FlowItem(urwid.WidgetWrap):
def __init__(self, master, flow):
diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py
index b88a0354..d1d470e1 100644
--- a/mitmproxy/tools/console/master.py
+++ b/mitmproxy/tools/console/master.py
@@ -31,8 +31,6 @@ from mitmproxy.tools.console import window
from mitmproxy import contentviews
from mitmproxy.utils import strutils
-EVENTLOG_SIZE = 10000
-
class Logger:
def log(self, evt):
@@ -82,15 +80,41 @@ class ConsoleAddon:
self.master = master
self.started = False
+ @command.command("console.layout.options")
+ def layout_options(self) -> typing.Sequence[str]:
+ """
+ Returns the valid options for console layout. Use these by setting
+ the console_layout option.
+ """
+ return ["single", "vertical", "horizontal"]
+
+ @command.command("console.layout.cycle")
+ def layout_cycle(self) -> None:
+ """
+ Cycle through the console layout options.
+ """
+ opts = self.layout_options()
+ off = self.layout_options().index(ctx.options.console_layout)
+ ctx.options.update(
+ console_layout = opts[(off + 1) % len(opts)]
+ )
+
+ @command.command("console.panes.next")
+ def panes_next(self) -> None:
+ """
+ Go to the next layout pane.
+ """
+ self.master.window.switch()
+
@command.command("console.options.reset.current")
def options_reset_current(self) -> None:
"""
Reset the current option in the options editor.
"""
- if self.master.window.focus.keyctx != "options":
+ fv = self.master.window.current("options")
+ if not fv:
raise exceptions.CommandError("Not viewing options.")
- name = self.master.window.windows["options"].current_name()
- self.master.commands.call("options.reset.one %s" % name)
+ self.master.commands.call("options.reset.one %s" % fv.current_name())
@command.command("console.nav.start")
def nav_start(self) -> None:
@@ -166,8 +190,9 @@ class ConsoleAddon:
except exceptions.CommandError as e:
signals.status_message.send(message=str(e))
- self.master.overlay(overlay.Chooser(self.master, prompt, choices, "", callback))
- ctx.log.info(choices)
+ self.master.overlay(
+ overlay.Chooser(self.master, prompt, choices, "", callback)
+ )
@command.command("console.choose.cmd")
def console_choose_cmd(
@@ -189,8 +214,9 @@ class ConsoleAddon:
except exceptions.CommandError as e:
signals.status_message.send(message=str(e))
- self.master.overlay(overlay.Chooser(self.master, prompt, choices, "", callback))
- ctx.log.info(choices)
+ self.master.overlay(
+ overlay.Chooser(self.master, prompt, choices, "", callback)
+ )
@command.command("console.command")
def console_command(self, *partial: typing.Sequence[str]) -> None:
@@ -209,6 +235,11 @@ class ConsoleAddon:
"""View the options editor."""
self.master.switch_view("options")
+ @command.command("console.view.eventlog")
+ def view_eventlog(self) -> None:
+ """View the options editor."""
+ self.master.switch_view("eventlog")
+
@command.command("console.view.help")
def view_help(self) -> None:
"""View help."""
@@ -296,9 +327,9 @@ class ConsoleAddon:
"""
Set the display mode for the current flow view.
"""
- if self.master.window.focus.keyctx != "flowview":
+ fv = self.master.window.current("flowview")
+ if not fv:
raise exceptions.CommandError("Not viewing a flow.")
- fv = self.master.window.windows["flowview"]
idx = fv.body.tab_offset
def callback(opt):
@@ -318,9 +349,9 @@ class ConsoleAddon:
"""
Get the display mode for the current flow view.
"""
- if self.master.window.focus.keyctx != "flowview":
+ fv = self.master.window.any("flowview")
+ if not fv:
raise exceptions.CommandError("Not viewing a flow.")
- fv = self.master.window.windows["flowview"]
idx = fv.body.tab_offset
return self.master.commands.call_args(
"view.getval",
@@ -340,19 +371,18 @@ class ConsoleAddon:
for f in flows:
signals.flow_change.send(self, flow=f)
- def configure(self, updated):
- if self.started:
- if "console_eventlog" in updated:
- pass
-
def default_keymap(km):
km.add(":", "console.command ''", ["global"])
km.add("?", "console.view.help", ["global"])
km.add("C", "console.view.commands", ["global"])
km.add("O", "console.view.options", ["global"])
+ km.add("E", "console.view.eventlog", ["global"])
km.add("Q", "console.exit", ["global"])
km.add("q", "console.view.pop", ["global"])
+ km.add("-", "console.layout.cycle", ["global"])
+ km.add("shift tab", "console.panes.next", ["global"])
+ km.add("P", "console.view.flow @focus", ["global"])
km.add("g", "console.nav.start", ["global"])
km.add("G", "console.nav.end", ["global"])
@@ -366,7 +396,6 @@ def default_keymap(km):
km.add("i", "console.command set intercept=", ["global"])
km.add("W", "console.command set save_stream_file=", ["global"])
-
km.add("A", "flow.resume @all", ["flowlist", "flowview"])
km.add("a", "flow.resume @focus", ["flowlist", "flowview"])
km.add(
@@ -375,9 +404,8 @@ def default_keymap(km):
)
km.add("d", "view.remove @focus", ["flowlist", "flowview"])
km.add("D", "view.duplicate @focus", ["flowlist", "flowview"])
- km.add("e", "set console_eventlog=toggle", ["flowlist"])
km.add(
- "E",
+ "e",
"console.choose.cmd Format export.formats "
"console.command export.file {choice} @focus ''",
["flowlist", "flowview"]
@@ -461,8 +489,6 @@ class ConsoleMaster(master.Master):
default_keymap(self.keymap)
self.options.errored.connect(self.options_error)
- self.logbuffer = urwid.SimpleListWalker([])
-
self.view_stack = []
signals.call_in.connect(self.sig_call_in)
@@ -508,19 +534,10 @@ class ConsoleMaster(master.Master):
def sig_add_log(self, sender, e, level):
if self.options.verbosity < log.log_tier(level):
return
-
if level in ("error", "warn"):
signals.status_message.send(
message = "{}: {}".format(level.title(), e)
)
- e = urwid.Text((level, str(e)))
- else:
- e = urwid.Text(str(e))
- self.logbuffer.append(e)
- if len(self.logbuffer) > EVENTLOG_SIZE:
- self.logbuffer.pop(0)
- if self.options.console_focus_follow:
- self.logbuffer.set_focus(len(self.logbuffer) - 1)
def sig_call_in(self, sender, seconds, callback, args=()):
def cb(*_):
@@ -621,12 +638,9 @@ class ConsoleMaster(master.Master):
self.window = window.Window(self)
self.loop.widget = self.window
+ self.window.refresh()
self.loop.set_alarm_in(0.01, self.ticker)
- self.loop.set_alarm_in(
- 0.0001,
- lambda *args: self.switch_view("flowlist")
- )
self.start()
try:
@@ -646,13 +660,8 @@ class ConsoleMaster(master.Master):
def shutdown(self):
raise urwid.ExitMainLoop
- def sig_exit_overlay(self, *args, **kwargs):
- self.loop.widget = self.window
-
def overlay(self, widget, **kwargs):
- self.loop.widget = overlay.SimpleOverlay(
- self, widget, self.loop.widget, widget.width, **kwargs
- )
+ self.window.set_overlay(widget, **kwargs)
def switch_view(self, name):
self.window.push(name)
@@ -660,6 +669,3 @@ class ConsoleMaster(master.Master):
def quit(self, a):
if a != "n":
self.shutdown()
-
- def clear_events(self):
- self.logbuffer[:] = []
diff --git a/mitmproxy/tools/console/overlay.py b/mitmproxy/tools/console/overlay.py
index 2fa6aa46..abfb3909 100644
--- a/mitmproxy/tools/console/overlay.py
+++ b/mitmproxy/tools/console/overlay.py
@@ -8,6 +8,8 @@ from mitmproxy.tools.console import grideditor
class SimpleOverlay(urwid.Overlay):
+ keyctx = "overlay"
+
def __init__(self, master, widget, parent, width, valign="middle"):
self.widget = widget
self.master = master
diff --git a/mitmproxy/tools/console/signals.py b/mitmproxy/tools/console/signals.py
index 885cdbfb..5cbbd875 100644
--- a/mitmproxy/tools/console/signals.py
+++ b/mitmproxy/tools/console/signals.py
@@ -48,6 +48,3 @@ flowlist_change = blinker.Signal()
# Pop and push view state onto a stack
pop_view_state = blinker.Signal()
push_view_state = blinker.Signal()
-
-# Exits overlay if there is one
-exit_overlay = blinker.Signal()
diff --git a/mitmproxy/tools/console/statusbar.py b/mitmproxy/tools/console/statusbar.py
index a5db0f4a..7e471b90 100644
--- a/mitmproxy/tools/console/statusbar.py
+++ b/mitmproxy/tools/console/statusbar.py
@@ -34,8 +34,8 @@ class PromptStub:
class ActionBar(urwid.WidgetWrap):
def __init__(self, master):
- urwid.WidgetWrap.__init__(self, None)
self.master = master
+ urwid.WidgetWrap.__init__(self, None)
self.clear()
signals.status_message.connect(self.sig_message)
signals.status_prompt.connect(self.sig_prompt)
@@ -151,7 +151,7 @@ class StatusBar(urwid.WidgetWrap):
self.master = master
self.helptext = helptext
self.ib = urwid.WidgetWrap(urwid.Text(""))
- self.ab = ActionBar(self)
+ self.ab = ActionBar(self.master)
super().__init__(urwid.Pile([self.ib, self.ab]))
signals.update_settings.connect(self.sig_update)
signals.flowlist_change.connect(self.sig_update)
diff --git a/mitmproxy/tools/console/window.py b/mitmproxy/tools/console/window.py
index d7038da0..ea5b7f3b 100644
--- a/mitmproxy/tools/console/window.py
+++ b/mitmproxy/tools/console/window.py
@@ -8,6 +8,64 @@ from mitmproxy.tools.console import options
from mitmproxy.tools.console import overlay
from mitmproxy.tools.console import help
from mitmproxy.tools.console import grideditor
+from mitmproxy.tools.console import eventlog
+
+
+class WindowStack:
+ def __init__(self, master, base):
+ self.master = master
+ self.windows = dict(
+ flowlist = flowlist.FlowListBox(master),
+ flowview = flowview.FlowView(master),
+ commands = commands.Commands(master),
+ options = options.Options(master),
+ help = help.HelpView(None),
+ eventlog = eventlog.EventLog(master),
+
+ edit_focus_query = grideditor.QueryEditor(master),
+ edit_focus_cookies = grideditor.CookieEditor(master),
+ edit_focus_setcookies = grideditor.SetCookieEditor(master),
+ edit_focus_form = grideditor.RequestFormEditor(master),
+ edit_focus_path = grideditor.PathEditor(master),
+ edit_focus_request_headers = grideditor.RequestHeaderEditor(master),
+ edit_focus_response_headers = grideditor.ResponseHeaderEditor(master),
+ )
+ self.stack = [base]
+ self.overlay = None
+
+ def set_overlay(self, o, **kwargs):
+ self.overlay = overlay.SimpleOverlay(self, o, self.top(), o.width, **kwargs)
+
+ @property
+ def topwin(self):
+ return self.windows[self.stack[-1]]
+
+ def top(self):
+ if self.overlay:
+ return self.overlay
+ return self.topwin
+
+ def push(self, wname):
+ if self.stack[-1] == wname:
+ return
+ self.stack.append(wname)
+
+ def pop(self, *args, **kwargs):
+ """
+ Pop off the stack, return True if we're already at the top.
+ """
+ if self.overlay:
+ self.overlay = None
+ elif len(self.stack) > 1:
+ self.call("view_popping")
+ self.stack.pop()
+ else:
+ return True
+
+ def call(self, name, *args, **kwargs):
+ f = getattr(self.topwin, name, None)
+ if f:
+ f(*args, **kwargs)
class Window(urwid.Frame):
@@ -19,38 +77,51 @@ class Window(urwid.Frame):
footer = urwid.AttrWrap(self.statusbar, "background")
)
self.master = master
- self.primary_stack = []
self.master.view.sig_view_refresh.connect(self.view_changed)
self.master.view.sig_view_add.connect(self.view_changed)
self.master.view.sig_view_remove.connect(self.view_changed)
self.master.view.sig_view_update.connect(self.view_changed)
self.master.view.focus.sig_change.connect(self.view_changed)
- signals.focus.connect(self.sig_focus)
-
self.master.view.focus.sig_change.connect(self.focus_changed)
- signals.flow_change.connect(self.flow_changed)
+ signals.focus.connect(self.sig_focus)
+ signals.flow_change.connect(self.flow_changed)
signals.pop_view_state.connect(self.pop)
signals.push_view_state.connect(self.push)
- self.windows = dict(
- flowlist = flowlist.FlowListBox(self.master),
- flowview = flowview.FlowView(self.master),
- commands = commands.Commands(self.master),
- options = options.Options(self.master),
- help = help.HelpView(None),
- edit_focus_query = grideditor.QueryEditor(self.master),
- edit_focus_cookies = grideditor.CookieEditor(self.master),
- edit_focus_setcookies = grideditor.SetCookieEditor(self.master),
- edit_focus_form = grideditor.RequestFormEditor(self.master),
- edit_focus_path = grideditor.PathEditor(self.master),
- edit_focus_request_headers = grideditor.RequestHeaderEditor(self.master),
- edit_focus_response_headers = grideditor.ResponseHeaderEditor(self.master),
- )
- def call(self, v, name, *args, **kwargs):
- f = getattr(v, name, None)
- if f:
- f(*args, **kwargs)
+ self.master.options.subscribe(self.configure, ["console_layout"])
+ self.pane = 0
+ self.stacks = [
+ WindowStack(master, "flowlist"),
+ WindowStack(master, "eventlog")
+ ]
+
+ def focus_stack(self):
+ return self.stacks[self.pane]
+
+ def configure(self, otions, updated):
+ self.refresh()
+
+ def refresh(self):
+ """
+ Redraw the layout.
+ """
+ c = self.master.options.console_layout
+
+ w = None
+ if c == "single":
+ w = self.stacks[0].top()
+ elif c == "vertical":
+ w = urwid.Pile(
+ [i.top() for i in self.stacks]
+ )
+ else:
+ w = urwid.Columns(
+ [i.top() for i in self.stacks], dividechars=1
+ )
+ self.body = urwid.AttrWrap(w, "background")
+ if c == "single":
+ self.pane = 0
def flow_changed(self, sender, flow):
if self.master.view.focus.flow:
@@ -62,49 +133,74 @@ class Window(urwid.Frame):
Triggered when the focus changes - either when it's modified, or
when it changes to a different flow altogether.
"""
- self.call(self.focus, "focus_changed")
+ for i in self.stacks:
+ i.call("focus_changed")
def view_changed(self, *args, **kwargs):
"""
Triggered when the view list has changed.
"""
- self.call(self.focus, "view_changed")
+ for i in self.stacks:
+ i.call("view_changed")
- def view_popping(self, *args, **kwargs):
+ def set_overlay(self, o, **kwargs):
"""
- Triggered when the view list has changed.
+ Set an overlay on the currently focused stack.
"""
- self.call(self.focus, "view_popping")
+ self.focus_stack().set_overlay(o, **kwargs)
+ self.refresh()
def push(self, wname):
- if self.primary_stack and self.primary_stack[-1] == wname:
- return
- self.primary_stack.append(wname)
- self.body = urwid.AttrWrap(
- self.windows[wname], "background"
- )
+ """
+ Push a window onto the currently focused stack.
+ """
+ self.focus_stack().push(wname)
+ self.refresh()
self.view_changed()
self.focus_changed()
def pop(self, *args, **kwargs):
- if isinstance(self.master.loop.widget, overlay.SimpleOverlay):
- self.master.loop.widget = self
+ """
+ Pop a window from the currently focused stack. If there is only one
+ window on the stack, this prompts for exit.
+ """
+ if self.focus_stack().pop():
+ self.master.prompt_for_exit()
else:
- if len(self.primary_stack) > 1:
- self.view_popping()
- self.primary_stack.pop()
- self.body = urwid.AttrWrap(
- self.windows[self.primary_stack[-1]],
- "background",
- )
- self.view_changed()
- self.focus_changed()
- else:
- self.master.prompt_for_exit()
+ self.refresh()
+ self.view_changed()
+ self.focus_changed()
+
+ def current(self, keyctx):
+ """
+
+ Returns the top window of the current stack, IF the current focus
+ has a matching key context.
+ """
+ t = self.focus_stack().topwin
+ if t.keyctx == keyctx:
+ return t
+
+ def any(self, keyctx):
+ """
+ Returns the top window of either stack if they match the context.
+ """
+ for t in [x.topwin for x in self.stacks]:
+ if t.keyctx == keyctx:
+ return t
def sig_focus(self, sender, section):
self.focus_position = section
+ def switch(self):
+ """
+ Switch between the two panes.
+ """
+ if self.master.options.console_layout == "single":
+ self.pane = 0
+ else:
+ self.pane = (self.pane + 1) % len(self.stacks)
+
def mouse_event(self, *args, **kwargs):
# args: (size, event, button, col, row)
k = super().mouse_event(*args, **kwargs)
@@ -123,7 +219,10 @@ class Window(urwid.Frame):
return True
def keypress(self, size, k):
- if self.focus.keyctx:
- k = self.master.keymap.handle(self.focus.keyctx, k)
- if k:
+ if self.focus_part == "footer":
return super().keypress(size, k)
+ else:
+ fs = self.focus_stack().top()
+ k = fs.keypress(size, k)
+ if k:
+ return self.master.keymap.handle(fs.keyctx, k)
diff --git a/test/mitmproxy/console/test_flowlist.py b/test/mitmproxy/console/test_flowlist.py
deleted file mode 100644
index 6d82749d..00000000
--- a/test/mitmproxy/console/test_flowlist.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import urwid
-
-import mitmproxy.tools.console.flowlist as flowlist
-from mitmproxy.tools import console
-from mitmproxy import proxy
-from mitmproxy import options
-
-
-class TestFlowlist:
- def mkmaster(self, **opts):
- if "verbosity" not in opts:
- opts["verbosity"] = 1
- o = options.Options(**opts)
- return console.master.ConsoleMaster(o, proxy.DummyServer())
-
- def test_logbuffer_set_focus(self):
- m = self.mkmaster()
- b = flowlist.LogBufferBox(m)
- e = urwid.Text("Log message")
- m.logbuffer.append(e)
- m.logbuffer.append(e)
-
- assert len(m.logbuffer) == 2
- b.set_focus(0)
- assert m.logbuffer.focus == 0
- b.set_focus(1)
- assert m.logbuffer.focus == 1
- b.set_focus(2)
- assert m.logbuffer.focus == 1