diff options
author | Vadim Rutkovsky <vrutkovs@redhat.com> | 2014-03-21 23:15:00 +0800 |
---|---|---|
committer | Vadim Rutkovsky <vrutkovs@redhat.com> | 2014-03-25 16:46:23 +0800 |
commit | 399b1a6fc593a9c5e692923120346e0386f578e5 (patch) | |
tree | 0ce0730706208dfb3f34cd2c4b7335aa3872491f | |
parent | a9b96e5ae15d13d204e7bef5da283bb34154d9c8 (diff) | |
download | gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.tar gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.tar.gz gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.tar.bz2 gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.tar.lz gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.tar.xz gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.tar.zst gsoc2013-evolution-399b1a6fc593a9c5e692923120346e0386f578e5.zip |
Add installed tests using --enable-installed-tests switch
https://bugzilla.gnome.org/show_bug.cgi?id=726832
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | m4/behave-installed-test.m4 | 135 | ||||
-rw-r--r-- | tests/common_steps.py | 223 | ||||
-rw-r--r-- | tests/environment.py | 46 | ||||
-rw-r--r-- | tests/shortcuts.feature | 135 | ||||
-rw-r--r-- | tests/steps/initial_setup_steps.py | 138 | ||||
-rw-r--r-- | tests/steps/steps.py | 122 |
8 files changed, 810 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index db35826bbb..521fe03b4d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -147,4 +147,9 @@ DISTCHECK_CONFIGURE_FLAGS = \ --disable-weather \ --with-help +@BEHAVE_INSTALLED_TESTS_RULE@ +INSTALLED_TESTS=general_shortcuts mail_shortcuts contacts_shortcuts \ + calendar_shortcuts memos_shortcuts view_shortcuts menu_shortcuts +INSTALLED_TESTS_TYPE=session-exclusive + -include $(top_srcdir)/git.mk diff --git a/configure.ac b/configure.ac index f4c9f7007f..023a126368 100644 --- a/configure.ac +++ b/configure.ac @@ -1411,6 +1411,11 @@ if test "$with_catalog" = "yes"; then fi AM_CONDITIONAL(GLADE_CATALOG, test "x$with_catalog" != "xno") +dnl ******************************************** +dnl Installed tests +dnl ******************************************** +BEHAVE_INSTALLED_TESTS + dnl ****************************** dnl Makefiles dnl ****************************** @@ -1592,4 +1597,5 @@ echo " Highlight support: $msg_text_highlight Plugins: $msg_plugins User documentation: $with_help + Installed tests: $enable_installed_tests " diff --git a/m4/behave-installed-test.m4 b/m4/behave-installed-test.m4 new file mode 100644 index 0000000000..f1f13cfaee --- /dev/null +++ b/m4/behave-installed-test.m4 @@ -0,0 +1,135 @@ +# How to use the installed tests m4 +# +# Place BEHAVE_INSTALLED_TESTS somewhere in configure.ac +# +# Writing your Makefile.am +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Somewhere in your Makefile.am in this test directory, you need to declare +# the following variables: +# +# INSTALLED_TESTS=list of tags for tests to install +# INSTALLED_TESTS_TYPE=session-exclusive +# +# First the list of tests which should be installed, followed by +# the type of test they should be configured as. The type can +# be 'session' or 'session-exclusive' +# +# More information about valid types can be found here: +# https://wiki.gnome.org/GnomeGoals/InstalledTests +# +# The last variable is optional, but can be useful to configure +# your test program to run in the installed environment as opposed +# to the normal `make check' run. +# +# Then place this somewhere in your Makefile.am +# +# @BEHAVE_INSTALLED_TESTS_RULE@ +# +# And the following in configure.ac +# +# BEHAVE_INSTALLED_TESTS +# +# And that's it, now your unit tests will be installed along with +# a .test metadata file into $(pkglibexecdir) if --enable-installed-tests +# is passed to your configure script, and will be run automatically +# by the continuous integration servers. +# +# FIXME: Change the above link to point to real documentation, not +# a gnome goal page which might disappear at some point. +# +# BUGS: This macro hooks into install-exec-am and install-data-am +# which are internals of Automake. This is because Automake doesnt +# consider the regular install-exec-local / install-exec-hook or +# data install components unless variables have been setup for them +# in advance. +# +# This doesnt seem to present a problem, but it is depending on +# internals of Automake instead of clear documented API. + +# Place this in configure.ac to enable +# the installed tests option. + +AC_DEFUN([BEHAVE_INSTALLED_TESTS], [ +AC_PREREQ([2.50])dnl +AC_REQUIRE([AM_NLS])dnl + + AC_PROG_INSTALL + AC_PROG_MKDIR_P + AC_PROG_LIBTOOL + + AC_ARG_ENABLE(installed-tests, + [AC_HELP_STRING([--enable-installed-tests], + [enable installed unit tests [default=no]])],, + [enable_installed_tests="no"]) + + AM_CONDITIONAL([BEHAVE_INSTALLED_TESTS_ENABLED],[test "x$enable_installed_tests" = "xyes"]) + AC_SUBST([BEHAVE_INSTALLED_TESTS_ENABLED], [$enable_installed_tests]) + + # Define the rule for makefiles + BEHAVE_INSTALLED_TESTS_RULE=' + +ifeq ($(BEHAVE_INSTALLED_TESTS_ENABLED),yes) + +install-exec-am: installed-tests-exec-hook +install-data-am: installed-tests-data-hook +uninstall-am: uninstall-tests-hook + +META_DIRECTORY=${DESTDIR}/${datadir}/installed-tests/${PACKAGE} +EXEC_DIRECTORY=${DESTDIR}/${pkglibexecdir}/installed-tests + +FINAL_TEST_ENVIRONMENT= +ifneq ($(INSTALLED_TESTS_ENVIRONMENT),) + FINAL_TEST_ENVIRONMENT="env $(INSTALLED_TESTS_ENVIRONMENT)" +endif +BEHAVE_FEATURES=$(notdir $(wildcard tests/*.feature)) +BEHAVE_STEP_DEFINITION=$(notdir $(wildcard tests/steps/*.py)) +BEHAVE_COMMON_FILES=environment.py common_steps.py + +installed-tests-exec-hook: + @$(MKDIR_P) $(EXEC_DIRECTORY); + @for feature in $(BEHAVE_FEATURES); do \ + $(LIBTOOL) --mode=install $(INSTALL) --mode=777 tests/$$feature $(EXEC_DIRECTORY);\ + done + @for common_file in $(BEHAVE_COMMON_FILES); do \ + $(LIBTOOL) --mode=install $(INSTALL) --mode=777 tests/$$common_file $(EXEC_DIRECTORY);\ + done + @$(MKDIR_P) $(EXEC_DIRECTORY)/steps; + @for step_definition in $(BEHAVE_STEP_DEFINITION); do \ + $(LIBTOOL) --mode=install $(INSTALL) --mode=777 tests/steps/$$step_definition $(EXEC_DIRECTORY)/steps;\ + done + + +installed-tests-data-hook: + @$(MKDIR_P) $(META_DIRECTORY); + @for test in $(INSTALLED_TESTS); do \ + echo "Installing $$test.test to $(META_DIRECTORY)"; \ + echo m4_escape([[Test]]) > $(META_DIRECTORY)/$$test.test; \ + echo "Exec=behave $(pkglibexecdir)/installed-tests -t $$test -k -f plain" \ + >> $(META_DIRECTORY)/$$test.test; \ + echo "Type=$(INSTALLED_TESTS_TYPE)" >> $(META_DIRECTORY)/$$test.test; \ + done + +uninstall-tests-hook: + @for feature in $(BEHAVE_FEATURES); do\ + echo "Removing feature $(EXEC_DIRECTORY) $$feature";\ + $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/$$feature;\ + done + @for common_file in $(BEHAVE_COMMON_FILES); do\ + echo "Removing feature $(EXEC_DIRECTORY) $$common_file";\ + $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/$$common_file;\ + done + @for step_definition in $(BEHAVE_STEP_DEFINITION); do\ + echo "Removing feature $(EXEC_DIRECTORY)/steps $$step_definition";\ + $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/steps/$$step_definition;\ + done + @for test in $(INSTALLED_TESTS); do\ + $(LIBTOOL) --mode=uninstall $(RM) $(META_DIRECTORY)/$$test.test;\ + done +endif +' + + # substitute @BEHAVE_INSTALLED_TESTS_RULE@ in Makefiles + AC_SUBST([BEHAVE_INSTALLED_TESTS_RULE]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([BEHAVE_INSTALLED_TESTS_RULE])]) +])
\ No newline at end of file diff --git a/tests/common_steps.py b/tests/common_steps.py new file mode 100644 index 0000000000..3752142f9d --- /dev/null +++ b/tests/common_steps.py @@ -0,0 +1,223 @@ +# -*- coding: UTF-8 -*- +from dogtail.utils import isA11yEnabled, enableA11y +if isA11yEnabled() is False: + enableA11y(True) + +from time import time, sleep +from functools import wraps +from os import strerror, errno, kill, system +from signal import signal, alarm, SIGALRM, SIGKILL +from subprocess import Popen +from behave import step +from gi.repository import GLib, Gio + +from dogtail.rawinput import keyCombo, absoluteMotion, pressKey +from dogtail.tree import root +from dogtail.utils import run +from unittest import TestCase + + +# Create a dummy unittest class to have nice assertions +class dummy(TestCase): + def runTest(self): # pylint: disable=R0201 + assert True + + +def wait_until(my_lambda, element, timeout=30, period=0.25): + """ + This function keeps running lambda with specified params until the result is True + or timeout is reached + Sample usages: + * wait_until(lambda x: x.name != 'Loading...', context.app) + Pause until window title is not 'Loading...'. + Return False if window title is still 'Loading...' + Throw an exception if window doesn't exist after default timeout + + * wait_until(lambda element, expected: x.text == expected, element, ('Expected text')) + Wait until element text becomes the expected (passed to the lambda) + + """ + exception_thrown = None + mustend = int(time()) + timeout + while int(time()) < mustend: + try: + if my_lambda(element): + return True + except Exception as e: + # If lambda has thrown the exception we'll re-raise it later + # and forget about if lambda passes + exception_thrown = e + sleep(period) + if exception_thrown: + raise exception_thrown + else: + return False + + +class TimeoutError(Exception): + """ + Timeout exception class for limit_execution_time_to function + """ + pass + + +def limit_execution_time_to( + seconds=10, error_message=strerror(errno.ETIME)): + """ + Decorator to limit function execution to specified limit + """ + def decorator(func): + def _handle_timeout(signum, frame): + raise TimeoutError(error_message) + + def wrapper(*args, **kwargs): + signal(SIGALRM, _handle_timeout) + alarm(seconds) + try: + result = func(*args, **kwargs) + finally: + alarm(0) + return result + + return wraps(func)(wrapper) + + return decorator + + +class App(object): + """ + This class does all basic events with the app + """ + def __init__( + self, appName, shortcut='<Control><Q>', a11yAppName=None, + forceKill=True, parameters='', recordVideo=False): + """ + Initialize object App + appName command to run the app + shortcut default quit shortcut + a11yAppName app's a11y name is different than binary + forceKill is the app supposed to be kill before/after test? + parameters has the app any params needed to start? (only for startViaCommand) + recordVideo start gnome-shell recording while running the app + """ + self.appCommand = appName + self.shortcut = shortcut + self.forceKill = forceKill + self.parameters = parameters + self.internCommand = self.appCommand.lower() + self.a11yAppName = a11yAppName + self.recordVideo = recordVideo + self.pid = None + + # a way of overcoming overview autospawn when mouse in 1,1 from start + pressKey('Esc') + absoluteMotion(100, 100, 2) + + # attempt to make a recording of the test + if self.recordVideo: + keyCombo('<Control><Alt><Shift>R') + + def isRunning(self): + """ + Is the app running? + """ + if self.a11yAppName is None: + self.a11yAppName = self.internCommand + + # Trap weird bus errors + for attempt in xrange(0, 10): + try: + return self.a11yAppName in [x.name for x in root.applications()] + except GLib.GError: + continue + raise Exception("10 at-spi errors, seems that bus is blocked") + + def kill(self): + """ + Kill the app via 'killall' + """ + if self.recordVideo: + keyCombo('<Control><Alt><Shift>R') + + try: + kill(self.pid, SIGKILL) + except: + # Fall back to killall + Popen("killall " + self.appCommand, shell=True).wait() + + def startViaCommand(self): + """ + Start the app via command + """ + if self.forceKill and self.isRunning(): + self.kill() + assert not self.isRunning(), "Application cannot be stopped" + + command = "%s %s" % (self.appCommand, self.parameters) + self.pid = run(command, timeout=1) + + assert self.isRunning(), "Application failed to start" + return root.application(self.a11yAppName) + + def closeViaShortcut(self): + """ + Close the app via shortcut + """ + if not self.isRunning(): + raise Exception("App is not running") + + keyCombo(self.shortcut) + assert not self.isRunning(), "Application cannot be stopped" + + +@step(u'Start a new Evolution instance') +def start_new_evolution_instance(context): + context.app = context.app_class.startViaCommand() + + +def cleanup(): + # Remove cached data and settings + folders = ['~/.local/share/evolution', '~/.cache/evolution', '~/.config/evolution'] + for folder in folders: + system("rm -rf %s > /dev/null" % folder) + + # Clean up goa data + system("rm -rf ~/.config/goa-1.0/accounts.conf") + system("killall goa-daemon 2&> /dev/null") + + # Reset GSettings + schemas = [x for x in Gio.Settings.list_schemas() if 'evolution' in x.lower()] + for schema in schemas: + system("gsettings reset-recursively %s" % schema) + + # Skip warning dialog + system("gsettings set org.gnome.evolution.shell skip-warning-dialog true") + # Show switcher buttons as icons (to minimize tree scrolling) + system("gsettings set org.gnome.evolution.shell buttons-style icons") + + + +def check_for_errors(context): + """Check that no error is displayed on Evolution UI""" + # Don't try to check for errors on dead app + if not context.app or context.app.dead: + return + alerts = context.app.findChildren(lambda x: x.roleName == 'alert') + if not alerts: + # alerts can also return None + return + alerts = filter(lambda x: x.showing, alerts) + if len(alerts) > 0: + labels = alerts[0].findChildren(lambda x: x.roleName == 'label') + messages = [x.name for x in labels] + + if alerts[0].name != 'Error' and alerts[0].showing: + # Erase the configuration and start all over again + system("evolution --force-shutdown &> /dev/null") + + # Remove previous data + folders = ['~/.local/share/evolution', '~/.cache/evolution', '~/.config/evolution'] + for folder in folders: + system("rm -rf %s > /dev/null" % folder) + + raise RuntimeError("Error occurred: %s" % messages) diff --git a/tests/environment.py b/tests/environment.py new file mode 100644 index 0000000000..2d6a09f479 --- /dev/null +++ b/tests/environment.py @@ -0,0 +1,46 @@ +# -*- coding: UTF-8 -*- + +from time import sleep +from dogtail.utils import isA11yEnabled, enableA11y +if not isA11yEnabled(): + enableA11y(True) + +from common_steps import App, dummy, cleanup +from dogtail.config import config + + +def before_all(context): + """Setup evolution stuff + Being executed once before any test + """ + + try: + # Skip dogtail actions to print to stdout + config.logDebugToStdOut = False + config.typingDelay = 0.2 + + # Include assertion object + context.assertion = dummy() + + # Cleanup existing data before any test + cleanup() + + context.app_class = App('evolution') + + except Exception as e: + print("Error in before_all: %s" % e.message) + + +def after_scenario(context, scenario): + """Teardown for each scenario + Kill evolution (in order to make this reliable we send sigkill) + """ + try: + # Stop evolution + context.app_class.kill() + + # Make some pause after scenario + sleep(1) + except Exception as e: + # Stupid behave simply crashes in case exception has occurred + print("Error in after_scenario: %s" % e.message) diff --git a/tests/shortcuts.feature b/tests/shortcuts.feature new file mode 100644 index 0000000000..0f9989a06a --- /dev/null +++ b/tests/shortcuts.feature @@ -0,0 +1,135 @@ +Feature: Shortcuts + + Background: + * Open Evolution and setup fake account + + @general_shortcuts + Scenario: Ctrl-Q to quit application - two instances + * Start a new Evolution instance + * Press "<Control>Q" + Then Evolution is closed + + @general_shortcuts + Scenario: F1 to launch help + * Press "<F1>" + Then Help section "Evolution Mail and Calendar" is displayed + + @general_shortcuts + Scenario: Shift-Ctrl-W to open a new window + * Press "<Control><Shift>W" + Then Evolution has 2 windows opened + + @general_shortcuts + Scenario: Ctrl-W to close a window + * Press "<Control><Shift>W" + * Press "<Control>W" + Then Evolution has 1 window opened + + @general_shortcuts + Scenario: Ctrl-Shift-S to open Preferences + * Press "<Control><Shift>S" + Then Preferences dialog is opened + + @mail_shortcuts + Scenario: Mail: Ctrl-Shift-M to compose new message + * Open "Mail" section + * Press "<Control><Shift>M" + Then Message composer with title "Compose Message" is opened + + @contacts_shortcuts + Scenario: Contacts: Ctrl-Shift-C to create new contact + * Open "Contacts" section + * Press "<Control><Shift>C" + Then Contact editor window is opened + + @contacts_shortcuts + Scenario: Contacts: Ctrl-Shift-L to create new contact list + * Open "Contacts" section + * Press "<Control><Shift>L" + Then Contact List editor window is opened + + @calendar_shortcuts + Scenario: Calendar: Ctrl-Shift-A to create new appointment + * Open "Calendar" section + * Press "<Control><Shift>A" + Then Event editor with title "Appointment - No Summary" is displayed + + @calendar_shortcuts + Scenario: Calendar: Ctrl-Shift-E to create new meeting + * Open "Calendar" section + * Press "<Control><Shift>E" + Then Event editor with title "Meeting - No Summary" is displayed + + @calendar_shortcuts + Scenario: Tasks: Ctrl-Shift-T to create new task + * Open "Tasks" section + * Press "<Control><Shift>T" + Then Task editor with title "Task - No Summary" is opened + + @memos_shortcuts + Scenario: Memos: Ctrl-Shift-O to create new memo + * Open "Memos" section + * Press "<Control><Shift>O" + Then Memo editor with title "Memo - No Summary" is opened + + @memos_shortcuts + Scenario: Memos: Ctrl-Shift-O to create new task + * Open "Memos" section + * Press "<Control><Shift>O" + Then Shared memo editor with title "Memo - No Summary" is opened + + @view_shortcuts + Scenario Outline: Ctrl+<1-5> to switch views + * Press "<shortcut>" + Then "<section>" view is opened + + Examples: + | shortcut | section | + | <Ctrl>1 | Mail | + | <Ctrl>2 | Contacts | + | <Ctrl>3 | Calendar | + | <Ctrl>4 | Tasks | + | <Ctrl>5 | Memos | + + @menu_shortcuts + Scenario Outline: Menu shortcuts on all views + * Open "<section>" section + * Press "<shortcut>" + Then "<menu>" menu is opened + + Examples: + | section | shortcut | menu | + | Mail | <Alt>F | File | + | Mail | <Alt>E | Edit | + | Mail | <Alt>V | View | + | Mail | <Alt>O | Folder | + | Mail | <Alt>M | Message | + | Mail | <Alt>S | Search | + | Mail | <Alt>H | Help | + + | Contacts | <Alt>F | File | + | Contacts | <Alt>E | Edit | + | Contacts | <Alt>V | View | + | Contacts | <Alt>A | Actions | + | Contacts | <Alt>S | Search | + | Contacts | <Alt>H | Help | + + | Calendar | <Alt>F | File | + | Calendar | <Alt>E | Edit | + | Calendar | <Alt>V | View | + | Calendar | <Alt>A | Actions | + | Calendar | <Alt>S | Search | + | Calendar | <Alt>H | Help | + + | Tasks | <Alt>F | File | + | Tasks | <Alt>E | Edit | + | Tasks | <Alt>V | View | + | Tasks | <Alt>A | Actions | + | Tasks | <Alt>S | Search | + | Tasks | <Alt>H | Help | + + | Memos | <Alt>F | File | + | Memos | <Alt>E | Edit | + | Memos | <Alt>V | View | + | Memos | <Alt>S | Search | + | Memos | <Alt>H | Help | diff --git a/tests/steps/initial_setup_steps.py b/tests/steps/initial_setup_steps.py new file mode 100644 index 0000000000..ec97e455e4 --- /dev/null +++ b/tests/steps/initial_setup_steps.py @@ -0,0 +1,138 @@ +# -*- coding: UTF-8 -*- +from behave import step + +from common_steps import wait_until, check_for_errors +from dogtail.tree import root +from os import system +from pyatspi import STATE_SENSITIVE +from time import sleep + + +@step(u'Open Evolution and setup fake account') +def open_evolution_and_setup_fake_account(context): + system("evolution --force-shutdown 2&> /dev/null") + context.execute_steps(u'* Start a new Evolution instance') + window = context.app.child(roleName='frame') + if window.name == 'Evolution Account Assistant': + context.execute_steps(u""" + * Complete Welcome dialog in Evolution Account Assistant + * Complete Restore from Backup dialog in Evolution Account Assistant + * Complete Identity dialog setting name to "GNOME QE User" and email address to "test@test" + * Wait for account is being looked up dialog in Evolution Account Assistant + * Complete Receiving Email dialog of Evolution Account Assistant setting + | Field | Value | + | Server Type: | None | + * Complete Sending Email dialog of Evolution Account Assistant setting + | Field | Value | + | Server Type: | Sendmail | + * Complete Account Summary in Evolution Account Assistant + * Complete Done dialog in Evolution Account Assistant + """) + # Evo doesn't create default addressbook immidiately + # We should restart it + system("evolution --force-shutdown 2&> /dev/null") + context.execute_steps(u'* Start a new Evolution instance') + + +@step(u'Complete Receiving Options in Evolution Account Assistant') +@step(u'Complete Account Summary in Evolution Account Assistant') +@step(u'Complete Restore from Backup dialog in Evolution Account Assistant') +@step(u'Complete Welcome dialog in Evolution Account Assistant') +def evo_account_assistant_dummy_dialogs(context): + # nothing to do here, skip it + window = context.app.child('Evolution Account Assistant') + click_continue(window) + + +@step(u'Complete Identity dialog setting name to "{name}" and email address to "{email}"') +def evo_account_assistant_identity_dialog(context, name, email): + # nothing to do here, skip it + window = context.app.child('Evolution Account Assistant') + window.childLabelled("Full Name:").text = name + window.childLabelled("Email Address:").text = email + click_continue(window) + + +@step(u"Wait for account is being looked up dialog in Evolution Account Assistant") +def wait_for_account_to_be_looked_up(context): + window = context.app.child('Evolution Account Assistant') + skip_lookup = window.findChildren(lambda x: x.name == 'Skip Lookup') + visible_skip_lookup = [x for x in skip_lookup if x.showing] + if len(visible_skip_lookup) > 0: + visible_skip_lookup = visible_skip_lookup[0] + assert wait_until(lambda x: not x.showing, visible_skip_lookup),\ + "Skip Lookup button didn't dissappear" + + +def click_continue(window): + # As initial wizard dialog creates a bunch of 'Continue' buttons + # We have to click to the visible and enabled one + button = None + for attempt in xrange(0, 10): + btns = window.findChildren(lambda x: x.name == 'Continue') + visible_and_enabled = [x for x in btns if x.showing and STATE_SENSITIVE in x.getState().getStates()] + if visible_and_enabled == []: + sleep(0.1) + continue + else: + button = visible_and_enabled[0] + break + button.click() + + +@step(u'Complete {sending_or_receiving} Email dialog of Evolution Account Assistant setting') +def evo_account_assistant_receiving_email_dialog_from_table(context, sending_or_receiving): + window = context.app.child('Evolution Account Assistant') + for row in context.table: + label = str(row['Field']) + value = str(row['Value']) + filler = window.child(roleName='filler', name='%s Email' % sending_or_receiving) + widgets = filler.findChildren(lambda x: x.showing) + visible_widgets = [x for x in widgets if x.labeller and x.labeller.name == label] + if len(visible_widgets) == 0: + raise RuntimeError("Cannot find visible widget labelled '%s'" % label) + widget = visible_widgets[0] + if widget.roleName == 'combo box': + if label != 'Port:': + widget.click() + widget.menuItem(value).click() + else: + # Port is a combobox, but you can type your port there + widget.textentry('').text = value + widget.textentry('').grab_focus() + widget.textentry('').keyCombo("<Enter>") + if widget.roleName == 'text': + widget.text = value + + # Check for password here and accept self-generated certificate (if appears) + btns = window.findChildren(lambda x: x.name == 'Check for Supported Types') + visible_btns = [w for w in btns if w.showing] + if visible_btns == []: + click_continue(window) + return + visible_btns[0].click() + + # Confirm all certificates by clicking 'Accept Permanently' until dialog is visible + apps = [x.name for x in root.applications()] + if 'evolution-user-prompter' in apps: + prompter = root.application('evolution-user-prompter') + dialog = prompter.child(roleName='dialog') + while dialog.showing: + if prompter.findChild(lambda x: x.name == 'Accept Permanently', retry=False, requireResult=False): + prompter.button('Accept Permanently').click() + else: + sleep(0.1) + + # Wait until Cancel button disappears + cancel = filler.findChildren(lambda x: x.name == 'Cancel')[0] + while cancel.showing: + sleep(0.1) + check_for_errors(context) + click_continue(window) + + +@step(u'Complete Done dialog in Evolution Account Assistant') +def evo_account_assistant_done_dialog(context): + # nothing to do here, skip it + window = context.app.child('Evolution Account Assistant') + window.button('Apply').click() diff --git a/tests/steps/steps.py b/tests/steps/steps.py new file mode 100644 index 0000000000..dac984f5dc --- /dev/null +++ b/tests/steps/steps.py @@ -0,0 +1,122 @@ +# -*- coding: UTF-8 -*- +from behave import step, then +from common_steps import wait_until +from dogtail.tree import root +from dogtail.rawinput import keyCombo +from time import sleep +from os import system + + +@step(u'Help section "{name}" is displayed') +def help_is_displayed(context, name): + try: + context.yelp = root.application('yelp') + frame = context.yelp.child(roleName='frame') + wait_until(lambda x: x.showing, frame) + sleep(1) + context.assertion.assertEquals(name, frame.name) + finally: + system("killall yelp") + + +@step(u'Evolution has {num:d} window opened') +@step(u'Evolution has {num:d} windows opened') +def evolution_has_num_windows_opened(context, num): + windows = context.app.findChildren(lambda x: x.roleName == 'frame') + context.assertion.assertEqual(len(windows), num) + + +@step(u'Preferences dialog is opened') +def preferences_dialog_opened(context): + context.app.window('Evolution Preferences') + + +@step(u'"{name}" view is opened') +def view_is_opened(context, name): + if name != 'Mail': + window_name = context.app.children[0].name + context.assertion.assertEquals(window_name, "%s - Evolution" % name) + else: + # A special case for Mail + context.assertion.assertTrue(context.app.menu('Message').showing) + + +@step(u'Open "{section_name}" section') +def open_section_by_name(context, section_name): + context.app.menu('View').click() + context.app.menu('View').menu('Window').point() + context.app.menu('View').menu('Window').menuItem(section_name).click() + + +@step(u'"{name}" menu is opened') +def menu_is_opened(context, name): + sleep(0.5) + menu = context.app.menu(name) + children_displayed = [x.showing for x in menu.children] + context.assertion.assertTrue(True in children_displayed, "Menu '%s' is not opened" % name) + + +@step(u'Press "{sequence}"') +def press_button_sequence(context, sequence): + keyCombo(sequence) + sleep(0.5) + + +@then(u'Evolution is closed') +def evolution_is_closed(context): + assert wait_until(lambda x: x.dead, context.app),\ + "Evolution window is opened" + context.assertion.assertFalse(context.app_class.isRunning(), "Evolution is in the process list") + + +@step(u'Message composer with title "{name}" is opened') +def message_composer_is_opened(context, name): + context.app.composer = context.app.window(name) + + +@then(u'Contact editor window with title "{title}" is opened') +def contact_editor_with_label_is_opened(context, title): + context.app.contact_editor = context.app.dialog(title) + context.assertion.assertIsNotNone( + context.app.contact_editor, "Contact Editor was not found") + context.assertion.assertTrue( + context.app.contact_editor.showing, "Contact Editor didn't appear") + + +@then(u'Contact editor window is opened') +def contact_editor_is_opened(context): + context.execute_steps(u'Then Contact editor window with title "Contact Editor" is opened') + + +@then(u'Contact List editor window is opened') +def contact_list_editor_is_opened(context): + context.execute_steps( + u'Then Contact List editor window with title "Contact List Editor" is opened') + + +@then(u'Contact List editor window with title "{name}" is opened') +def contact_list_editor__with_name_is_opened(context, name): + context.app.contact_list_editor = context.app.dialog(name) + + +@step(u'Memo editor with title "{name}" is opened') +def memo_editor_is_opened(context, name): + context.execute_steps(u'* Task editor with title "%s" is opened' % name) + + +@step(u'Shared Memo editor with title "{name}" is opened') +def shared_memo_editor_is_opened(context, name): + context.execute_steps(u'* Task editor with title "%s" is opened' % name) + + +@step(u'Task editor with title "{title}" is opened') +def task_editor_with_title_is_opened(context, title): + context.app.task_editor = context.app.window(title) + # Spoof event_editor for assigned tasks + if 'Assigned' in title: + context.app.event_editor = context.app.task_editor + + +@step(u'Event editor with title "{name}" is displayed') +def event_editor_with_name_displayed(context, name): + context.app.event_editor = context.app.window(name)
\ No newline at end of file |