diff --git a/web/pgadmin/feature_tests/datatype_test.json b/web/pgadmin/feature_tests/datatype_test.json
new file mode 100644
index 000000000..8ded72896
--- /dev/null
+++ b/web/pgadmin/feature_tests/datatype_test.json
@@ -0,0 +1,173 @@
+[
+ {
+ "datatype": [
+ "smallint",
+ "smallint",
+ "integer",
+ "integer",
+ "bigint",
+ "bigint",
+ "decimal",
+ "decimal",
+ "numeric",
+ "numeric",
+ "float[]",
+ "float[]",
+ "real",
+ "real[]",
+ "bytea",
+ "bytea[]"
+ ],
+ "input":[
+ "-32767",
+ "32767",
+ "-2147483647",
+ "2147483647",
+ "-9223372036854775807",
+ "9223372036854775807",
+ "922337203685.4775807",
+ "92203685.477",
+ "922337203685.922337203685",
+ "-92233720368547758.08",
+ "ARRAY[1, 2, 3]",
+ "ARRAY['nan', 'nan', 'nan']",
+ "'Infinity'",
+ "'{Infinity}'",
+ "'E\\\\xDEADBEEF'",
+ "ARRAY['E\\\\xDEADBEEF', 'E\\\\xDEADBEEF']"
+ ],
+ "output":[
+ "-32767",
+ "32767",
+ "-2147483647",
+ "2147483647",
+ "-9223372036854775807",
+ "9223372036854775807",
+ "922337203685.4775807",
+ "92203685.477",
+ "922337203685.922337203685",
+ "-92233720368547758.08",
+ "{1,2,3}",
+ "{NaN,NaN,NaN}",
+ "Infinity",
+ "{Infinity}",
+ "[binary data]",
+ "[binary data[]]"
+ ]
+ },
+ {
+ "datatype": [
+ "int4range",
+ "int8range",
+ "numrange",
+ "daterange",
+ "tsrange",
+ "tstzrange",
+ "int4range[]",
+ "int8range[]",
+ "numrange[]",
+ "daterange[]",
+ "tsrange[]",
+ "tstzrange[]",
+ "int8range[]",
+ "daterange[]",
+ "tstzrange[]",
+ "",
+ ""
+ ],
+ "input":[
+ "'(1,2147483647)'",
+ "'(2,9223372036854775807)'",
+ "'(3,922337203685.922337203685]'",
+ "'(2010-01-01, 2010-02-01]'",
+ "'[2010-01-01 14:00, 2010-04-01 15:00)'",
+ "'[2010-01-01 14:00:00{tz}, 2010-02-01 15:00:00{tz})'",
+ "'{\"(1,2147483647)\", \"(2,2147483647)\"}'",
+ "'{\"(2,9223372036854775807)\", \"(2,9223372036854775807)\"}'",
+ "'{\"(3,922337203685.922337203685]\", \"(5,922337203685.922337203685]\"}'",
+ "'{\"(2010-01-01, 2010-02-01]\", \"(2010-01-01, 2010-02-01]\"}'",
+ "'{\"[2010-01-01 14:00, 2010-04-01 15:00)\", \"[2010-01-01 14:00, 2010-04-01 15:00)\"}'",
+ "'{{\"[2010-01-01 14:00:00{tz}, 2010-02-01 15:00:00{tz})\", \"[2017-01-12 14:00:00{tz}, 2017-02-28 15:00:00{tz})\"}}'",
+ "'{{\"(2,9223372036854775807)\", \"(2,9223372036854775807)\"},{\"(2,9223372036854775807)\", \"(2,9223372036854775807)\"}}'",
+ "'{{\"(2010-01-01, 2010-02-01]\", \"(2010-01-01, 2010-02-01]\"},{\"(2010-01-01, 2010-02-01]\", \"(2010-01-01, 2010-02-01]\"}}'",
+ "'{{{{\"[2010-01-01 14:00:00{tz}, 2010-02-01 15:00:00{tz})\", \"[2017-01-12 14:00:00{tz}, 2017-02-28 15:00:00{tz})\"}}, {{\"[2010-01-01 14:00:00{tz}, 2010-02-01 15:00:00{tz})\", \"[2017-01-12 14:00:00{tz}, 2017-02-28 15:00:00{tz})\"}}}}'",
+ "enum_range(NULL::rainbow)",
+ "ARRAY[enum_range(NULL::rainbow), enum_range(NULL::rainbow)]"
+ ],
+ "output":[
+ "[2,2147483647)",
+ "[3,9223372036854775807)",
+ "(3,922337203685.922337203685]",
+ "[2010-01-02,2010-02-02)",
+ "[\"2010-01-01 14:00:00\",\"2010-04-01 15:00:00\")",
+ "[\"2010-01-01 14:00:00{tz}\",\"2010-02-01 15:00:00{tz}\")",
+ "{\"[2,2147483647)\",\"[3,2147483647)\"}",
+ "{\"[3,9223372036854775807)\",\"[3,9223372036854775807)\"}",
+ "{\"(3,922337203685.922337203685]\",\"(5,922337203685.922337203685]\"}",
+ "{\"[2010-01-02,2010-02-02)\",\"[2010-01-02,2010-02-02)\"}",
+ "{\"[\\\"2010-01-01 14:00:00\\\",\\\"2010-04-01 15:00:00\\\")\",\"[\\\"2010-01-01 14:00:00\\\",\\\"2010-04-01 15:00:00\\\")\"}",
+ "{{\"[\\\"2010-01-01 14:00:00{tz}\\\",\\\"2010-02-01 15:00:00{tz}\\\")\",\"[\\\"2017-01-12 14:00:00{tz}\\\",\\\"2017-02-28 15:00:00{tz}\\\")\"}}",
+ "{{\"[3,9223372036854775807)\",\"[3,9223372036854775807)\"},{\"[3,9223372036854775807)\",\"[3,9223372036854775807)\"}}",
+ "{{\"[2010-01-02,2010-02-02)\",\"[2010-01-02,2010-02-02)\"},{\"[2010-01-02,2010-02-02)\",\"[2010-01-02,2010-02-02)\"}}",
+ "{{{{\"[\\\"2010-01-01 14:00:00{tz}\\\",\\\"2010-02-01 15:00:00{tz}\\\")\",\"[\\\"2017-01-12 14:00:00{tz}\\\",\\\"2017-02-28 15:00:00{tz}\\\")\"}},{{\"[\\\"2010-01-01 14:00:00{tz}\\\",\\\"2010-02-01 15:00:00{tz}\\\")\",\"[\\\"2017-01-12 14:00:00{tz}\\\",\\\"2017-02-28 15:00:00{tz}\\\")\"}}}}",
+ "{red,orange,yellow,green,blue,purple}",
+ "{{red,orange,yellow,green,blue,purple},{red,orange,yellow,green,blue,purple}}"
+ ]
+ },
+ {
+ "datatype": [
+ "inet",
+ "inet[]",
+ "inet[]",
+ "cidr",
+ "cidr[]",
+ "cidr[]",
+ "uuid",
+ "uuid[]",
+ "xml",
+ "xml[]",
+ "bit",
+ "bit[]",
+ "varbit",
+ "varbit[]",
+ "macaddr",
+ "macaddr[]"
+ ],
+ "input":[
+ "'::2'",
+ "'{\"::2\",\"192.168.1.1/16\",\"FFF0:0:007a::\"}'",
+ "'{{\"::2\",\"192.168.1.1/16\",\"FFF0:0:007a::\"},{\"::2\",\"192.168.1.1/16\",\"FFF0:0:007a::\"}}'",
+ "'::1'",
+ "'{\"::1\", \"192.168.100.128/25\", \"FFF0:0:007a::\"}'",
+ "'{{\"::1\", \"192.168.100.128/25\", \"FFF0:0:007a::\"},{\"::1\", \"192.168.100.128/25\", \"FFF0:0:007a::\"}}'",
+ "'e1ab7b6d-a62d-4bee-b0ce-b8488f83d89c'",
+ "'{55f8e502-e0b4-11e7-80c1-9a214cf093ae, e1ab7b6d-a62d-4bee-b0ce-b8488f83d89c}'",
+ "'pgAdmin 4'",
+ "'{\"pgAdmin 4\", \"pgAdmin 4\"}'",
+ "'1'",
+ "'{0,1}'",
+ "'1001'",
+ "'{10010,1011}'",
+ "'08:00:2b:01:02:03'",
+ "'{08:00:2b:01:02:03, 08-00-2b-01-02-03, 08002b:010203, 08002b-010203, 0800.2b01.0203, 0800-2b01-0203, 08002b010203}'"
+ ],
+ "output":[
+ "::2",
+ "{::2,192.168.1.1/16,fff0:0:7a::}",
+ "{{::2,192.168.1.1/16,fff0:0:7a::},{::2,192.168.1.1/16,fff0:0:7a::}}",
+ "::1/128",
+ "{::1/128,192.168.100.128/25,fff0:0:7a::/128}",
+ "{{::1/128,192.168.100.128/25,fff0:0:7a::/128},{::1/128,192.168.100.128/25,fff0:0:7a::/128}}",
+ "e1ab7b6d-a62d-4bee-b0ce-b8488f83d89c",
+ "{55f8e502-e0b4-11e7-80c1-9a214cf093ae,e1ab7b6d-a62d-4bee-b0ce-b8488f83d89c}",
+ "pgAdmin 4",
+ "{\"pgAdmin 4\",\"pgAdmin 4\"}",
+ "1",
+ "{0,1}",
+ "1001",
+ "{10010,1011}",
+ "08:00:2b:01:02:03",
+ "{08:00:2b:01:02:03,08:00:2b:01:02:03,08:00:2b:01:02:03,08:00:2b:01:02:03,08:00:2b:01:02:03,08:00:2b:01:02:03,08:00:2b:01:02:03}"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/web/pgadmin/feature_tests/pg_datatype_validation_test.py b/web/pgadmin/feature_tests/pg_datatype_validation_test.py
index 75993a491..b1b10a81c 100644
--- a/web/pgadmin/feature_tests/pg_datatype_validation_test.py
+++ b/web/pgadmin/feature_tests/pg_datatype_validation_test.py
@@ -7,14 +7,27 @@
#
##########################################################################
+import os
+import json
+import time
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
+from selenium.webdriver import ActionChains
from regression.python_test_utils import test_utils
from regression.feature_utils.base_feature_test import BaseFeatureTest
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+
+try:
+ with open(CURRENT_PATH + '/datatype_test.json') as data_file:
+ config_data = json.load(data_file)
+except Exception as e:
+ print(str(e))
+
+
class PGDataypeFeatureTest(BaseFeatureTest):
"""
This feature test will test the different Postgres
@@ -32,9 +45,74 @@ class PGDataypeFeatureTest(BaseFeatureTest):
self.server['host'],
self.server['port'],
self.server['sslmode'])
+
+ self.timezone = int(test_utils.get_timezone_without_dst(connection))
+
+ if abs(self.timezone) % 3600 > 0:
+ hh_mm = '%H:%M'
+ else:
+ hh_mm = '%H'
+
+ self.timezone_hh_mm = time.strftime(
+ hh_mm, time.gmtime(abs(self.timezone)))
+
+ if self.timezone < 0:
+ self.timezone_hh_mm = '-{}'.format(self.timezone_hh_mm)
+ else:
+ self.timezone_hh_mm = '+{}'.format(self.timezone_hh_mm)
+
test_utils.drop_database(connection, "acceptance_test_db")
test_utils.create_database(self.server, "acceptance_test_db")
+ # For this test case we need to set "Insert bracket pairs?"
+ # SQL Editor preference to 'false' to avoid codemirror
+ # to add matching closing bracket by it self.
+ self._update_preferences()
+
+ def _update_preferences(self):
+ self.page.find_by_id("mnu_file").click()
+ self.page.find_by_id("mnu_preferences").click()
+ wait = WebDriverWait(self.page.driver, 10)
+
+ wait.until(EC.presence_of_element_located(
+ (By.XPATH, "//*[contains(string(), 'Show system objects?')]"))
+ )
+
+ self.page.find_by_css_selector(".ajs-maximize").click()
+
+ sql_editor = self.page.find_by_xpath(
+ "//*[contains(@class,'aciTreeLi') and contains(.,'SQL Editor')]")
+
+ sql_editor.find_element_by_xpath(
+ "//*[contains(@class,'aciTreeText') and contains(.,'Options')]")\
+ .click()
+
+ insert_bracket_pairs_control= self.page.find_by_xpath(
+ "//div[contains(@class,'pgadmin-control-group') and contains(.,'Insert bracket pairs?')]")
+
+ switch_btn = insert_bracket_pairs_control.\
+ find_element_by_class_name('bootstrap-switch')
+
+ # check if switch is on then only toggle.
+ if 'bootstrap-switch-on' in switch_btn.get_attribute('class'):
+ switch_btn.click()
+
+ # save and close the preference dialog.
+ self.page.find_by_xpath(
+ "//*[contains(@class,'btn-primary') and contains(.,'OK')]").click()
+
+ self.page.wait_for_element_to_disappear(
+ lambda driver: driver.find_element_by_css_selector(".ajs-modal")
+ )
+
+ def _create_enum_type(self):
+ query = """CREATE TYPE public.rainbow AS ENUM ('red', 'orange',
+ 'yellow','green','blue','purple');
+ """
+ self.page.fill_codemirror_area_with(query)
+ self.page.find_by_id("btn-flash").click()
+ self._clear_query_tool()
+
def runTest(self):
self.page.wait_for_spinner_to_disappear()
self.page.add_server(self.server)
@@ -60,59 +138,87 @@ class PGDataypeFeatureTest(BaseFeatureTest):
self.page.toggle_open_tree_item('acceptance_test_db')
def _check_datatype(self):
- query = r"SELECT -32767::smallint, 32767::smallint," \
- r"-2147483647::integer, 2147483647::integer," \
- r"9223372036854775807::bigint, 9223372036854775807::bigint," \
- r"922337203685.4775807::decimal, 92203685.477::decimal," \
- r"922337203685.922337203685::numeric, " \
- r"-92233720368547758.08::numeric," \
- r"ARRAY[1, 2, 3]::float[], ARRAY['nan', 'nan', 'nan']::float[]," \
- r"'Infinity'::real, '{Infinity}'::real[]," \
- r"E'\\xDEADBEEF'::bytea, ARRAY[E'\\xDEADBEEF', E'\\xDEADBEEF']::bytea[];"
-
- expected_output = [
- '-32767', '32767', '-2147483647', '2147483647',
- '9223372036854775807', '9223372036854775807',
- '922337203685.4775807', '92203685.477',
- '922337203685.922337203685', '-92233720368547758.08',
- '{1,2,3}', '{NaN,NaN,NaN}',
- 'Infinity', '{Infinity}',
- 'binary data', 'binary data[]'
- ]
-
+ # Slick grid does not render all the column if viewport is not enough
+ # wide. So execute test as batch of queries.
self.page.open_query_tool()
- self.page.fill_codemirror_area_with(query)
- self.page.find_by_id("btn-flash").click()
- wait = WebDriverWait(self.page.driver, 5)
+ self._create_enum_type()
+ for batch in config_data:
+ query = self.construct_select_query(batch)
+ self.page.fill_codemirror_area_with(query)
+ self.page.find_by_id("btn-flash").click()
+ wait = WebDriverWait(self.page.driver, 5)
- canvas = wait.until(EC.presence_of_element_located(
- (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))
- )
-
- # For every sample data-type value, check the expected output.
- cnt = 2
- cells = canvas.find_elements_by_css_selector('.slick-cell')
- # remove first element as it is row number.
- cells.pop(0)
- for val, cell in zip(expected_output, cells):
- try:
- source_code = cell.text
-
- PGDataypeFeatureTest.check_result(
- source_code,
- expected_output[cnt - 2]
- )
- cnt += 1
- except TimeoutException:
- assert False, "{0} does not match with {1}".format(
- val, expected_output[cnt]
- )
-
- @staticmethod
- def check_result(source_code, string_to_find):
- assert source_code.find(string_to_find) != -1,\
- "{0} does not match with {1}".format(
- source_code, string_to_find
+ wait.until(EC.presence_of_element_located(
+ (By.XPATH,
+ "//*[contains(@class,'column-type') and contains(.,'{}')]".format(batch['datatype'][0])
+ ))
)
+ canvas = wait.until(EC.presence_of_element_located(
+ (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))
+ )
+ # For every sample data-type value, check the expected output.
+ cnt = 2
+ cells = canvas.find_elements_by_css_selector('.slick-cell')
+ # remove first element as it is row number.
+ cells.pop(0)
+ for val, cell, datatype in zip(batch['output'], cells, batch['datatype']):
+ expected_output = batch['output'][cnt - 2]
+
+ if datatype in ('tstzrange', 'tstzrange[]'):
+ expected_output = expected_output.format(
+ **dict([('tz', self.timezone_hh_mm)]))
+ try:
+ source_code = cell.text
+ PGDataypeFeatureTest.check_result(
+ datatype,
+ source_code,
+ expected_output
+ )
+
+ cnt += 1
+ except TimeoutException:
+ assert False,\
+ "for datatype {0}\n{1} does not match with {2}".format(
+ datatype, val, expected_output
+ )
+ self._clear_query_tool()
+
+ def construct_select_query(self, batch):
+ query = 'SELECT '
+ first = True
+ for datatype, inputdata in zip(batch['datatype'], batch['input']):
+ if datatype != '':
+ dataformatter = '{}::{}'
+ else:
+ dataformatter = '{}'
+
+ if datatype in ('tstzrange', 'tstzrange[]'):
+ inputdata = inputdata.format(
+ **dict([('tz', self.timezone_hh_mm)]))
+ if first:
+ query += dataformatter.format(inputdata, datatype)
+ else:
+ query += ','+dataformatter.format(inputdata, datatype)
+ first = False
+ return query + ';'
+
+ @staticmethod
+ def check_result(datatype, source_code, string_to_find):
+ assert source_code == string_to_find,\
+ "for datatype {0}\n{1} does not match with {2}".format(
+ datatype, source_code, string_to_find
+ )
+
+ def _clear_query_tool(self):
+ self.page.click_element(
+ self.page.find_by_xpath("//*[@id='btn-clear-dropdown']")
+ )
+ ActionChains(self.driver)\
+ .move_to_element(self.page.find_by_xpath("//*[@id='btn-clear']"))\
+ .perform()
+ self.page.click_element(
+ self.page.find_by_xpath("//*[@id='btn-clear']")
+ )
+ self.page.click_modal('Yes')
diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py
index 26e10cd23..255842f83 100644
--- a/web/regression/feature_utils/pgadmin_page.py
+++ b/web/regression/feature_utils/pgadmin_page.py
@@ -226,6 +226,19 @@ class PgadminPage:
return self._wait_for("element to exist", element_if_it_exists)
+ def wait_for_element_to_disappear(self, find_method_with_args):
+ def element_if_it_disappears(driver):
+ try:
+ element = find_method_with_args(driver)
+ if element.is_displayed() and element.is_enabled():
+ return False
+
+ return True
+ except NoSuchElementException:
+ return True
+
+ return self._wait_for("element to disappear", element_if_it_disappears)
+
def wait_for_reloading_indicator_to_disappear(self):
def reloading_indicator_has_disappeared(driver):
try:
diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py
index af52b9db3..668742575 100644
--- a/web/regression/python_test_utils/test_utils.py
+++ b/web/regression/python_test_utils/test_utils.py
@@ -712,3 +712,21 @@ class Database:
def __exit__(self, type, value, traceback):
self.connection.close()
drop_database(self.maintenance_connection, self.name)
+
+
+def get_timezone_without_dst(connection):
+ """
+ Returns timezone when daylight savings is not observed.
+ DST starts at mid of March and ends on first week of November.
+ So when getting timezone without dst use date (2017-01-01) which do not
+ fall in dst range.
+ """
+
+ timezone_no_dst_sql = """SELECT EXTRACT(
+ TIMEZONE FROM '2017-01-01 00:00:00'::timestamp with time zone);"""
+
+ pg_cursor = connection.cursor()
+
+ pg_cursor.execute(timezone_no_dst_sql)
+
+ return pg_cursor.fetchone()[0]