Merge pull request #324 from CanonicalLtd/mwhudson/click-helper

add --click, --script arguments to subiquity-tui
This commit is contained in:
Michael Hudson-Doyle 2018-05-03 11:35:35 +12:00 committed by GitHub
commit 92457e5724
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 0 deletions

View File

@ -48,6 +48,10 @@ checks:
- /usr/bin/curtin - /usr/bin/curtin
''' '''
class ClickAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
namespace.scripts.append("c(" + repr(values) + ")")
def parse_options(argv): def parse_options(argv):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='SUbiquity - Ubiquity for Servers', description='SUbiquity - Ubiquity for Servers',
@ -65,6 +69,8 @@ def parse_options(argv):
dest='uefi', dest='uefi',
help='run in uefi support mode') help='run in uefi support mode')
parser.add_argument('--screens', action='append', dest='screens', default=[]) parser.add_argument('--screens', action='append', dest='screens', default=[])
parser.add_argument('--script', metavar="SCRIPT", action='append', dest='scripts', default=[], help='Execute SCRIPT in a namespace containing view helpers and "ui"')
parser.add_argument('--click', metavar="PAT", action=ClickAction, help='Synthesize a click on a button matching PAT')
parser.add_argument('--answers') parser.add_argument('--answers')
return parser.parse_args(argv) return parser.parse_args(argv)

View File

@ -321,6 +321,69 @@ class Application:
def exit(self): def exit(self):
raise urwid.ExitMainLoop() raise urwid.ExitMainLoop()
def run_scripts(self, scripts):
# run_scripts runs (or rather arranges to run, it's all async)
# a series of python snippets in a helpful namespace. This is
# all in aid of being able to test some part of the UI without
# having to click the same buttons over and over again to get
# the UI to the part you are working on.
#
# In the namespace are:
# * everything from view_helpers
# * wait, delay execution of subsequent scripts for a while
# * c, a function that finds a button and clicks it. uses
# wait, above to wait for the button to appear in case it
# takes a while.
from subiquitycore.testing import view_helpers
loop = self.common['loop']
class ScriptState:
def __init__(self):
self.ns = view_helpers.__dict__.copy()
self.waiting = False
self.wait_count = 0
self.scripts = scripts
ss = ScriptState()
def _run_script(*args):
log.debug("running %s", ss.scripts[0])
exec(ss.scripts[0], ss.ns)
if ss.waiting:
return
ss.scripts = ss.scripts[1:]
if ss.scripts:
loop.set_alarm_in(0.01, _run_script)
def c(pat):
but = view_helpers.find_button_matching(self.common['ui'], '.*' + pat + '.*')
if not but:
ss.wait_count += 1
if ss.wait_count > 10:
raise Exception("no button found matching %r after waiting for 10 secs"%(pat,))
wait(1, func=lambda : c(pat))
return
ss.wait_count = 0
view_helpers.click(but)
def wait(delay, func=None):
ss.waiting = True
def next(loop, user_data):
ss.waiting = False
if func is not None:
func()
if not ss.waiting:
ss.scripts = ss.scripts[1:]
if ss.scripts:
_run_script()
loop.set_alarm_in(delay, next)
ss.ns['c'] = c
ss.ns['wait'] = wait
ss.ns['ui'] = self.common['ui']
self.common['loop'].set_alarm_in(0.06, _run_script)
def run(self): def run(self):
if not hasattr(self, 'loop'): if not hasattr(self, 'loop'):
if self.common['opts'].run_on_serial: if self.common['opts'].run_on_serial:
@ -338,6 +401,8 @@ class Application:
self.common['base_model'] = self.model_class(self.common) self.common['base_model'] = self.model_class(self.common)
try: try:
self.common['loop'].set_alarm_in(0.05, self.next_screen) self.common['loop'].set_alarm_in(0.05, self.next_screen)
if self.common['opts'].scripts:
self.run_scripts(self.common['opts'].scripts)
controllers_mod = __import__('%s.controllers' % self.project, None, None, ['']) controllers_mod = __import__('%s.controllers' % self.project, None, None, [''])
for k in self.controllers: for k in self.controllers:
log.debug("Importing controller: {}".format(k)) log.debug("Importing controller: {}".format(k))

View File

@ -4,6 +4,8 @@ import urwid
def find_with_pred(w, pred, return_path=False): def find_with_pred(w, pred, return_path=False):
def _walk(w, path): def _walk(w, path):
if not isinstance(w, urwid.Widget):
raise RuntimeError("_walk walked to non-widget %r via %r" % (w, path))
if pred(w): if pred(w):
return w, path return w, path
if hasattr(w, '_wrapped_widget'): if hasattr(w, '_wrapped_widget'):
@ -15,6 +17,11 @@ def find_with_pred(w, pred, return_path=False):
r, p = _walk(w, (w,) + path) r, p = _walk(w, (w,) + path)
if r: if r:
return r, p return r, p
elif isinstance(w, urwid.Frame):
for w, _ in w.contents.values():
r, p = _walk(w, (w,) + path)
if r:
return r, p
elif hasattr(w, 'contents'): elif hasattr(w, 'contents'):
contents = w.contents contents = w.contents
for w, _ in contents: for w, _ in contents: