mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Make the query tool datatype test easily configurable and extendable and add support for multi-dimensional arrays and enums.
This commit is contained in:
committed by
Dave Page
parent
6577723a2a
commit
88bcd3b512
173
web/pgadmin/feature_tests/datatype_test.json
Normal file
173
web/pgadmin/feature_tests/datatype_test.json
Normal file
@@ -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}'",
|
||||
"'<?xml version=\"1.0\" encoding=\"UTF-8\"?><name>pgAdmin 4</name>'",
|
||||
"'{\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><name>pgAdmin 4</name>\", \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><name>pgAdmin 4</name>\"}'",
|
||||
"'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}",
|
||||
"<name>pgAdmin 4</name>",
|
||||
"{\"<name>pgAdmin 4</name>\",\"<name>pgAdmin 4</name>\"}",
|
||||
"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}"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user