Hi Dave, Here is a new patch which includes the following: - randomized ports - delete the acceptance_test_db database in setup in case a prior run failed - fixed size browser window
Cheers, Tira & George On Tue, Jan 31, 2017 at 11:25 AM, Dave Page <dp...@pgadmin.org> wrote: > Hi George, > > I just tried to do some debugging of pgAdmin, and found that I > couldn't start it. On further investigation, I found that I had an > instance running in the background on my system. I'm assuming this was > started by the acceptance tests, but not shutdown. I killed it off, > and re-ran the tests only to see failures because the database and > table used in the acceptance tests were still present. When the tests > completed, pgAdmin was again left running in the background. > > I've just re-run the tests, having first killed the backgrounded > pgAdmin and then manually cleaned up the test objects. This time I do > indeed only get the two errors below when it tests the first of 3 > servers I have configured. The second and third servers get three > errors each, and pgAdmin is left running in the background again. > > So, you were right that I had another instance of pgAdmin running... > but it was tests that caused it :-p > > > > On Tue, Jan 31, 2017 at 3:10 PM, Dave Page <dp...@pgadmin.org> wrote: > > Hi > > > > On Tue, Jan 31, 2017 at 2:54 PM, George Gelashvili > > <ggelashv...@pivotal.io> wrote: > >> Hi Dave, > >> > >> We agree that a random port would be a nice addition. We think having > >> randomized test database names can lead to polluting with lots of extra > >> databases left around in the event that cleanup fails for whatever > reason > >> (e.g. a test errors out). We see this happen already with the > randomized > >> test databases you mention. We agree that there should probably be one > >> strategy across the test suite. We could use randomized names and have a > >> more general cleanup step that removes all databases of the form > "test_...". > > > > I'm very wary about doing things like that. We had an early version of > > the suite that managed to delete all databases :-/. Maybe we could use > > a patterned name, but only delete databases that also have a comment > > with some text in it that we can verify? > > > >> Dave, are those errors you saw when you shut down your application on > :5050 > >> and did a fresh run of the tests? If not, could you please do a clean > run? > >> It's possible that the second error could be related to viewport size > as you > >> suggested, but the first error just looks like a problem with the test > not > >> being able to spin up its own server. > > > > That was on a second run of the tests, yes. I just did a careful > > cleanup of left-over test databases, double-checked my server wasn't > > running and re-ran the tests - I got the same results. > > > >> > >> Thanks, > >> George & Tira > >> > >> On Tue, Jan 31, 2017 at 9:41 AM, Dave Page <dp...@pgadmin.org> wrote: > >>> > >>> Hi > >>> > >>> On Mon, Jan 30, 2017 at 9:23 PM, Atira Odhner <aodh...@pivotal.io> > wrote: > >>> > Here's the patch with one more fix -- cleaning up the connections > that > >>> > get > >>> > created in pgAdmin. > >>> > >>> Hmm, I had trouble with this one. I noticed a few issues: > >>> > >>> - The tests started pgAdmin listening on the default port (5050), > >>> however, I already had an instance running on there; > >>> a) It should have detected that something else was running on the > port > >>> b) Shouldn't we just use a random, unused port? > >>> > >>> - Errors were given because I already had an acceptance_test_db on a > >>> number of servers, and that contained the test table. Obviously the > >>> code now cleans up after itself, but I think we should use a random > >>> database name as the main regression tests do (they append a random > >>> number to the name iirc). > >>> > >>> - Some of the tests just seemed to time out. I *think* this might be > >>> because the test browser window opens quite narrowly, and it looks > >>> like the tests are probably trying to do things with nodes that aren't > >>> actually visible. > >>> > >>> ====================================================================== > >>> ERROR: runTest > >>> (pgadmin.acceptance.tests.connect_to_server_feature_test. > ConnectsToServerFeatureTest) > >>> ---------------------------------------------------------------------- > >>> Traceback (most recent call last): > >>> File > >>> "/Users/dpage/git/pgadmin4/web/pgadmin/acceptance/tests/ > connect_to_server_feature_test.py", > >>> line 69, in tearDown > >>> self.app_starter.stop_app() > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/app_ > starter.py", > >>> line 27, in stop_app > >>> os.killpg(os.getpgid(self.pgadmin_process.pid), signal.SIGTERM) > >>> OSError: [Errno 3] No such process > >>> > >>> ====================================================================== > >>> ERROR: runTest > >>> (pgadmin.acceptance.tests.sql_template_selection_by_ > postgres_version_works_feature_test.SQLTemplateSelectionByPostgres > VersionWorksFeatureTest) > >>> ---------------------------------------------------------------------- > >>> Traceback (most recent call last): > >>> File > >>> "/Users/dpage/git/pgadmin4/web/pgadmin/acceptance/tests/ > sql_template_selection_by_postgres_version_works_feature_test.py", > >>> line 37, in runTest > >>> self.page.find_by_xpath("//*[@id='tree']//*[@class='aciTreeText' > >>> and .='Trigger Functions']").click() > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/pgadmin_ > page.py", > >>> line 45, in find_by_xpath > >>> return self.wait_for_element(lambda: > >>> self.driver.find_element_by_xpath(xpath)) > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/pgadmin_ > page.py", > >>> line 72, in wait_for_element > >>> return self._wait_for("element to exist", element_if_it_exists) > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/pgadmin_ > page.py", > >>> line 106, in _wait_for > >>> raise RuntimeError("timed out waiting for " + waiting_for_message) > >>> RuntimeError: timed out waiting for element to exist > >>> > >>> ====================================================================== > >>> ERROR: runTest > >>> (pgadmin.acceptance.tests.sql_template_selection_by_ > postgres_version_works_feature_test.SQLTemplateSelectionByPostgres > VersionWorksFeatureTest) > >>> ---------------------------------------------------------------------- > >>> Traceback (most recent call last): > >>> File > >>> "/Users/dpage/git/pgadmin4/web/pgadmin/acceptance/tests/ > sql_template_selection_by_postgres_version_works_feature_test.py", > >>> line 60, in tearDown > >>> self.page.find_by_xpath("//button[contains(.,'Cancel')]").click() > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/pgadmin_ > page.py", > >>> line 45, in find_by_xpath > >>> return self.wait_for_element(lambda: > >>> self.driver.find_element_by_xpath(xpath)) > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/pgadmin_ > page.py", > >>> line 72, in wait_for_element > >>> return self._wait_for("element to exist", element_if_it_exists) > >>> File "/Users/dpage/git/pgadmin4/web/regression/utils/pgadmin_ > page.py", > >>> line 106, in _wait_for > >>> raise RuntimeError("timed out waiting for " + waiting_for_message) > >>> RuntimeError: timed out waiting for element to exist > >>> > >>> ---------------------------------------------------------------------- > >>> > >>> Thanks. > >>> > >>> -- > >>> Dave Page > >>> Blog: http://pgsnake.blogspot.com > >>> Twitter: @pgsnake > >>> > >>> EnterpriseDB UK: http://www.enterprisedb.com > >>> The Enterprise PostgreSQL Company > >> > >> > > > > > > > > -- > > Dave Page > > Blog: http://pgsnake.blogspot.com > > Twitter: @pgsnake > > > > EnterpriseDB UK: http://www.enterprisedb.com > > The Enterprise PostgreSQL Company > > > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company >
diff --git a/requirements_py2.txt b/requirements_py2.txt index 4fb05891..998cdabf 100644 --- a/requirements_py2.txt +++ b/requirements_py2.txt @@ -36,6 +36,7 @@ testscenarios==0.5.0 testtools==2.0.0 traceback2==1.4.0 unittest2==1.1.0 +selenium==3.0.2 Werkzeug==0.9.6 WTForms==2.0.2 sqlparse==0.1.19 diff --git a/requirements_py3.txt b/requirements_py3.txt index c4490f52..2239de63 100644 --- a/requirements_py3.txt +++ b/requirements_py3.txt @@ -35,6 +35,7 @@ testscenarios==0.5.0 testtools==2.0.0 traceback2==1.4.0 unittest2==1.1.0 +selenium==3.0.2 Werkzeug==0.9.6 WTForms==2.0.2 sqlparse==0.1.19 diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py index 68848c00..95e675b7 100644 --- a/web/pgAdmin4.py +++ b/web/pgAdmin4.py @@ -59,6 +59,12 @@ if 'PGADMIN_PORT' in globals(): globals()['PGADMIN_PORT']) server_port = int(globals()['PGADMIN_PORT']) PGADMIN_RUNTIME = True +elif 'PGADMIN_PORT' in os.environ: + port = os.environ['PGADMIN_PORT'] + app.logger.debug( + 'Not running under the desktop runtime, port: %s', + port) + server_port = int(port) else: app.logger.debug( 'Not running under the desktop runtime, port: %s', diff --git a/web/pgadmin/acceptance/__init__.py b/web/pgadmin/acceptance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web/pgadmin/acceptance/tests/__init__.py b/web/pgadmin/acceptance/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web/pgadmin/acceptance/tests/connect_to_server_feature_test.py b/web/pgadmin/acceptance/tests/connect_to_server_feature_test.py new file mode 100644 index 00000000..b54696f6 --- /dev/null +++ b/web/pgadmin/acceptance/tests/connect_to_server_feature_test.py @@ -0,0 +1,73 @@ +############################################################# +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2017, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +############################################################## + +import time + +from selenium import webdriver +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver import ActionChains + +from pgadmin.utils.route import BaseTestGenerator + +import subprocess +import os +import signal +import config as app_config +from regression import test_utils +from regression.utils.app_starter import AppStarter +from regression.utils.pgadmin_page import PgadminPage + + +class ConnectsToServerFeatureTest(BaseTestGenerator): + """ + Tests that a database connection can be created from the UI + """ + + def setUp(self): + if app_config.SERVER_MODE: + self.skipTest("Currently, config is set to start pgadmin in server mode. " + "This test doesn't know username and password so doesn't work in server mode") + + driver = webdriver.Chrome() + self.app_starter = AppStarter(driver, app_config) + self.page = PgadminPage(driver, app_config) + + self.app_starter.start_app() + self.page.wait_for_app() + + + def runTest(self): + self.assertEqual(app_config.APP_NAME, self.page.driver.title) + self.page.wait_for_spinner_to_disappear() + + self.page.find_by_xpath("//*[@class='aciTreeText' and .='Servers']").click() + self.page.driver.find_element_by_link_text("Object").click() + ActionChains(self.page.driver) \ + .move_to_element(self.page.driver.find_element_by_link_text("Create")) \ + .perform() + self.page.find_by_partial_link_text("Server...").click() + + server_config = self.server + self.page.fill_input_by_field_name("name", server_config['name']) + self.page.find_by_partial_link_text("Connection").click() + self.page.fill_input_by_field_name("host", server_config['host']) + self.page.fill_input_by_field_name("port", server_config['port']) + self.page.fill_input_by_field_name("username", server_config['username']) + self.page.fill_input_by_field_name("password", server_config['db_password']) + self.page.find_by_xpath("//button[contains(.,'Save')]").click() + + self.page.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "']") + + def tearDown(self): + self.page.remove_server(self.server) + self.app_starter.stop_app() + + def failureException(self, *args, **kwargs): + self.page.driver.save_screenshot('/tmp/pgadmin_connect_to_server_test_failure.png') + return AssertionError(*args, **kwargs) diff --git a/web/pgadmin/acceptance/tests/sql_template_selection_by_postgres_version_works_feature_test.py b/web/pgadmin/acceptance/tests/sql_template_selection_by_postgres_version_works_feature_test.py new file mode 100644 index 00000000..65b96eae --- /dev/null +++ b/web/pgadmin/acceptance/tests/sql_template_selection_by_postgres_version_works_feature_test.py @@ -0,0 +1,78 @@ +from selenium import webdriver +from selenium.webdriver import ActionChains + +import config as app_config +from pgadmin.utils.route import BaseTestGenerator +from regression import test_utils +from regression.utils.app_starter import AppStarter +from regression.utils.pgadmin_page import PgadminPage + + +class SQLTemplateSelectionByPostgresVersionWorksFeatureTest(BaseTestGenerator): + def setUp(self): + if app_config.SERVER_MODE: + self.skipTest("Currently, config is set to start pgadmin in server mode. " + "This test doesn't know username and password so doesn't work in server mode") + + driver = webdriver.Chrome() + self.app_starter = AppStarter(driver, app_config) + self.page = PgadminPage(driver, app_config) + + connection = test_utils.get_db_connection(self.server['db'], + self.server['username'], + self.server['db_password'], + self.server['host'], + self.server['port']) + test_utils.drop_database(connection, "acceptance_test_db") + + test_utils.create_database(self.server, "acceptance_test_db") + + self.app_starter.start_app() + self.page.wait_for_app() + + self.page.add_server(self.server) + + def runTest(self): + test_utils.create_table(self.server, "acceptance_test_db", "test_table") + + self.page.toggle_open_tree_item(self.server['name']) + self.page.toggle_open_tree_item('Databases') + self.page.toggle_open_tree_item('acceptance_test_db') + self.page.toggle_open_tree_item('Schemas') + self.page.toggle_open_tree_item('public') + self.page.find_by_xpath("//*[@id='tree']//*[@class='aciTreeText' and .='Trigger Functions']").click() + self.page.find_by_partial_link_text("Object").click() + ActionChains(self.page.driver) \ + .move_to_element(self.page.driver.find_element_by_link_text("Create")) \ + .perform() + self.page.find_by_partial_link_text("Trigger function...").click() + self.page.fill_input_by_field_name("name", "test-trigger-function") + self.page.find_by_partial_link_text("Definition").click() + self.page.fill_codemirror_area_with( +"""CREATE OR REPLACE FUNCTION log_last_name_changes() +RETURNS TRIGGER AS +$BODY$ +BEGIN + +END; +$BODY$ +""" + ) + self.page.find_by_partial_link_text("SQL").click() + + self.page.find_by_xpath("//*[contains(@class,'CodeMirror-lines') and contains(.,'LEAKPROOF')]") + + def tearDown(self): + self.page.find_by_xpath("//button[contains(.,'Cancel')]").click() + self.page.remove_server(self.server) + self.app_starter.stop_app() + connection = test_utils.get_db_connection(self.server['db'], + self.server['username'], + self.server['db_password'], + self.server['host'], + self.server['port']) + test_utils.drop_database(connection, "acceptance_test_db") + + def failureException(self, *args, **kwargs): + self.page.driver.save_screenshot('/tmp/pgadmin_sql_template_selection_failure.png') + return AssertionError(*args, **kwargs) diff --git a/web/pgadmin/utils/route.py b/web/pgadmin/utils/route.py index f18d2c18..996892a6 100644 --- a/web/pgadmin/utils/route.py +++ b/web/pgadmin/utils/route.py @@ -54,27 +54,23 @@ class TestsGeneratorRegistry(ABCMeta): ABCMeta.__init__(cls, name, bases, d) @classmethod - def load_generators(cls, pkg): + def load_generators(cls, pkg_root, exclude_pkgs): cls.registry = dict() + all_modules = [] + + all_modules += find_modules(pkg_root, False, True) + # Check for SERVER mode - if config.SERVER_MODE: - for module_name in find_modules(pkg, False, True): - try: - if "tests." in str(module_name): - import_module(module_name) - except ImportError: - traceback.print_exc(file=sys.stderr) - else: - for module_name in find_modules(pkg, False, True): - try: - # Exclude the test cases in browser node if SERVER_MODE - # is False - if "pgadmin.browser.tests" not in module_name: - import_module(module_name) - except ImportError: - traceback.print_exc(file=sys.stderr) + for module_name in all_modules: + try: + if "tests." in str(module_name) and not any( + str(module_name).startswith('pgadmin.' + str(exclude_pkg)) for exclude_pkg in exclude_pkgs + ): + import_module(module_name) + except ImportError: + traceback.print_exc(file=sys.stderr) import six diff --git a/web/regression/.gitignore b/web/regression/.gitignore index 0581810b..723fce7e 100644 --- a/web/regression/.gitignore +++ b/web/regression/.gitignore @@ -1,4 +1,5 @@ parent_id.pkl regression.log +test_greenplum_config.json test_advanced_config.json test_config.json diff --git a/web/regression/README b/web/regression/README index 8cc29987..5b077d81 100644 --- a/web/regression/README +++ b/web/regression/README @@ -103,6 +103,10 @@ Test Data Details Execution: ----------- +- For acceptance tests to run as part of the entire test suite, Chrome and chromedriver need to be installed: + get chromedriver from https://sites.google.com/a/chromium.org/chromedriver/downloads or a package manager + and make sure it is on the PATH + - The test framework is modular and pluggable and dynamically locates tests for modules which are discovered at runtime. All test cases are found and registered automatically by its module name in @@ -122,3 +126,8 @@ Execution: Example 2) Run test framework for 'database' node run 'python runtests.py --pkg browser.server_groups.servers.databases' + +- Exclude a package and its subpackages when running tests: + + Example: exclude acceptance tests but run all others: + run 'python runtests.py --exclude acceptance' diff --git a/web/regression/runtests.py b/web/regression/runtests.py index 272e3802..7b1bf543 100644 --- a/web/regression/runtests.py +++ b/web/regression/runtests.py @@ -138,12 +138,20 @@ def get_test_modules(arguments): from pgadmin.utils.route import TestsGeneratorRegistry + exclude_pkgs = [] + + if not config.SERVER_MODE: + exclude_pkgs.append("browser.tests") + if 'exclude' in arguments: + exclude_pkgs.append(arguments['exclude']) + # Load the test modules which are in given package(i.e. in arguments.pkg) if arguments['pkg'] is None or arguments['pkg'] == "all": - TestsGeneratorRegistry.load_generators('pgadmin') + TestsGeneratorRegistry.load_generators('pgadmin', exclude_pkgs) else: TestsGeneratorRegistry.load_generators('pgadmin.%s.tests' % - arguments['pkg']) + arguments['pkg'], + exclude_pkgs) # Sort module list so that test suite executes the test cases sequentially module_list = TestsGeneratorRegistry.registry.items() @@ -164,6 +172,8 @@ def add_arguments(): parser = argparse.ArgumentParser(description='Test suite for pgAdmin4') parser.add_argument('--pkg', help='Executes the test cases of particular' ' package') + parser.add_argument('--exclude', help='Skips execution of the test ' + 'cases of particular package') arg = parser.parse_args() return arg diff --git a/web/regression/test_utils.py b/web/regression/test_utils.py index 1f9f0522..68f36cbc 100644 --- a/web/regression/test_utils.py +++ b/web/regression/test_utils.py @@ -134,6 +134,24 @@ def create_database(server, db_name): traceback.print_exc(file=sys.stderr) +def create_table(server, db_name, table_name): + try: + connection = get_db_connection(db_name, + server['username'], + server['db_password'], + server['host'], + server['port']) + old_isolation_level = connection.isolation_level + connection.set_isolation_level(0) + pg_cursor = connection.cursor() + pg_cursor.execute('''CREATE TABLE "%s" (name VARCHAR, value NUMERIC)''' % table_name) + pg_cursor.execute('''INSERT INTO "%s" VALUES ('Some-Name', 6)''' % table_name) + connection.set_isolation_level(old_isolation_level) + connection.commit() + + except Exception: + traceback.print_exc(file=sys.stderr) + def drop_database(connection, database_name): """This function used to drop the database""" if database_name not in ["postgres", "template1", "template0"]: diff --git a/web/regression/utils/app_starter.py b/web/regression/utils/app_starter.py new file mode 100644 index 00000000..b297bd6d --- /dev/null +++ b/web/regression/utils/app_starter.py @@ -0,0 +1,34 @@ +import os +import subprocess + +import signal + +import random + +class AppStarter: + """ + Helper for starting the full pgadmin4 app and loading the page via selenium + """ + + def __init__(self, driver, app_config): + self.driver = driver + self.app_config = app_config + + def start_app(self): + random_server_port = str(random.randint(10000, 65535)) + env = {"PGADMIN_PORT": random_server_port} + env.update(os.environ) + + self.pgadmin_process = subprocess.Popen(["python", "pgAdmin4.py", "magic-portal", random_server_port], + shell=False, + preexec_fn=os.setsid, + stderr=open(os.devnull, 'w'), + env=env) + + self.driver.set_window_size(1024, 1024) + print("opening browser") + self.driver.get("http://" + self.app_config.DEFAULT_SERVER + ":" + random_server_port) + + def stop_app(self): + self.driver.close() + os.killpg(os.getpgid(self.pgadmin_process.pid), signal.SIGTERM) diff --git a/web/regression/utils/pgadmin_page.py b/web/regression/utils/pgadmin_page.py new file mode 100644 index 00000000..4e334e80 --- /dev/null +++ b/web/regression/utils/pgadmin_page.py @@ -0,0 +1,106 @@ +import time +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver import ActionChains + + +class PgadminPage: + """ + Helper class for interacting with the page, given a selenium driver + """ + def __init__(self, driver, app_config): + self.driver = driver + self.app_config = app_config + + def add_server(self, server_config): + self.wait_for_spinner_to_disappear() + + self.find_by_xpath("//*[@class='aciTreeText' and contains(.,'Servers')]").click() + self.driver.find_element_by_link_text("Object").click() + ActionChains(self.driver) \ + .move_to_element(self.driver.find_element_by_link_text("Create")) \ + .perform() + self.find_by_partial_link_text("Server...").click() + + self.fill_input_by_field_name("name", server_config['name']) + self.find_by_partial_link_text("Connection").click() + self.fill_input_by_field_name("host", server_config['host']) + self.fill_input_by_field_name("port", server_config['port']) + self.fill_input_by_field_name("username", server_config['username']) + self.fill_input_by_field_name("password", server_config['db_password']) + self.find_by_xpath("//button[contains(.,'Save')]").click() + + self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "']") + + def remove_server(self, server_config): + self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']").click() + self.find_by_partial_link_text("Object").click() + self.find_by_partial_link_text("Delete/Drop").click() + time.sleep(0.5) + self.find_by_xpath("//button[contains(.,'OK')]").click() + + def toggle_open_tree_item(self, tree_item_text): + self.find_by_xpath("//*[@id='tree']//*[.='" + tree_item_text + "']/../*[@class='aciTreeButton']").click() + + def find_by_xpath(self, xpath): + return self.wait_for_element(lambda: self.driver.find_element_by_xpath(xpath)) + + def find_by_id(self, element_id): + return self.wait_for_element(lambda: self.driver.find_element_by_id(element_id)) + + def find_by_partial_link_text(self, link_text): + return self.wait_for_element(lambda: self.driver.find_element_by_partial_link_text(link_text)) + + def fill_input_by_field_name(self, field_name, field_content): + self.find_by_xpath("//input[@name='" + field_name + "']").clear() + self.find_by_xpath("//input[@name='" + field_name + "']").send_keys( + field_content) + + def fill_codemirror_area_with(self, field_content): + self.find_by_xpath( + "//pre[contains(@class,'CodeMirror-line')]/../../../*[contains(@class,'CodeMirror-code')]").click() + ActionChains(self.driver).send_keys(field_content).perform() + + def wait_for_element(self, find_method_with_args): + def element_if_it_exists(): + try: + element = find_method_with_args() + if element.is_displayed() & element.is_enabled(): + return element + except NoSuchElementException: + return False + + return self._wait_for("element to exist", element_if_it_exists) + + def wait_for_spinner_to_disappear(self): + def spinner_has_disappeared(): + try: + self.driver.find_element_by_id("pg-spinner") + return False + except NoSuchElementException: + return True + + self._wait_for("spinner to disappear", spinner_has_disappeared) + + def wait_for_app(self): + def page_shows_app(): + if self.driver.title == self.app_config.APP_NAME: + return True + else: + self.driver.refresh() + return False + + self._wait_for("app to start", page_shows_app) + + def _wait_for(self, waiting_for_message, condition_met_function): + timeout = 5 + time_waited = 0 + sleep_time = 0.01 + + while time_waited < timeout: + result = condition_met_function() + if result: + return result + time_waited += sleep_time + time.sleep(sleep_time) + + raise RuntimeError("timed out waiting for " + waiting_for_message)
-- Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers