diff --git a/requirements_py2.txt b/requirements_py2.txt index 51170a45..de167121 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 f68db7a8..9565a6e4 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/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/test_connects_to_database.py b/web/pgadmin/acceptance/tests/test_connects_to_database.py new file mode 100644 index 00000000..c35cd0fd --- /dev/null +++ b/web/pgadmin/acceptance/tests/test_connects_to_database.py @@ -0,0 +1,127 @@ +############################################################# +# +# 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 + + +class ConnectsToDatabase(BaseTestGenerator): + """ + Tests that a database connection can be created from the UI + """ + + def setUp(self): + if 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") + + self.pgadmin_process = subprocess.Popen(["python", "pgAdmin4.py"], shell=False, preexec_fn=os.setsid, + stderr=open(os.devnull, 'w')) + + self.driver = webdriver.Chrome() + self.server_config = self.server + + print("opening browser") + self.driver.get("http://" + config.DEFAULT_SERVER + ":" + str(config.DEFAULT_SERVER_PORT)) + self._wait_for_app() + + def runTest(self): + self.assertEqual(config.APP_NAME, self.driver.title) + self._wait_for_spinner_to_disappear() + + self._find_by_xpath("//*[@class='aciTreeText' and .='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_xpath("name", self.server_config['name']) + self._find_by_partial_link_text("Connection").click() + self._fill_input_by_xpath("host", self.server_config['host']) + self._fill_input_by_xpath("port", self.server_config['port']) + self._fill_input_by_xpath("username", self.server_config['username']) + self._fill_input_by_xpath("password", self.server_config['db_password']) + self._find_by_xpath("//button[contains(.,'Save')]").click() + + self._find_by_xpath("//*[@id='tree']//*[.='" + self.server_config['name'] + "']") + + def tearDown(self): + self.driver.close() + os.killpg(os.getpgid(self.pgadmin_process.pid), signal.SIGTERM) + + def failureException(self, *args, **kwargs): + self.driver.save_screenshot('/tmp/pgadmin_test_screenshot.png') + return AssertionError(*args, **kwargs) + + def _find_by_xpath(self, xpath): + return self._wait_for_element(lambda: self.driver.find_element_by_xpath(xpath)) + + 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_xpath(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 _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 == 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) + + self.fail("Timed out waiting for " + waiting_for_message) diff --git a/web/pgadmin/utils/route.py b/web/pgadmin/utils/route.py index f18d2c18..fed26a0f 100644 --- a/web/pgadmin/utils/route.py +++ b/web/pgadmin/utils/route.py @@ -54,20 +54,25 @@ class TestsGeneratorRegistry(ABCMeta): ABCMeta.__init__(cls, name, bases, d) @classmethod - def load_generators(cls, pkg): + def load_generators(cls, *pkgs): cls.registry = dict() + all_modules = [] + + for pkg in pkgs: + all_modules += find_modules(pkg, False, True) + + # Check for SERVER mode - if config.SERVER_MODE: - for module_name in find_modules(pkg, False, True): + for module_name in all_modules: + if config.SERVER_MODE: 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): + else: try: # Exclude the test cases in browser node if SERVER_MODE # is False 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..ae5d268d 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 diff --git a/web/regression/test_utils.py b/web/regression/test_utils.py index a2d2f5cd..1f9f0522 100644 --- a/web/regression/test_utils.py +++ b/web/regression/test_utils.py @@ -69,7 +69,7 @@ def get_config_data(): """This function reads the server data from config_data""" server_data = [] for srv in test_setup.config_data['server_credentials']: - if (not srv.has_key('enabled')) or srv['enabled'] == True: + if (not 'enabled' in srv) or srv['enabled']: data = {"name": srv['name'], "comment": srv['comment'], "host": srv['host'],