Feature test improvement and fix intermittent failures part of #3936
parent
7090c02014
commit
9f455a514e
|
@ -10,9 +10,8 @@
|
|||
from __future__ import print_function
|
||||
import sys
|
||||
import random
|
||||
import time
|
||||
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.locators import BrowserToolBarLocators
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from selenium.common.exceptions import TimeoutException, \
|
||||
StaleElementReferenceException
|
||||
|
@ -57,27 +56,25 @@ class BrowserToolBarFeatureTest(BaseFeatureTest):
|
|||
|
||||
def after(self):
|
||||
self.page.remove_server(self.server)
|
||||
test_utils.delete_table(self.server, self.test_db,
|
||||
self.test_table_name)
|
||||
|
||||
def _locate_database_tree_node(self):
|
||||
def test_query_tool_button(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
|
||||
def test_query_tool_button(self):
|
||||
self._locate_database_tree_node()
|
||||
|
||||
retry_count = 0
|
||||
while retry_count < 5:
|
||||
try:
|
||||
self.page.find_by_css_selector(
|
||||
".wcFrameButton[title='Query Tool']:not(.disabled)")\
|
||||
BrowserToolBarLocators.open_query_tool_button_css)\
|
||||
.click()
|
||||
break
|
||||
except StaleElementReferenceException:
|
||||
except (StaleElementReferenceException, TimeoutException):
|
||||
retry_count += 1
|
||||
|
||||
time.sleep(0.5)
|
||||
self.page.find_by_css_selector(".wcPanelTab .wcTabIcon.fa.fa-bolt")
|
||||
self.page.find_by_css_selector(
|
||||
BrowserToolBarLocators.query_tool_panel_css)
|
||||
|
||||
def test_view_data_tool_button(self):
|
||||
self.page.select_tree_item(self.test_db)
|
||||
|
@ -90,26 +87,23 @@ class BrowserToolBarFeatureTest(BaseFeatureTest):
|
|||
while retry_count < 5:
|
||||
try:
|
||||
self.page.find_by_css_selector(
|
||||
".wcFrameButton[title='View Data']:not(.disabled)").click()
|
||||
BrowserToolBarLocators.view_table_data_button_css).click()
|
||||
break
|
||||
except StaleElementReferenceException:
|
||||
except (StaleElementReferenceException, TimeoutException):
|
||||
retry_count += 1
|
||||
|
||||
time.sleep(0.5)
|
||||
self.page.find_by_css_selector(".wcPanelTab .wcTabIcon.fa.fa-bolt")
|
||||
self.page.find_by_css_selector(
|
||||
BrowserToolBarLocators.view_data_panel_css)
|
||||
|
||||
def test_filtered_rows_tool_button(self):
|
||||
retry_count = 0
|
||||
while retry_count < 5:
|
||||
try:
|
||||
self.page.find_by_css_selector(
|
||||
".wcFrameButton[title='Filtered Rows']:not(.disabled)")\
|
||||
BrowserToolBarLocators.filter_data_button_css)\
|
||||
.click()
|
||||
break
|
||||
except StaleElementReferenceException:
|
||||
except (StaleElementReferenceException, TimeoutException):
|
||||
retry_count += 1
|
||||
|
||||
time.sleep(0.5)
|
||||
self.page.find_by_css_selector(
|
||||
".alertify .ajs-header[data-title~='Filter']")
|
||||
BrowserToolBarLocators.filter_alertify_box_css)
|
||||
self.page.click_modal('Cancel')
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
|
||||
import pyperclip
|
||||
import random
|
||||
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from regression.feature_utils.locators import QueryToolLocators
|
||||
|
||||
|
||||
class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
|
@ -32,10 +32,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
# Create test table with random name to avoid same name conflicts in
|
||||
# parallel execution
|
||||
self.test_table_name = "test_table" + str(random.randint(1000, 3000))
|
||||
|
||||
self.page.add_server(self.server)
|
||||
test_utils.create_table(
|
||||
self.server, self.test_db, self.test_table_name)
|
||||
self.page.add_server(self.server)
|
||||
|
||||
def runTest(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
|
@ -46,7 +45,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
self.page.fill_codemirror_area_with(
|
||||
"SELECT * FROM %s ORDER BY some_column" % self.test_table_name)
|
||||
|
||||
self.page.find_by_id("btn-flash").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self._copies_rows()
|
||||
self._copies_columns()
|
||||
|
@ -59,21 +59,26 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
|
||||
def _copies_rows(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
self.page.find_by_xpath(
|
||||
"//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||
first_row = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_row_xpath.format(1))
|
||||
first_row.click()
|
||||
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
copy_button = self.page.find_by_css_selector(
|
||||
QueryToolLocators.copy_button_css)
|
||||
copy_button.click()
|
||||
|
||||
self.assertEqual('"Some-Name"\t"6"\t"some info"',
|
||||
pyperclip.paste())
|
||||
|
||||
def _copies_columns(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
self.page.find_by_xpath(
|
||||
"//*[@data-test='output-column-header' and "
|
||||
"contains(., 'some_column')]"
|
||||
).click()
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
column = self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_column_header_css.format('some_column'))
|
||||
column.click()
|
||||
|
||||
copy_button = self.page.find_by_css_selector(
|
||||
QueryToolLocators.copy_button_css)
|
||||
copy_button.click()
|
||||
|
||||
self.assertEqual(
|
||||
"""\"Some-Name"
|
||||
|
@ -83,8 +88,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
|
||||
def _copies_row_using_keyboard_shortcut(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
self.page.find_by_xpath(
|
||||
"//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||
first_row = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_row_xpath.format(1))
|
||||
first_row.click()
|
||||
|
||||
ActionChains(self.page.driver).key_down(
|
||||
Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
@ -94,10 +100,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
|
||||
def _copies_column_using_keyboard_shortcut(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
self.page.find_by_xpath(
|
||||
"//*[@data-test='output-column-header' and "
|
||||
"contains(., 'some_column')]"
|
||||
).click()
|
||||
column = self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_column_header_css.format('some_column'))
|
||||
column.click()
|
||||
|
||||
ActionChains(self.page.driver).key_down(
|
||||
Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
@ -111,12 +116,12 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
def _copies_rectangular_selection(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
top_left_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and "
|
||||
"contains(., 'Some-Other-Name')]"
|
||||
)
|
||||
top_left_cell = \
|
||||
self.page.find_by_xpath(
|
||||
QueryToolLocators.output_column_data_xpath.
|
||||
format('Some-Other-Name'))
|
||||
bottom_right_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and contains(., '14')]")
|
||||
QueryToolLocators.output_column_data_xpath.format('14'))
|
||||
|
||||
ActionChains(
|
||||
self.page.driver
|
||||
|
@ -135,11 +140,11 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
top_left_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and "
|
||||
"contains(., 'Some-Other-Name')]"
|
||||
QueryToolLocators.output_column_data_xpath.
|
||||
format('Some-Other-Name')
|
||||
)
|
||||
initial_bottom_right_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and contains(., '14')]")
|
||||
QueryToolLocators.output_column_data_xpath.format('14'))
|
||||
ActionChains(
|
||||
self.page.driver
|
||||
).click_and_hold(top_left_cell).move_to_element(
|
||||
|
@ -160,10 +165,10 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
def _shift_resizes_column_selection(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
self.page.find_by_xpath(
|
||||
"//*[@data-test='output-column-header' and "
|
||||
"contains(., 'value')]"
|
||||
).click()
|
||||
column = self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_column_header_css.format('value')
|
||||
)
|
||||
column.click()
|
||||
|
||||
ActionChains(self.page.driver).key_down(
|
||||
Keys.SHIFT).send_keys(Keys.ARROW_LEFT).key_up(Keys.SHIFT).perform()
|
||||
|
@ -181,11 +186,11 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
bottom_right_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and "
|
||||
"contains(., 'cool info')]"
|
||||
QueryToolLocators.output_column_data_xpath.format('cool info')
|
||||
)
|
||||
|
||||
load_button = self.page.find_by_xpath("//button[@id='btn-load-file']")
|
||||
load_button = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_load_file_css)
|
||||
ActionChains(self.page.driver).click_and_hold(bottom_right_cell) \
|
||||
.move_to_element(load_button) \
|
||||
.release(load_button) \
|
||||
|
@ -199,3 +204,5 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||
def after(self):
|
||||
self.page.close_query_tool()
|
||||
self.page.remove_server(self.server)
|
||||
test_utils.delete_table(self.server, self.test_db,
|
||||
self.test_table_name)
|
||||
|
|
|
@ -9,15 +9,16 @@
|
|||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import time
|
||||
import sys
|
||||
|
||||
import time
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.common.exceptions import StaleElementReferenceException, \
|
||||
TimeoutException
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from .locators import QueryToolLocatorsCss
|
||||
from regression.feature_utils.locators import QueryToolLocators
|
||||
|
||||
|
||||
class CheckFileManagerFeatureTest(BaseFeatureTest):
|
||||
|
@ -65,32 +66,40 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
|
|||
self.page.open_query_tool()
|
||||
|
||||
def _create_new_file(self):
|
||||
self.page.find_by_css_selector(QueryToolLocatorsCss.btn_save_file)\
|
||||
self.page.find_by_css_selector(QueryToolLocators.btn_save_file)\
|
||||
.click()
|
||||
# Set the XSS value in input
|
||||
self.page.find_by_css_selector('.change_file_types')
|
||||
self.page.fill_input_by_css_selector("input#file-input-path",
|
||||
self.XSS_FILE)
|
||||
self.page.fill_input_by_css_selector(
|
||||
QueryToolLocators.input_file_path_css, self.XSS_FILE)
|
||||
# Save the file
|
||||
self.page.click_modal('Create')
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
def _open_file_manager_and_check_xss_file(self):
|
||||
self.page.find_by_id("btn-load-file").click()
|
||||
load_file = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_load_file_css)
|
||||
load_file.click()
|
||||
self.page.find_by_css_selector('.change_file_types')
|
||||
self.page.fill_input_by_css_selector("#file-input-path", "/tmp/",
|
||||
key_after_input=Keys.RETURN)
|
||||
self.page.fill_input_by_css_selector(
|
||||
QueryToolLocators.input_file_path_css,
|
||||
"/tmp/", key_after_input=Keys.RETURN)
|
||||
|
||||
if self.page.driver.capabilities['browserName'] == 'firefox':
|
||||
table = self.page.wait_for_element_to_reload(
|
||||
lambda driver:
|
||||
driver.find_element_by_css_selector("table#contents")
|
||||
lambda driver: driver.find_element_by_css_selector(
|
||||
QueryToolLocators.select_file_content_css)
|
||||
)
|
||||
else:
|
||||
table = self.page.driver \
|
||||
.find_element_by_css_selector("table#contents")
|
||||
|
||||
contents = table.get_attribute('innerHTML')
|
||||
table = self.page.driver.find_element_by_css_selector(
|
||||
QueryToolLocators.select_file_content_css)
|
||||
retry_count = 0
|
||||
while retry_count < 5:
|
||||
try:
|
||||
contents = table.get_attribute('innerHTML')
|
||||
break
|
||||
except (StaleElementReferenceException, TimeoutException):
|
||||
retry_count += 1
|
||||
|
||||
self.page.click_modal('Cancel')
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
@ -107,7 +116,9 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
|
|||
) != -1, "{0} might be vulnerable to XSS ".format(source)
|
||||
|
||||
def _check_file_sorting(self):
|
||||
self.page.find_by_id("btn-load-file").click()
|
||||
load_file = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_load_file_css)
|
||||
load_file.click()
|
||||
self.page.find_by_css_selector("#contents th[data-column='0']")
|
||||
|
||||
# Added time.sleep so that the element to be clicked.
|
||||
|
@ -134,7 +145,7 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
|
|||
if not success:
|
||||
raise Exception("Unable to sort in ascending order while clicked "
|
||||
"on 'Name' column")
|
||||
|
||||
# Added time.sleep so that the element to be clicked.
|
||||
time.sleep(0.05)
|
||||
|
||||
# Click and Check for sort Descending
|
||||
|
@ -143,9 +154,10 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
|
|||
iteration = 0
|
||||
success = False
|
||||
while not success and iteration < 4:
|
||||
self.page.find_by_xpath("//th[@data-column='0']"
|
||||
"/div/span[text()='Name']").click()
|
||||
|
||||
try:
|
||||
self.page.find_by_xpath("//th[@data-column='0']"
|
||||
"/div/span[text()='Name']").click()
|
||||
self.wait.until(
|
||||
EC.presence_of_element_located((
|
||||
By.CSS_SELECTOR,
|
||||
|
|
|
@ -15,13 +15,14 @@ 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.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from regression.feature_utils.locators import NavMenuLocators
|
||||
|
||||
|
||||
class KeyboardShortcutFeatureTest(BaseFeatureTest):
|
||||
"""
|
||||
This feature test will test the keyboard short is working
|
||||
This feature test will test the keyboard shortcut is working
|
||||
properly.
|
||||
"""
|
||||
|
||||
|
@ -85,23 +86,36 @@ class KeyboardShortcutFeatureTest(BaseFeatureTest):
|
|||
print("OK", file=sys.stderr)
|
||||
|
||||
def _update_preferences(self):
|
||||
self.page.find_by_id("mnu_file").click()
|
||||
self.page.find_by_id("mnu_preferences").click()
|
||||
file_menu = self.page.find_by_css_selector(
|
||||
NavMenuLocators.file_menu_css)
|
||||
file_menu.click()
|
||||
|
||||
pref_menu_item = self.page.find_by_css_selector(
|
||||
NavMenuLocators.preference_menu_item_css)
|
||||
pref_menu_item.click()
|
||||
|
||||
# Wait till the preference dialogue box is displayed by checking the
|
||||
# visibility of Show System Object label
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, "//*[contains(string(), 'Show system objects?')]"))
|
||||
(By.XPATH, NavMenuLocators.show_system_objects_pref_label_xpath))
|
||||
)
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
".ajs-dialog.pg-el-container .ajs-maximize"
|
||||
).click()
|
||||
maximize_button = self.page.find_by_css_selector(
|
||||
NavMenuLocators.maximize_pref_dialogue_css)
|
||||
maximize_button.click()
|
||||
|
||||
browser = self.page.find_by_xpath(
|
||||
"//*[contains(@class,'aciTreeLi') and contains(.,'Browser')]")
|
||||
browser_node = self.page.find_by_xpath(
|
||||
NavMenuLocators.specified_preference_tree_node.format('Browser'))
|
||||
if self.page.find_by_xpath(
|
||||
NavMenuLocators.specified_pref_node_exp_status.
|
||||
format('Browser')).get_attribute('aria-expanded') == 'false':
|
||||
|
||||
browser.find_element_by_xpath(
|
||||
"//*[contains(@class,'aciTreeText') and "
|
||||
"contains(.,'Keyboard shortcuts')]").click()
|
||||
ActionChains(self.driver).double_click(browser_node).perform()
|
||||
|
||||
keyboard_node = self.page.find_by_xpath(
|
||||
NavMenuLocators.specified_sub_node_of_pref_tree_node.format(
|
||||
'Browser', 'Keyboard shortcuts'))
|
||||
keyboard_node.click()
|
||||
|
||||
for s in self.new_shortcuts:
|
||||
key = self.new_shortcuts[s]['shortcut'][2]
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
class QueryToolLocatorsCss:
|
||||
btn_save_file = "#btn-save-file"
|
||||
btn_save_data = "#btn-save-data"
|
||||
btn_execute_query = "#btn-flash"
|
||||
btn_query_dropdown = "#btn-query-dropdown"
|
||||
btn_auto_rollback = "#btn-auto-rollback"
|
||||
btn_auto_rollback_check_status = "#btn-auto-rollback > i"
|
||||
btn_auto_commit = "#btn-auto-commit"
|
||||
btn_auto_commit_check_status = "#btn-auto-commit > i"
|
||||
btn_cancel_query = "#btn-cancel-query"
|
||||
btn_explain = "#btn-explain"
|
||||
btn_explain_analyze = "#btn-explain-analyze"
|
||||
btn_explain_options_dropdown = "#btn-explain-options-dropdown"
|
||||
btn_explain_verbose = "#btn-explain-verbose"
|
||||
btn_explain_costs = "#btn-explain-costs"
|
||||
btn_explain_buffers = "#btn-explain-buffers"
|
||||
btn_explain_timing = "#btn-explain-timing"
|
||||
btn_clear_dropdown = "#btn-clear-dropdown"
|
||||
btn_clear = "#btn-clear"
|
||||
btn_commit = "#btn-commit"
|
||||
query_editor_panel = "#output-panel"
|
||||
query_history_selected = "#query_list .selected"
|
||||
query_history_selected_icon = '#query_list .selected #query_source_icon'
|
||||
query_history_detail = "#query_detail"
|
||||
query_history_generated_queries_toggle = '#generated-queries-toggle'
|
||||
editor_panel = "#output-panel"
|
||||
query_messages_panel = ".sql-editor-message"
|
||||
execute_icon = "fa-bolt"
|
||||
explain_icon = "fa-hand-pointer-o"
|
||||
explain_analyze_icon = "fa-list-alt"
|
||||
save_data_icon = "icon-save-data-changes"
|
||||
commit_icon = "icon-commit"
|
|
@ -18,6 +18,8 @@ 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
|
||||
from regression.feature_utils.locators import NavMenuLocators, \
|
||||
QueryToolLocators
|
||||
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
@ -78,35 +80,42 @@ class PGDataypeFeatureTest(BaseFeatureTest):
|
|||
connection.close()
|
||||
|
||||
def _update_preferences(self):
|
||||
self.page.find_by_id("mnu_file").click()
|
||||
self.page.find_by_id("mnu_preferences").click()
|
||||
file_menu = self.page.find_by_css_selector(
|
||||
NavMenuLocators.file_menu_css)
|
||||
file_menu.click()
|
||||
|
||||
pref_menu_item = self.page.find_by_css_selector(
|
||||
NavMenuLocators.preference_menu_item_css)
|
||||
pref_menu_item.click()
|
||||
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
# Wait till the preference dialogue box is displayed by checking the
|
||||
# visibility of Show System Object label
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, "//*[contains(string(), 'Show system objects?')]"))
|
||||
(By.XPATH, NavMenuLocators.show_system_objects_pref_label_xpath))
|
||||
)
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
".ajs-dialog.pg-el-container .ajs-maximize").click()
|
||||
maximize_button = self.page.find_by_css_selector(
|
||||
NavMenuLocators.maximize_pref_dialogue_css)
|
||||
maximize_button.click()
|
||||
|
||||
sql_editor = self.page.find_by_xpath(
|
||||
"//*[contains(@class,'aciTreeLi') and contains(.,'Query Tool')]")
|
||||
NavMenuLocators.specified_preference_tree_node.
|
||||
format('Query Tool'))
|
||||
if self.page.find_by_xpath(
|
||||
NavMenuLocators.specified_pref_node_exp_status.
|
||||
format('Query Tool')).\
|
||||
get_attribute('aria-expanded') == 'false':
|
||||
ActionChains(self.driver).double_click(sql_editor).perform()
|
||||
|
||||
sql_editor.find_element_by_xpath(
|
||||
"//*[contains(@class,'aciTreeText') and contains(.,'Options')]"
|
||||
).click()
|
||||
option_node = self.page.find_by_xpath(
|
||||
NavMenuLocators.specified_sub_node_of_pref_tree_node.format(
|
||||
'Query Tool', 'Options'))
|
||||
option_node.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('toggle')
|
||||
|
||||
# check if switch is on then only toggle.
|
||||
if 'off' not in switch_btn.get_attribute('class'):
|
||||
switch_btn.click()
|
||||
self.page.set_switch_box_status(
|
||||
NavMenuLocators.insert_bracket_pair_switch_btn, 'No')
|
||||
|
||||
# save and close the preference dialog.
|
||||
self.page.click_modal('Save')
|
||||
|
@ -121,8 +130,10 @@ class PGDataypeFeatureTest(BaseFeatureTest):
|
|||
'yellow','green','blue','purple');
|
||||
"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_id("btn-flash").click()
|
||||
self._clear_query_tool()
|
||||
execute_query = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css)
|
||||
execute_query.click()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
def runTest(self):
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
|
@ -150,7 +161,9 @@ class PGDataypeFeatureTest(BaseFeatureTest):
|
|||
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()
|
||||
execute_query = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css)
|
||||
execute_query.click()
|
||||
|
||||
wait = WebDriverWait(self.page.driver, 5)
|
||||
|
||||
|
@ -168,12 +181,13 @@ class PGDataypeFeatureTest(BaseFeatureTest):
|
|||
))
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css))
|
||||
)
|
||||
|
||||
# For every sample data-type value, check the expected output.
|
||||
cnt = 2
|
||||
cells = canvas.find_elements_by_css_selector('.slick-cell')
|
||||
cells = canvas.find_elements_by_css_selector(
|
||||
QueryToolLocators.query_output_cells)
|
||||
# remove first element as it is row number.
|
||||
cells.pop(0)
|
||||
for val, cell, datatype in zip(
|
||||
|
@ -202,7 +216,7 @@ class PGDataypeFeatureTest(BaseFeatureTest):
|
|||
"for datatype {0}\n{1} does not match with {2}".format(
|
||||
datatype, val, expected_output
|
||||
)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
def construct_select_query(self, batch):
|
||||
query = 'SELECT '
|
||||
|
@ -233,18 +247,6 @@ class PGDataypeFeatureTest(BaseFeatureTest):
|
|||
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')
|
||||
|
||||
def _is_datatype_available_in_current_database(self, datatype):
|
||||
if datatype == '':
|
||||
return True
|
||||
|
|
|
@ -11,9 +11,11 @@ import os
|
|||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.common.exceptions import ElementClickInterceptedException
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.python_test_utils import test_gui_helper
|
||||
from regression.feature_utils.locators import NavMenuLocators
|
||||
|
||||
|
||||
class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
||||
|
@ -61,22 +63,47 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
self.page.toggle_open_tree_item(self.database_name)
|
||||
|
||||
# Backup
|
||||
self.driver.find_element_by_link_text("Tools").click()
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
try:
|
||||
self.driver.find_element_by_link_text(
|
||||
NavMenuLocators.tools_menu_link_text).click()
|
||||
break
|
||||
except ElementClickInterceptedException:
|
||||
retry -= 1
|
||||
|
||||
self.page.find_by_partial_link_text("Backup...").click()
|
||||
backup_object = self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, NavMenuLocators.backup_obj_css)))
|
||||
backup_object.click()
|
||||
|
||||
self.wait.until(EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, ".file [name='file']")))
|
||||
|
||||
self.wait.until(EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, ".file [name='file']"))).click()
|
||||
# Enter the file name of the backup to be taken
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.NAME, NavMenuLocators.backup_filename_txt_box_name)))
|
||||
element = self.wait.until(EC.element_to_be_clickable(
|
||||
(By.NAME, NavMenuLocators.backup_filename_txt_box_name)))
|
||||
element.click()
|
||||
self.page.fill_input_by_field_name(
|
||||
"file", "test_backup", loose_focus=True)
|
||||
NavMenuLocators.backup_filename_txt_box_name,
|
||||
"test_backup", loose_focus=True)
|
||||
|
||||
self.page.find_by_xpath("//button[contains(@class,'fa-save') "
|
||||
"and contains(.,'Backup')]").click()
|
||||
# Click on the take Backup button
|
||||
take_bckup = self.page.find_by_xpath(
|
||||
NavMenuLocators.backup_btn_xpath)
|
||||
click = True
|
||||
while click:
|
||||
try:
|
||||
take_bckup.click()
|
||||
if self.page.wait_for_element_to_disappear(
|
||||
lambda driver: driver.find_element_by_name(
|
||||
NavMenuLocators.backup_filename_txt_box_name)):
|
||||
click = False
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
self.page.find_by_css_selector('.ajs-bg-bgprocess')
|
||||
# Wait for the backup status alertfier
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
NavMenuLocators.bcg_process_status_alertifier_css)))
|
||||
|
||||
status = test_utils.get_watcher_dialogue_status(self)
|
||||
|
||||
|
@ -86,7 +113,10 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
self.assertEquals(status, "Successfully completed.")
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
".pg-bg-more-details").click()
|
||||
NavMenuLocators.status_alertifier_more_btn_css).click()
|
||||
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.XPATH, NavMenuLocators.process_watcher_alertfier)))
|
||||
|
||||
backup_file = None
|
||||
# Check for XSS in Backup details
|
||||
|
@ -94,7 +124,8 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
self._check_detailed_window_for_xss('Backup')
|
||||
else:
|
||||
command = self.page.find_by_css_selector(
|
||||
".bg-process-details .bg-detailed-desc").text
|
||||
NavMenuLocators.process_watcher_detailed_command_canvas_css).\
|
||||
text
|
||||
|
||||
self.assertIn(self.server['name'], str(command))
|
||||
self.assertIn("from database 'pg_utility_test_db'", str(command))
|
||||
|
@ -109,26 +140,41 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
backup_file = command[int(command.find('--file')) +
|
||||
8:int(command.find('--host')) - 2]
|
||||
|
||||
self.page.find_by_xpath("//div[contains(@class,'wcFloatingFocus')"
|
||||
"]//div[contains(@class,'fa-close')]").click()
|
||||
close_btn = self.page.find_by_xpath(
|
||||
NavMenuLocators.process_watcher_close_button_xpath)
|
||||
close_btn.click()
|
||||
|
||||
# Restore
|
||||
self.driver.find_element_by_link_text("Tools").click()
|
||||
self.page.find_by_partial_link_text("Restore...").click()
|
||||
tools_menu = self.driver.find_element_by_link_text(
|
||||
NavMenuLocators.tools_menu_link_text)
|
||||
tools_menu.click()
|
||||
|
||||
restore_obj = self.page.find_by_css_selector(
|
||||
NavMenuLocators.restore_obj_css)
|
||||
restore_obj.click()
|
||||
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.NAME, NavMenuLocators.restore_file_name_txt_box_name)))
|
||||
|
||||
self.wait.until(EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, ".file [name='file']")))
|
||||
|
||||
self.wait.until(EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, ".file [name='file']"))).click()
|
||||
(By.NAME, NavMenuLocators.restore_file_name_txt_box_name))).click()
|
||||
|
||||
self.page.fill_input_by_field_name(
|
||||
"file", "test_backup", loose_focus=True)
|
||||
NavMenuLocators.restore_file_name_txt_box_name,
|
||||
"test_backup", loose_focus=True)
|
||||
|
||||
self.page.find_by_xpath("//button[contains(@class,'fa-upload')"
|
||||
" and contains(.,'Restore')]").click()
|
||||
restore_btn = self.page.find_by_xpath(
|
||||
NavMenuLocators.restore_button_xpath)
|
||||
restore_btn.click()
|
||||
|
||||
self.page.find_by_css_selector('.ajs-bg-bgprocess')
|
||||
self.page.wait_for_element_to_disappear(
|
||||
lambda driver: driver.find_element_by_css_selector(
|
||||
NavMenuLocators.restore_file_name_txt_box_name))
|
||||
|
||||
# Wait for the backup status alertfier
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
NavMenuLocators.bcg_process_status_alertifier_css)))
|
||||
|
||||
status = test_utils.get_watcher_dialogue_status(self)
|
||||
|
||||
|
@ -138,14 +184,18 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
self.assertEquals(status, "Successfully completed.")
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
".pg-bg-more-details").click()
|
||||
NavMenuLocators.status_alertifier_more_btn_css).click()
|
||||
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.XPATH, NavMenuLocators.process_watcher_alertfier)))
|
||||
|
||||
# Check for XSS in Restore details
|
||||
if self.is_xss_check:
|
||||
self._check_detailed_window_for_xss('Restore')
|
||||
else:
|
||||
command = self.page.find_by_css_selector(
|
||||
".bg-process-details .bg-detailed-desc").text
|
||||
NavMenuLocators.process_watcher_detailed_command_canvas_css).\
|
||||
text
|
||||
|
||||
self.assertIn(self.server['name'], str(command))
|
||||
if os.name is not 'nt':
|
||||
|
@ -153,8 +203,9 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
|
||||
self.assertIn("pg_restore", str(command))
|
||||
|
||||
self.page.find_by_xpath("//div[contains(@class,'wcFloatingFocus')]"
|
||||
"//div[contains(@class,'fa-close')]").click()
|
||||
close_watcher = self.page.find_by_xpath(
|
||||
NavMenuLocators.process_watcher_close_button_xpath)
|
||||
close_watcher.click()
|
||||
|
||||
if backup_file is not None:
|
||||
if os.path.isfile(backup_file):
|
||||
|
@ -175,7 +226,7 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
|||
|
||||
def _check_detailed_window_for_xss(self, tool_name):
|
||||
source_code = self.page.find_by_css_selector(
|
||||
".bg-process-details .bg-detailed-desc"
|
||||
NavMenuLocators.process_watcher_detailed_command_canvas_css
|
||||
).get_attribute('innerHTML')
|
||||
self._check_escaped_characters(
|
||||
source_code,
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import time
|
||||
|
||||
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.common.exceptions import ElementClickInterceptedException
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.python_test_utils import test_gui_helper
|
||||
from regression.feature_utils.locators import NavMenuLocators
|
||||
import random
|
||||
|
||||
|
||||
class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
||||
|
@ -55,6 +57,8 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
|||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
|
||||
self.table_name = self.table_name + str(random.randint(1000, 3000))
|
||||
test_utils.drop_database(connection, self.database_name)
|
||||
test_utils.create_database(self.server, self.database_name)
|
||||
test_utils.create_table(self.server, self.database_name,
|
||||
|
@ -66,8 +70,16 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
|||
def runTest(self):
|
||||
self._open_maintenance_dialogue()
|
||||
self.page.click_modal('OK')
|
||||
self.page.find_by_css_selector('.ajs-bg-bgprocess')
|
||||
self._verify_command()
|
||||
self.page.wait_for_element_to_disappear(
|
||||
lambda driver: driver.find_element_by_xpath(
|
||||
NavMenuLocators.maintenance_operation))
|
||||
|
||||
# Wait for the backup status alertfier
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
NavMenuLocators.bcg_process_status_alertifier_css)))
|
||||
|
||||
self.verify_command()
|
||||
|
||||
def _open_maintenance_dialogue(self):
|
||||
self.page.toggle_open_server(self.server['name'])
|
||||
|
@ -78,30 +90,49 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
|||
self.page.toggle_open_tree_item('public')
|
||||
self.page.toggle_open_tables_node()
|
||||
self.page.select_tree_item(self.table_name)
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
try:
|
||||
tools_menu = self.driver.find_element_by_link_text(
|
||||
NavMenuLocators.tools_menu_link_text)
|
||||
tools_menu.click()
|
||||
break
|
||||
except ElementClickInterceptedException:
|
||||
retry -= 1
|
||||
maintenance_obj = self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css)))
|
||||
maintenance_obj.click()
|
||||
|
||||
self.driver.find_element_by_link_text("Tools").click()
|
||||
self.page.find_by_partial_link_text("Maintenance...").click()
|
||||
time.sleep(0.5)
|
||||
self.page.check_if_element_exist_by_xpath(
|
||||
NavMenuLocators.maintenance_operation, 10)
|
||||
|
||||
def _verify_command(self):
|
||||
def verify_command(self):
|
||||
status = test_utils.get_watcher_dialogue_status(self)
|
||||
if status != "Successfully completed.":
|
||||
|
||||
test_gui_helper.close_bgprocess_popup(self)
|
||||
|
||||
self.assertEquals(status, "Successfully completed.")
|
||||
self.page.find_by_css_selector(".pg-bg-more-details").click()
|
||||
self.page.find_by_css_selector(
|
||||
NavMenuLocators.status_alertifier_more_btn_css).click()
|
||||
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.XPATH, NavMenuLocators.process_watcher_alertfier)))
|
||||
|
||||
command = self.page.find_by_css_selector(
|
||||
".bg-process-details .bg-detailed-desc").text
|
||||
NavMenuLocators.
|
||||
process_watcher_detailed_command_canvas_css).text
|
||||
|
||||
if self.test_level == 'database':
|
||||
self.assertEquals(command, "VACUUM "
|
||||
"(VERBOSE)\nRunning Query:"
|
||||
self.assertEquals(command, "VACUUM (VERBOSE)\nRunning Query:"
|
||||
"\nVACUUM VERBOSE;")
|
||||
elif self.is_xss_check and self.test_level == 'table':
|
||||
# Check for XSS in the dialog
|
||||
source_code = self.page.find_by_css_selector(
|
||||
".bg-process-details .bg-detailed-desc"
|
||||
NavMenuLocators.
|
||||
process_watcher_detailed_command_canvas_css
|
||||
).get_attribute('innerHTML')
|
||||
self._check_escaped_characters(
|
||||
self.check_escaped_characters(
|
||||
source_code,
|
||||
'<h1>test_me</h1>',
|
||||
'Maintenance detailed window'
|
||||
|
@ -112,12 +143,14 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
|||
"\nVACUUM VERBOSE"
|
||||
" public." + self.table_name + ";")
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
"div.wcFloatingFocus div.fa-close").click()
|
||||
self.page.find_by_xpath(
|
||||
NavMenuLocators.process_watcher_close_button_xpath).click()
|
||||
|
||||
def after(self):
|
||||
test_gui_helper.close_bgprocess_popup(self)
|
||||
self.page.remove_server(self.server)
|
||||
test_utils.delete_table(self.server, self.test_db,
|
||||
self.table_name)
|
||||
connection = test_utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
|
@ -128,7 +161,7 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
|||
)
|
||||
test_utils.drop_database(connection, self.database_name)
|
||||
|
||||
def _check_escaped_characters(self, source_code, string_to_find, source):
|
||||
def check_escaped_characters(self, source_code, string_to_find, source):
|
||||
# For XSS we need to search against element's html code
|
||||
assert source_code.find(string_to_find) != - \
|
||||
1, "{0} might be vulnerable to XSS ".format(source)
|
||||
|
|
|
@ -15,6 +15,7 @@ from selenium.webdriver import ActionChains
|
|||
from selenium.webdriver.common.keys import Keys
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from regression.feature_utils.locators import QueryToolLocators
|
||||
|
||||
|
||||
class QueryToolAutoCompleteFeatureTest(BaseFeatureTest):
|
||||
|
@ -55,7 +56,10 @@ class QueryToolAutoCompleteFeatureTest(BaseFeatureTest):
|
|||
test_utils.create_table(self.server, self.test_db,
|
||||
self.second_table_name)
|
||||
|
||||
self._locate_database_tree_node()
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
|
||||
self.page.open_query_tool()
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
|
||||
|
@ -64,64 +68,64 @@ class QueryToolAutoCompleteFeatureTest(BaseFeatureTest):
|
|||
print("\nAuto complete ALTER keyword... ", file=sys.stderr, end="")
|
||||
self._auto_complete("A", "ALTER")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete BEGIN keyword... ", file=sys.stderr, end="")
|
||||
self._auto_complete("BE", "BEGIN")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete CASCADED keyword... ", file=sys.stderr, end="")
|
||||
self._auto_complete("CAS", "CASCADED")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete SELECT keyword... ", file=sys.stderr, end="")
|
||||
self._auto_complete("SE", "SELECT")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete pg_backend_pid() function ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT pg_", "pg_backend_pid()")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete current_query() function ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT current_", "current_query()")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete function with argument ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT pg_st", "pg_stat_file(filename)")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete schema other than default start with test_ ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT * FROM te", self.first_schema_name)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete schema other than default starts with comp_ ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT * FROM co", self.second_schema_name)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete first table in public schema ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT * FROM public.", self.first_table_name)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete second table in public schema ... ",
|
||||
file=sys.stderr, end="")
|
||||
self._auto_complete("SELECT * FROM public.", self.second_table_name)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete JOIN second table with after schema name ... ",
|
||||
file=sys.stderr, end="")
|
||||
|
@ -129,7 +133,7 @@ class QueryToolAutoCompleteFeatureTest(BaseFeatureTest):
|
|||
" JOIN public."
|
||||
self._auto_complete(query, self.second_table_name)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete JOIN ON some columns ... ",
|
||||
file=sys.stderr, end="")
|
||||
|
@ -140,54 +144,51 @@ class QueryToolAutoCompleteFeatureTest(BaseFeatureTest):
|
|||
".some_column"
|
||||
self._auto_complete(query, expected_string)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("Auto complete JOIN ON some columns using tabel alias ... ",
|
||||
print("Auto complete JOIN ON some columns using table alias ... ",
|
||||
file=sys.stderr, end="")
|
||||
query = "SELECT * FROM public." + self.first_table_name + \
|
||||
" t1 JOIN public." + self.second_table_name + " t2 ON t2."
|
||||
self._auto_complete(query, "some_column = t1.some_column")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
def after(self):
|
||||
self.page.remove_server(self.server)
|
||||
|
||||
def _locate_database_tree_node(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
|
||||
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')
|
||||
test_utils.delete_table(self.server, self.test_db,
|
||||
self.first_table_name)
|
||||
test_utils.delete_table(self.server, self.test_db,
|
||||
self.second_table_name)
|
||||
|
||||
def _auto_complete(self, word, expected_string):
|
||||
self.page.fill_codemirror_area_with(word)
|
||||
ActionChains(self.page.driver).key_down(
|
||||
Keys.CONTROL).send_keys(Keys.SPACE).key_up(Keys.CONTROL).perform()
|
||||
|
||||
# if IntelliSense is present then verify this
|
||||
if self.page.check_if_element_exist_by_xpath(
|
||||
"//ul[@class='CodeMirror-hints default']", 2):
|
||||
hint_displayed = False
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
ActionChains(self.page.driver).key_down(
|
||||
Keys.CONTROL).send_keys(Keys.SPACE).key_up(
|
||||
Keys.CONTROL).perform()
|
||||
if self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.code_mirror_hint_box_xpath, 20):
|
||||
hint_displayed = True
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
if hint_displayed:
|
||||
# if IntelliSense is present then verify this
|
||||
self.page.find_by_xpath(
|
||||
"//ul[contains(@class, 'CodeMirror-hints') and "
|
||||
"contains(., '" + expected_string + "')]")
|
||||
QueryToolLocators.code_mirror_hint_item_xpath.format(
|
||||
expected_string))
|
||||
else:
|
||||
# if no IntelliSense is present it means there is only one option
|
||||
# so check if required string is present in codeMirror
|
||||
code_mirror = self.page.find_by_xpath(
|
||||
"//pre[@class=' CodeMirror-line ']/span")
|
||||
code_mirror_text = code_mirror.text
|
||||
|
||||
if expected_string not in code_mirror_text:
|
||||
raise Exception("Required String %s is not "
|
||||
"present" % expected_string)
|
||||
code_mirror = self.driver.find_elements_by_xpath(
|
||||
QueryToolLocators.code_mirror_data_xpath)
|
||||
for data in code_mirror:
|
||||
code_mirror_text = data.text
|
||||
print("Single entry..........")
|
||||
if expected_string not in code_mirror_text:
|
||||
print("single entry exception.........")
|
||||
raise Exception("Required String %s is not "
|
||||
"present" % expected_string)
|
||||
|
|
|
@ -14,10 +14,10 @@ import random
|
|||
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from .locators import QueryToolLocatorsCss
|
||||
from regression.feature_utils.locators import QueryToolLocators
|
||||
|
||||
|
||||
class QueryToolJourneyTest(BaseFeatureTest):
|
||||
|
@ -31,9 +31,12 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
|
||||
test_table_name = ""
|
||||
test_editable_table_name = ""
|
||||
invalid_table_name = ""
|
||||
|
||||
def before(self):
|
||||
self.test_table_name = "test_table" + str(random.randint(1000, 3000))
|
||||
self.invalid_table_name = \
|
||||
"table_that_doesnt_exist_" + str(random.randint(1000, 3000))
|
||||
test_utils.create_table(
|
||||
self.server, self.test_db, self.test_table_name)
|
||||
|
||||
|
@ -52,10 +55,11 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
|
||||
driver_version = test_utils.get_driver_version()
|
||||
self.driver_version = float('.'.join(driver_version.split('.')[:2]))
|
||||
self.wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
def runTest(self):
|
||||
self._navigate_to_query_tool()
|
||||
self._execute_query(
|
||||
self.page.execute_query(
|
||||
"SELECT * FROM %s ORDER BY value " % self.test_table_name)
|
||||
|
||||
print("Copy rows...", file=sys.stderr, end="")
|
||||
|
@ -78,7 +82,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
self._test_query_sources_and_generated_queries()
|
||||
print(" OK.", file=sys.stderr)
|
||||
|
||||
print("Updatable resultsets...", file=sys.stderr, end="")
|
||||
print("Updatable result sets...", file=sys.stderr, end="")
|
||||
self._test_updatable_resultset()
|
||||
print(" OK.", file=sys.stderr)
|
||||
|
||||
|
@ -87,9 +91,14 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
self.page.driver.switch_to.default_content()
|
||||
self.page.driver.switch_to_frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
self.page.find_by_xpath(
|
||||
"//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
|
||||
select_row = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_row_xpath.format('1'))
|
||||
select_row.click()
|
||||
|
||||
copy_row = self.page.find_by_css_selector(
|
||||
QueryToolLocators.copy_button_css)
|
||||
copy_row.click()
|
||||
|
||||
self.assertEqual('"Some-Name"\t"6"\t"some info"',
|
||||
pyperclip.paste())
|
||||
|
@ -100,86 +109,85 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
self.page.driver.switch_to.default_content()
|
||||
self.page.driver.switch_to_frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
self.page.find_by_xpath(
|
||||
"//*[@data-test='output-column-header' and "
|
||||
"contains(., 'some_column')]"
|
||||
).click()
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
|
||||
column_header = self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_column_header_css.format('some_column'))
|
||||
column_header.click()
|
||||
|
||||
copy_btn = self.page.find_by_css_selector(
|
||||
QueryToolLocators.copy_button_css)
|
||||
copy_btn.click()
|
||||
|
||||
self.assertTrue('"Some-Name"' in pyperclip.paste())
|
||||
self.assertTrue('"Some-Other-Name"' in pyperclip.paste())
|
||||
self.assertTrue('"Yet-Another-Name"' in pyperclip.paste())
|
||||
|
||||
def _test_history_tab(self):
|
||||
self.__clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
editor_input = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.query_editor_panel)
|
||||
QueryToolLocators.query_editor_panel)
|
||||
self.page.click_element(editor_input)
|
||||
self._execute_query("SELECT * FROM table_that_doesnt_exist")
|
||||
self.page.execute_query("SELECT * FROM %s" % self.invalid_table_name)
|
||||
|
||||
self.page.click_tab("Query History")
|
||||
selected_history_entry = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.query_history_selected)
|
||||
self.assertIn("SELECT * FROM table_that_doesnt_exist",
|
||||
QueryToolLocators.query_history_selected)
|
||||
self.assertIn("SELECT * FROM %s" % self.invalid_table_name,
|
||||
selected_history_entry.text)
|
||||
|
||||
failed_history_detail_pane = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.query_history_detail)
|
||||
QueryToolLocators.query_history_detail)
|
||||
|
||||
self.assertIn(
|
||||
"Error Message relation \"table_that_doesnt_exist\" "
|
||||
"does not exist", failed_history_detail_pane.text
|
||||
"Error Message relation \"%s\" does not exist"
|
||||
% self.invalid_table_name,
|
||||
failed_history_detail_pane.text
|
||||
)
|
||||
self.page.wait_for_element(lambda driver: driver
|
||||
.find_element_by_css_selector(
|
||||
"#query_list> .query-group>ul>li"))
|
||||
self.page.wait_for_elements(
|
||||
lambda driver: driver.find_elements_by_css_selector(
|
||||
QueryToolLocators.query_history_entries))
|
||||
|
||||
# get the query history rows and click the previous query row which
|
||||
# was executed and verify it
|
||||
history_rows = self.driver.find_elements_by_css_selector(
|
||||
"#query_list> .query-group>ul>li")
|
||||
QueryToolLocators.query_history_entries)
|
||||
history_rows[1].click()
|
||||
|
||||
selected_history_entry = self.page.find_by_css_selector(
|
||||
"#query_list .selected")
|
||||
QueryToolLocators.query_history_selected)
|
||||
self.assertIn(("SELECT * FROM %s ORDER BY value" %
|
||||
self.test_table_name),
|
||||
selected_history_entry.text)
|
||||
|
||||
# check second(invalid) query also exist in the history tab with error
|
||||
newly_selected_history_entry = self.page.find_by_xpath(
|
||||
"//*[@id='query_list']/div/ul/li[1]")
|
||||
newly_selected_history_entry = history_rows[0]
|
||||
self.page.click_element(newly_selected_history_entry)
|
||||
|
||||
selected_invalid_history_entry = self.page.find_by_css_selector(
|
||||
"#query_list .selected .entry.error .query")
|
||||
invalid_history_entry = self.page.find_by_css_selector(
|
||||
QueryToolLocators.invalid_query_history_entry_css)
|
||||
|
||||
self.assertIn("SELECT * FROM table_that_doesnt_exist",
|
||||
selected_invalid_history_entry.text)
|
||||
self.assertIn("SELECT * FROM %s" % self.invalid_table_name,
|
||||
invalid_history_entry.text)
|
||||
|
||||
self.page.click_tab("Query Editor")
|
||||
self.__clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
self.page.click_element(editor_input)
|
||||
|
||||
# Check if 15 more query executed then the history should contain 17
|
||||
# entries.
|
||||
self.page.fill_codemirror_area_with("SELECT * FROM hats")
|
||||
for _ in range(15):
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab("Query History")
|
||||
|
||||
query_we_need_to_scroll_to = self.page.find_by_xpath(
|
||||
"//*[@id='query_list']/div/ul/li[17]")
|
||||
query_list = self.page.wait_for_elements(
|
||||
lambda driver: driver.find_elements_by_css_selector(
|
||||
QueryToolLocators.query_history_entries))
|
||||
|
||||
self.page.click_element(query_we_need_to_scroll_to)
|
||||
|
||||
for _ in range(17):
|
||||
ActionChains(self.page.driver) \
|
||||
.send_keys(Keys.ARROW_DOWN) \
|
||||
.perform()
|
||||
|
||||
self._assert_clickable(query_we_need_to_scroll_to)
|
||||
self.assertTrue(17, len(query_list))
|
||||
|
||||
def _test_query_sources_and_generated_queries(self):
|
||||
self.__clear_query_history()
|
||||
|
@ -193,12 +201,12 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
self.page.click_tab("Query History")
|
||||
|
||||
history_entries_icons = [
|
||||
QueryToolLocatorsCss.commit_icon,
|
||||
QueryToolLocatorsCss.save_data_icon,
|
||||
QueryToolLocatorsCss.save_data_icon,
|
||||
QueryToolLocatorsCss.execute_icon,
|
||||
QueryToolLocatorsCss.explain_analyze_icon,
|
||||
QueryToolLocatorsCss.explain_icon
|
||||
QueryToolLocators.commit_icon,
|
||||
QueryToolLocators.save_data_icon,
|
||||
QueryToolLocators.save_data_icon,
|
||||
QueryToolLocators.execute_icon,
|
||||
QueryToolLocators.explain_analyze_icon,
|
||||
QueryToolLocators.explain_icon
|
||||
]
|
||||
|
||||
history_entries_queries = [
|
||||
|
@ -217,33 +225,31 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
def _test_toggle_generated_queries(self):
|
||||
xpath = '//li[contains(@class, "pgadmin-query-history-entry")]'
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(xpath))
|
||||
toggle_el = self.page.find_by_xpath(
|
||||
'//input[@id ="generated-queries-toggle"]/..'
|
||||
)
|
||||
toggle_el.click()
|
||||
self.page.set_switch_box_status(
|
||||
QueryToolLocators.show_query_internally_btn, 'No')
|
||||
self.assertFalse(self.page.check_if_element_exist_by_xpath(xpath))
|
||||
toggle_el.click()
|
||||
self.page.set_switch_box_status(
|
||||
QueryToolLocators.show_query_internally_btn, 'Yes')
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(xpath))
|
||||
|
||||
def _test_updatable_resultset(self):
|
||||
if self.driver_version < 2.8:
|
||||
return
|
||||
|
||||
self.page.click_tab("Query Editor")
|
||||
|
||||
# Select all data (contains the primary key -> should be editable)
|
||||
self.__clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
query = "SELECT pk_column, normal_column FROM %s" \
|
||||
% self.test_editable_table_name
|
||||
self._check_query_results_editable(query, True)
|
||||
|
||||
# Select data without primary keys -> should not be editable
|
||||
self.__clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
query = "SELECT normal_column FROM %s" % self.test_editable_table_name
|
||||
self._check_query_results_editable(query, False)
|
||||
|
||||
def _execute_sources_test_queries(self):
|
||||
self.__clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
self._explain_query(
|
||||
"SELECT * FROM %s;"
|
||||
|
@ -253,17 +259,17 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
"SELECT * FROM %s;"
|
||||
% self.test_editable_table_name
|
||||
)
|
||||
self._execute_query(
|
||||
self.page.execute_query(
|
||||
"SELECT * FROM %s;"
|
||||
% self.test_editable_table_name
|
||||
)
|
||||
|
||||
# Turn off autocommit
|
||||
query_options = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_options.click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit).click()
|
||||
QueryToolLocators.btn_auto_commit).click()
|
||||
query_options.click() # Click again to close dropdown
|
||||
|
||||
self._update_numeric_cell(2, 10)
|
||||
|
@ -272,24 +278,25 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
|
||||
# Turn on autocommit
|
||||
query_options = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_options.click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit).click()
|
||||
QueryToolLocators.btn_auto_commit).click()
|
||||
query_options.click() # Click again to close dropdown
|
||||
|
||||
def _check_history_queries_and_icons(self, history_queries, history_icons):
|
||||
# Select first query history entry
|
||||
self.page.find_by_xpath("//*[@id='query_list']/div/ul/li[1]").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.query_history_specific_entry.format(1)).click()
|
||||
for icon, query in zip(history_icons, history_queries):
|
||||
# Check query
|
||||
query_history_selected_item = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.query_history_selected
|
||||
QueryToolLocators.query_history_selected
|
||||
)
|
||||
self.assertIn(query, query_history_selected_item.text)
|
||||
# Check source icon
|
||||
query_history_selected_icon = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.query_history_selected_icon)
|
||||
QueryToolLocators.query_history_selected_icon)
|
||||
icon_classes = query_history_selected_icon.get_attribute('class')
|
||||
icon_classes = icon_classes.split(" ")
|
||||
self.assertTrue(icon in icon_classes)
|
||||
|
@ -302,47 +309,37 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
"""
|
||||
Updates a numeric cell in the first row of the resultset
|
||||
"""
|
||||
xpath = '//div[contains(@class, "slick-row") and ' \
|
||||
'contains(@style, "top:0px")]'
|
||||
xpath += '/div[contains(@class, "slick-cell") and ' \
|
||||
'contains(@class, "r' + str(cell_index) + '")]'
|
||||
cell_el = self.page.find_by_xpath(xpath)
|
||||
self.page.check_if_element_exist_by_xpath(
|
||||
"//div[contains(@style, 'top:0px')]//div[contains(@class, "
|
||||
"'l{0} r{1}')]".format(cell_index, cell_index))
|
||||
cell_el = self.page.find_by_xpath(
|
||||
"//div[contains(@style, 'top:0px')]//div[contains(@class, "
|
||||
"'l{0} r{1}')]".format(cell_index, cell_index))
|
||||
ActionChains(self.driver).double_click(cell_el).perform()
|
||||
ActionChains(self.driver).send_keys(value). \
|
||||
send_keys(Keys.ENTER).perform()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_save_data).click()
|
||||
QueryToolLocators.btn_save_data).click()
|
||||
|
||||
def _insert_data_into_test_editable_table(self):
|
||||
self.page.click_tab("Query Editor")
|
||||
self.__clear_query_tool()
|
||||
self._execute_query(
|
||||
self.page.clear_query_tool()
|
||||
self.page.execute_query(
|
||||
"INSERT INTO %s VALUES (1, 1), (2, 2);"
|
||||
% self.test_editable_table_name
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
def __clear_query_history(self):
|
||||
self.page.click_element(
|
||||
self.page.find_by_xpath("//*[@id='btn-clear-dropdown']")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_clear_dropdown)
|
||||
)
|
||||
ActionChains(self.driver)\
|
||||
.move_to_element(
|
||||
self.page.find_by_xpath(
|
||||
"//*[@id='btn-clear-history']")).perform()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_clear_history)).perform()
|
||||
self.page.click_element(
|
||||
self.page.find_by_xpath("//*[@id='btn-clear-history']")
|
||||
self.page.find_by_css_selector(QueryToolLocators.btn_clear_history)
|
||||
)
|
||||
self.page.click_modal('Yes')
|
||||
|
||||
|
@ -353,44 +350,39 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
self.page.open_query_tool()
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
|
||||
def _execute_query(self, query):
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
def _explain_query(self, query):
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain).click()
|
||||
QueryToolLocators.btn_explain).click()
|
||||
|
||||
def _explain_analyze_query(self, query):
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_analyze).click()
|
||||
QueryToolLocators.btn_explain_analyze).click()
|
||||
|
||||
def _commit_transaction(self):
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_commit).click()
|
||||
QueryToolLocators.btn_commit).click()
|
||||
|
||||
def _assert_clickable(self, element):
|
||||
self.page.click_element(element)
|
||||
|
||||
def _check_query_results_editable(self, query, should_be_editable):
|
||||
self._execute_query(query)
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
self.page.execute_query(query)
|
||||
# Check if the first cell in the first row is editable
|
||||
is_editable = self._check_cell_editable(1)
|
||||
self.assertEqual(is_editable, should_be_editable)
|
||||
|
||||
def _check_cell_editable(self, cell_index):
|
||||
"""
|
||||
Checks if a cell in the first row of the resultset is editable
|
||||
"""
|
||||
xpath = '//div[contains(@class, "slick-row") and ' \
|
||||
'contains(@style, "top:0px")]'
|
||||
xpath += '/div[contains(@class, "slick-cell") and ' \
|
||||
'contains(@class, "r' + str(cell_index) + '")]'
|
||||
cell_el = self.page.find_by_xpath(xpath)
|
||||
"""Checks if a cell in the first row of the resultset is editable"""
|
||||
|
||||
self.page.check_if_element_exist_by_xpath(
|
||||
"//div[contains(@style, 'top:0px')]//div[contains(@class, "
|
||||
"'l{0} r{1}')]".format(cell_index, cell_index))
|
||||
cell_el = self.page.find_by_xpath(
|
||||
"//div[contains(@style, 'top:0px')]//div[contains(@class, "
|
||||
"'l{0} r{1}')]".format(cell_index, cell_index))
|
||||
|
||||
# Get existing value
|
||||
cell_value = int(cell_el.text)
|
||||
new_value = cell_value + 1
|
||||
|
@ -401,6 +393,12 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||
# Check if the value was updated
|
||||
return int(cell_el.text) == new_value
|
||||
|
||||
def _check_can_add_row(self):
|
||||
return self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.new_row_xpath)
|
||||
|
||||
def after(self):
|
||||
self.page.close_query_tool()
|
||||
self.page.remove_server(self.server)
|
||||
test_utils.delete_table(
|
||||
self.server, self.test_db, self.test_table_name)
|
||||
|
|
|
@ -10,15 +10,16 @@
|
|||
from __future__ import print_function
|
||||
import sys
|
||||
|
||||
from selenium.common.exceptions import StaleElementReferenceException
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.common.exceptions import StaleElementReferenceException, \
|
||||
ElementClickInterceptedException
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.common.by import By
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
import config
|
||||
from .locators import QueryToolLocatorsCss
|
||||
from regression.feature_utils.locators import \
|
||||
QueryToolLocators
|
||||
|
||||
|
||||
class QueryToolFeatureTest(BaseFeatureTest):
|
||||
|
@ -33,17 +34,20 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
def before(self):
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
self.page.add_server(self.server)
|
||||
self._locate_database_tree_node()
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
self.page.open_query_tool()
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
self._reset_options()
|
||||
self.wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
def runTest(self):
|
||||
# on demand result set on scrolling.
|
||||
print("\nOn demand query result... ",
|
||||
file=sys.stderr, end="")
|
||||
self._on_demand_result()
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
# explain query with verbose and cost
|
||||
print("Explain query with verbose and cost... ",
|
||||
|
@ -51,7 +55,7 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
if self._supported_server_version():
|
||||
self._query_tool_explain_with_verbose_and_cost()
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
else:
|
||||
print("Skipped.", file=sys.stderr)
|
||||
|
||||
|
@ -61,7 +65,7 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
if self._supported_server_version():
|
||||
self._query_tool_explain_analyze_with_buffers_and_timing()
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
else:
|
||||
print("Skipped.", file=sys.stderr)
|
||||
|
||||
|
@ -69,30 +73,30 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
print("Auto commit disabled... ", file=sys.stderr, end="")
|
||||
self._query_tool_auto_commit_disabled()
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
# auto commit enabled.
|
||||
print("Auto commit enabled... ", file=sys.stderr, end="")
|
||||
self._query_tool_auto_commit_enabled()
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
# auto rollback enabled.
|
||||
print("Auto rollback enabled...", file=sys.stderr, end="")
|
||||
self._query_tool_auto_rollback_enabled()
|
||||
print(" OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
# cancel query.
|
||||
print("Cancel query... ", file=sys.stderr, end="")
|
||||
self._query_tool_cancel_query()
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
# Notify Statements.
|
||||
print("Capture Notify Statements... ", file=sys.stderr, end="")
|
||||
self._query_tool_notify_statements()
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
# explain query with JIT stats
|
||||
print("Explain query with JIT stats... ",
|
||||
|
@ -100,7 +104,7 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
if self._supported_jit_on_server():
|
||||
self._query_tool_explain_check_jit_stats()
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
else:
|
||||
print("Skipped.", file=sys.stderr)
|
||||
|
||||
|
@ -112,33 +116,33 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
self.page.fill_codemirror_area_with('')
|
||||
|
||||
explain_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_options_dropdown)
|
||||
QueryToolLocators.btn_explain_options_dropdown)
|
||||
explain_op.click()
|
||||
|
||||
# disable Explain options and auto rollback only if they are enabled.
|
||||
for op in (QueryToolLocatorsCss.btn_explain_verbose,
|
||||
QueryToolLocatorsCss.btn_explain_costs,
|
||||
QueryToolLocatorsCss.btn_explain_buffers,
|
||||
QueryToolLocatorsCss.btn_explain_timing):
|
||||
for op in (QueryToolLocators.btn_explain_verbose,
|
||||
QueryToolLocators.btn_explain_costs,
|
||||
QueryToolLocators.btn_explain_buffers,
|
||||
QueryToolLocators.btn_explain_timing):
|
||||
btn = self.page.find_by_css_selector(op)
|
||||
check = btn.find_element_by_tag_name('i')
|
||||
if 'visibility-hidden' not in check.get_attribute('class'):
|
||||
btn.click()
|
||||
|
||||
query_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_op.click()
|
||||
|
||||
# disable auto rollback only if they are enabled
|
||||
btn = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_rollback)
|
||||
QueryToolLocators.btn_auto_rollback)
|
||||
check = btn.find_element_by_tag_name('i')
|
||||
if 'visibility-hidden' not in check.get_attribute('class'):
|
||||
btn.click()
|
||||
|
||||
# enable autocommit only if it's disabled
|
||||
btn = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit)
|
||||
QueryToolLocators.btn_auto_commit)
|
||||
check = btn.find_element_by_tag_name('i')
|
||||
if 'visibility-hidden' in check.get_attribute('class'):
|
||||
btn.click()
|
||||
|
@ -146,23 +150,6 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
|||
# close menu
|
||||
query_op.click()
|
||||
|
||||
def _locate_database_tree_node(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
|
||||
def _clear_query_tool(self):
|
||||
self.page.click_element(self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_clear_dropdown)
|
||||
)
|
||||
ActionChains(self.driver) \
|
||||
.move_to_element(self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_clear)).perform()
|
||||
self.page.click_element(
|
||||
self.page.find_by_css_selector(QueryToolLocatorsCss.btn_clear)
|
||||
)
|
||||
self.page.click_modal('Yes')
|
||||
|
||||
def _on_demand_result(self):
|
||||
ON_DEMAND_CHUNKS = 2
|
||||
row_id_to_find = config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS
|
||||
|
@ -175,126 +162,133 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format(
|
|||
|
||||
print("\nOn demand result set on scrolling... ",
|
||||
file=sys.stderr, end="")
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
self.page.execute_query(query)
|
||||
|
||||
# wait for header of the table to be visible
|
||||
wait.until(EC.visibility_of_element_located(
|
||||
(By.XPATH, '//div[@class="slick-header-columns"]')))
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH,
|
||||
'//span[@data-row="0" and text()="1"]'))
|
||||
)
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
QueryToolLocators.query_output_cells)))
|
||||
|
||||
# scroll to bottom to fetch next chunk of result set.
|
||||
self.driver.execute_script(
|
||||
"pgAdmin.SqlEditor.jquery('.slick-viewport')"
|
||||
".scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas').height());"
|
||||
)
|
||||
canvas = self.page.find_by_css_selector(
|
||||
QueryToolLocators.query_output_canvas_css)
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")))
|
||||
|
||||
self._check_ondemand_result(row_id_to_find, canvas)
|
||||
self._check_ondemand_result(row_id_to_find)
|
||||
print("OK.", file=sys.stderr)
|
||||
|
||||
print("On demand result set on grid select all... ",
|
||||
file=sys.stderr, end="")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
# wait for header of the table to be visible
|
||||
wait.until(EC.visibility_of_element_located(
|
||||
(By.XPATH, '//div[@class="slick-header-columns"]')))
|
||||
canvas = self.page.find_by_css_selector(
|
||||
QueryToolLocators.query_output_canvas_css)
|
||||
|
||||
# wait for first row to contain value
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH,
|
||||
'//span[@data-row="0" and text()="1"]'))
|
||||
# wait for the rows in the table to be displayed
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
QueryToolLocators.query_output_cells))
|
||||
)
|
||||
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, ".slick-header-column"))).click()
|
||||
# Select all rows in a table
|
||||
multiple_check = True
|
||||
count = 0
|
||||
while multiple_check:
|
||||
try:
|
||||
select_all = self.wait.until(EC.element_to_be_clickable(
|
||||
(By.XPATH, QueryToolLocators.select_all_column)))
|
||||
select_all.click()
|
||||
multiple_check = False
|
||||
except (StaleElementReferenceException,
|
||||
ElementClickInterceptedException):
|
||||
count += 1
|
||||
pass
|
||||
print(count)
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")))
|
||||
|
||||
self._check_ondemand_result(row_id_to_find, canvas)
|
||||
self._check_ondemand_result(row_id_to_find)
|
||||
print("OK.", file=sys.stderr)
|
||||
|
||||
print("On demand result set on column select all... ",
|
||||
file=sys.stderr, end="")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
# wait for header of the table to be visible
|
||||
wait.until(EC.visibility_of_element_located(
|
||||
(By.XPATH, '//div[@class="slick-header-columns"]')))
|
||||
self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH,
|
||||
'//span[@data-row="0" and text()="1"]'))
|
||||
# wait for the rows in the table to be displayed
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
QueryToolLocators.query_output_cells))
|
||||
)
|
||||
|
||||
# click on first data column to select all column.
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(
|
||||
By.XPATH,
|
||||
"//span[contains(@class, 'column-name') "
|
||||
"and contains(., 'id1')]"))
|
||||
).click()
|
||||
column_1 = \
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_column_header_css.format('id1'))
|
||||
column_1.click()
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")))
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
self._check_ondemand_result(row_id_to_find, canvas)
|
||||
self._check_ondemand_result(row_id_to_find)
|
||||
print("OK.", file=sys.stderr)
|
||||
|
||||
def _check_ondemand_result(self, row_id_to_find, canvas):
|
||||
def _check_ondemand_result(self, row_id_to_find):
|
||||
# scroll to bottom to bring last row of next chunk in viewport.
|
||||
self.driver.execute_script(
|
||||
"pgAdmin.SqlEditor.jquery('.slick-viewport')"
|
||||
".scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas').height());"
|
||||
)
|
||||
# canvas_ele = self.page.find_by_css_selector()
|
||||
scroll = 10
|
||||
while scroll:
|
||||
canvas_ele = self.page.find_by_css_selector('.grid-canvas')
|
||||
scrolling_height = canvas_ele.size['height']
|
||||
self.driver.execute_script(
|
||||
"pgAdmin.SqlEditor.jquery('.slick-viewport')"
|
||||
".scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas')"
|
||||
".height());"
|
||||
)
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
if canvas_ele.size['height'] == scrolling_height:
|
||||
break
|
||||
else:
|
||||
scroll -= 1
|
||||
|
||||
canvas.find_element_by_xpath(
|
||||
'//span[text()="{}"]'.format(row_id_to_find)
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.output_column_data_xpath.format(row_id_to_find)
|
||||
))
|
||||
|
||||
def _query_tool_explain_with_verbose_and_cost(self):
|
||||
query = """-- Explain query with verbose and cost
|
||||
SELECT generate_series(1, 1000) as id order by id desc"""
|
||||
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
explain_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_options_dropdown)
|
||||
QueryToolLocators.btn_explain_options_dropdown)
|
||||
explain_op.click()
|
||||
|
||||
# disable Explain options and auto rollback only if they are enabled.
|
||||
for op in (QueryToolLocatorsCss.btn_explain_verbose,
|
||||
QueryToolLocatorsCss.btn_explain_costs):
|
||||
for op in (QueryToolLocators.btn_explain_verbose,
|
||||
QueryToolLocators.btn_explain_costs):
|
||||
self.page.find_by_css_selector(op).click()
|
||||
|
||||
explain_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain).click()
|
||||
QueryToolLocators.btn_explain).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab('Data Output')
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css))
|
||||
)
|
||||
|
||||
# Search for 'Output' word in result (verbose option)
|
||||
|
@ -307,40 +301,38 @@ SELECT generate_series(1, 1000) as id order by id desc"""
|
|||
query = """-- Explain analyze query with buffers and timing
|
||||
SELECT generate_series(1, 1000) as id order by id desc"""
|
||||
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
explain_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_options_dropdown)
|
||||
QueryToolLocators.btn_explain_options_dropdown)
|
||||
explain_op.click()
|
||||
|
||||
# disable Explain options and auto rollback only if they are enabled.
|
||||
for op in (QueryToolLocatorsCss.btn_explain_buffers,
|
||||
QueryToolLocatorsCss.btn_explain_timing):
|
||||
for op in (QueryToolLocators.btn_explain_buffers,
|
||||
QueryToolLocators.btn_explain_timing):
|
||||
self.page.find_by_css_selector(op).click()
|
||||
|
||||
explain_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_analyze).click()
|
||||
QueryToolLocators.btn_explain_analyze).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab('Data Output')
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))
|
||||
)
|
||||
# Search for 'Shared Read Blocks' word in result (buffers option)
|
||||
canvas.find_element_by_xpath(
|
||||
"//*[contains(string(), 'Shared Read Blocks')]"
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, QueryToolLocators.output_cell_xpath.format(1, 1)))
|
||||
)
|
||||
|
||||
result = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_cell_xpath.format(1, 1))
|
||||
|
||||
# Search for 'Shared Read Blocks' word in result (buffers option)
|
||||
self.assertIn('Shared Read Blocks', result.text)
|
||||
|
||||
# Search for 'Actual Total Time' word in result (timing option)
|
||||
canvas.find_element_by_xpath(
|
||||
"//*[contains(string(), 'Actual Total Time')]"
|
||||
)
|
||||
self.assertIn('Actual Total Time', result.text)
|
||||
|
||||
def _query_tool_auto_commit_disabled(self):
|
||||
table_name = 'query_tool_auto_commit_disabled_table'
|
||||
|
@ -349,32 +341,31 @@ SELECT generate_series(1, 1000) as id order by id desc"""
|
|||
-- 3. ROLLBACK transaction.
|
||||
-- 4. Check if table is *NOT* created.
|
||||
CREATE TABLE public.{}();""".format(table_name)
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
# open auto commit option and disable it
|
||||
query_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_op.click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit).click()
|
||||
QueryToolLocators.btn_auto_commit).click()
|
||||
# close option
|
||||
query_op.click()
|
||||
|
||||
# execute query
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "CREATE TABLE")]'
|
||||
)
|
||||
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('CREATE TABLE')),
|
||||
"CREATE TABLE message does not displayed")
|
||||
|
||||
# do the ROLLBACK and check if the table is present or not
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
query = """-- 1. (Done) Disable auto commit.
|
||||
-- 2. (Done) Create table in public schema.
|
||||
-- 3. ROLLBACK transaction.
|
||||
|
@ -382,16 +373,15 @@ CREATE TABLE public.{}();""".format(table_name)
|
|||
ROLLBACK;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "ROLLBACK")]'
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('ROLLBACK')),
|
||||
"ROLLBACK message does not displayed")
|
||||
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
query = """-- 1. (Done) Disable auto commit.
|
||||
-- 2. (Done) Create table in public schema.
|
||||
-- 3. (Done) ROLLBACK transaction.
|
||||
|
@ -400,16 +390,15 @@ SELECT relname FROM pg_class
|
|||
WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Data Output')
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")))
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
el = canvas.find_elements_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and "
|
||||
"contains(text(), '{}')]".format(table_name))
|
||||
QueryToolLocators.output_column_data_xpath.format(table_name))
|
||||
|
||||
assert len(el) == 0, "Table '{}' created with auto commit disabled " \
|
||||
"and without any explicit commit.".format(
|
||||
|
@ -423,7 +412,7 @@ SELECT relname FROM pg_class
|
|||
ROLLBACK;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
|
@ -437,23 +426,21 @@ END;"""
|
|||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
query_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit).click()
|
||||
QueryToolLocators.btn_auto_commit).click()
|
||||
|
||||
query_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
table_name = 'query_tool_auto_commit_enabled_table'
|
||||
query = """-- 1. (Done) END any open transaction.
|
||||
|
@ -463,37 +450,29 @@ END;"""
|
|||
-- 5. Check if table is created event after ROLLBACK.
|
||||
CREATE TABLE public.{}();""".format(table_name)
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "CREATE TABLE")]'
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('CREATE TABLE')),
|
||||
"CREATE TABLE message does not displayed")
|
||||
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
query = """-- 1. (Done) END any open transaction if any.
|
||||
-- 2. (Done) Enable auto commit.
|
||||
-- 3. (Done) Create table in public schema.
|
||||
-- 4. ROLLBACK transaction
|
||||
-- 5. Check if table is created event after ROLLBACK.
|
||||
ROLLBACK;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "ROLLBACK")]'
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('ROLLBACK')),
|
||||
"ROLLBACK message does not displayed")
|
||||
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
query = """-- 1. (Done) END any open transaction if any.
|
||||
-- 2. (Done) Enable auto commit.
|
||||
-- 3. (Done) Create table in public schema.
|
||||
|
@ -503,17 +482,16 @@ SELECT relname FROM pg_class
|
|||
WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.click_tab('Data Output')
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")))
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
el = canvas.find_elements_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and "
|
||||
"contains(text(), '{}')]".format(table_name))
|
||||
QueryToolLocators.output_column_data_xpath.format(table_name))
|
||||
|
||||
assert len(el) != 0, "Table '{}' is not created with auto " \
|
||||
"commit enabled.".format(table_name)
|
||||
|
@ -527,12 +505,11 @@ SELECT relname FROM pg_class
|
|||
-- 5. END transaction.
|
||||
-- 6. Check if table is *NOT* created after ending transaction.
|
||||
END;"""
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
query_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_op.click()
|
||||
|
||||
# uncheckt auto commit and check auto-rollback
|
||||
|
@ -542,10 +519,10 @@ END;"""
|
|||
query_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
query = """-- 1. (Done) END any open transaction.
|
||||
-- 2. Enable auto rollback and disable auto commit.
|
||||
|
@ -554,21 +531,14 @@ END;"""
|
|||
-- 5. END transaction.
|
||||
-- 6. Check if table is *NOT* created after ending transaction.
|
||||
CREATE TABLE public.{}();""".format(table_name)
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "CREATE TABLE")]'
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('CREATE TABLE')),
|
||||
"CREATE TABLE message does not displayed")
|
||||
self.page.clear_query_tool()
|
||||
|
||||
self._clear_query_tool()
|
||||
query = """-- 1. (Done) END any open transaction.
|
||||
-- 2. (Done) Enable auto rollback and disable auto commit.
|
||||
-- 3. (Done) Create table in public schema.
|
||||
|
@ -576,18 +546,14 @@ CREATE TABLE public.{}();""".format(table_name)
|
|||
-- 5. END transaction.
|
||||
-- 6. Check if table is *NOT* created after ending transaction.
|
||||
SELECT 1/0;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "division by zero")]'
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('division by zero')),
|
||||
"division by zero message does not displayed")
|
||||
self.page.clear_query_tool()
|
||||
|
||||
self._clear_query_tool()
|
||||
query = """-- 1. (Done) END any open transaction.
|
||||
-- 2. (Done) Enable auto rollback and disable auto commit.
|
||||
-- 3. (Done) Create table in public schema.
|
||||
|
@ -595,19 +561,15 @@ SELECT 1/0;"""
|
|||
-- 5. END transaction.
|
||||
-- 6. Check if table is *NOT* created after ending transaction.
|
||||
END;"""
|
||||
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'contains(string(), "Query returned successfully")]'
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.
|
||||
format('Query returned successfully')),
|
||||
"Query returned successfully message does not displayed")
|
||||
self.page.clear_query_tool()
|
||||
|
||||
self._clear_query_tool()
|
||||
query = """-- 1. (Done) END any open transaction.
|
||||
-- 2. (Done) Enable auto rollback and disable auto commit.
|
||||
-- 3. (Done) Create table in public schema.
|
||||
|
@ -616,18 +578,14 @@ END;"""
|
|||
-- 6. Check if table is *NOT* created after ending transaction.
|
||||
SELECT relname FROM pg_class
|
||||
WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Data Output')
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")))
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
el = canvas.find_elements_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and "
|
||||
"contains(text(), '{}')]".format(table_name))
|
||||
QueryToolLocators.output_column_data_xpath.format(table_name))
|
||||
|
||||
assert len(el) == 0, "Table '{}' created even after ROLLBACK due to " \
|
||||
"sql error.".format(table_name)
|
||||
|
@ -648,7 +606,7 @@ SELECT 1, pg_sleep(300)"""
|
|||
commit_button.click()
|
||||
|
||||
query_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_query_dropdown)
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_op.click()
|
||||
|
||||
# enable auto-commit and disable auto-rollback
|
||||
|
@ -658,18 +616,18 @@ SELECT 1, pg_sleep(300)"""
|
|||
query_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.find_by_xpath("//*[@id='fetching_data']")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_cancel_query).click()
|
||||
QueryToolLocators.btn_cancel_query).click()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.find_by_xpath(
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
'//div[contains(@class, "sql-editor-message") and '
|
||||
'(contains(string(), "canceling statement due to user request") '
|
||||
'or contains(string(), "Execution Cancelled!"))]'
|
||||
)
|
||||
))
|
||||
|
||||
def _supported_server_version(self):
|
||||
connection = test_utils.get_db_connection(
|
||||
|
@ -683,48 +641,33 @@ SELECT 1, pg_sleep(300)"""
|
|||
return connection.server_version > 90100
|
||||
|
||||
def _query_tool_notify_statements(self):
|
||||
wait = WebDriverWait(self.page.driver, 60)
|
||||
|
||||
print("\n\tListen on an event... ", file=sys.stderr, end="")
|
||||
self.page.fill_codemirror_area_with("LISTEN foo;")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.execute_query("LISTEN foo;")
|
||||
self.page.click_tab('Messages')
|
||||
|
||||
wait.until(EC.text_to_be_present_in_element(
|
||||
(By.CSS_SELECTOR, ".sql-editor-message"), "LISTEN")
|
||||
)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('LISTEN')),
|
||||
"LISTEN message does not displayed")
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("\tNotify event without data... ", file=sys.stderr, end="")
|
||||
self.page.fill_codemirror_area_with("NOTIFY foo;")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.execute_query("NOTIFY foo;")
|
||||
self.page.click_tab('Notifications')
|
||||
wait.until(EC.text_to_be_present_in_element(
|
||||
self.wait.until(EC.text_to_be_present_in_element(
|
||||
(By.CSS_SELECTOR, "td.channel"), "foo")
|
||||
)
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
print("\tNotify event with data... ", file=sys.stderr, end="")
|
||||
if self._supported_server_version():
|
||||
self.page.fill_codemirror_area_with("SELECT pg_notify('foo', "
|
||||
"'Hello')")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.execute_query("SELECT pg_notify('foo', 'Hello')")
|
||||
self.page.click_tab('Notifications')
|
||||
wait.until(WaitForAnyElementWithText(
|
||||
self.wait.until(WaitForAnyElementWithText(
|
||||
(By.CSS_SELECTOR, 'td.payload'), "Hello"))
|
||||
print("OK.", file=sys.stderr)
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
else:
|
||||
print("Skipped.", file=sys.stderr)
|
||||
|
||||
|
@ -756,69 +699,63 @@ SELECT 1, pg_sleep(300)"""
|
|||
is_edb = 'EnterpriseDB' in version_string[0]
|
||||
|
||||
connection.close()
|
||||
|
||||
return connection.server_version >= 110000 and jit_enabled
|
||||
|
||||
def _query_tool_explain_check_jit_stats(self):
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
self.page.fill_codemirror_area_with("SET jit_above_cost=10;")
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_execute_query).click()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self._clear_query_tool()
|
||||
self.page.execute_query("SET jit_above_cost=10;")
|
||||
self.page.clear_query_tool()
|
||||
|
||||
self.page.fill_codemirror_area_with("SELECT count(*) FROM pg_class;")
|
||||
|
||||
explain_op = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_options_dropdown)
|
||||
QueryToolLocators.btn_explain_options_dropdown)
|
||||
explain_op.click()
|
||||
|
||||
# disable Explain options and only enable COST option
|
||||
for op in (QueryToolLocatorsCss.btn_explain_verbose,
|
||||
QueryToolLocatorsCss.btn_explain_costs,
|
||||
QueryToolLocatorsCss.btn_explain_buffers,
|
||||
QueryToolLocatorsCss.btn_explain_timing):
|
||||
for op in (QueryToolLocators.btn_explain_verbose,
|
||||
QueryToolLocators.btn_explain_costs,
|
||||
QueryToolLocators.btn_explain_buffers,
|
||||
QueryToolLocators.btn_explain_timing):
|
||||
btn = self.page.find_by_css_selector(op)
|
||||
check = btn.find_element_by_tag_name('i')
|
||||
if 'visibility-hidden' not in check.get_attribute('class'):
|
||||
btn.click()
|
||||
# click cost button
|
||||
cost_btn = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_costs)
|
||||
QueryToolLocators.btn_explain_costs)
|
||||
cost_btn.click()
|
||||
|
||||
# close explain options
|
||||
explain_op.click()
|
||||
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_explain_analyze).click()
|
||||
QueryToolLocators.btn_explain_analyze).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Data Output')
|
||||
|
||||
canvas = wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css))
|
||||
)
|
||||
# Search for 'Output' word in result (verbose option)
|
||||
canvas.find_element_by_xpath("//*[contains(string(), 'JIT')]")
|
||||
|
||||
self._clear_query_tool()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
def check_execute_option(self, option):
|
||||
""""This function will check auto commit or auto roll back based on
|
||||
user input. If button is already checked, no action will be taken"""
|
||||
if option == 'auto_commit':
|
||||
check_status = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit_check_status)
|
||||
QueryToolLocators.btn_auto_commit_check_status)
|
||||
if 'visibility-hidden' in check_status.get_attribute('class'):
|
||||
self.page.find_by_css_selector(QueryToolLocatorsCss.
|
||||
self.page.find_by_css_selector(QueryToolLocators.
|
||||
btn_auto_commit).click()
|
||||
if option == 'auto_rollback':
|
||||
check_status = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_rollback_check_status)
|
||||
QueryToolLocators.btn_auto_rollback_check_status)
|
||||
if 'visibility-hidden' in check_status.get_attribute('class'):
|
||||
self.page.find_by_css_selector(QueryToolLocatorsCss.
|
||||
self.page.find_by_css_selector(QueryToolLocators.
|
||||
btn_auto_rollback).click()
|
||||
|
||||
def uncheck_execute_option(self, option):
|
||||
|
@ -826,15 +763,15 @@ SELECT 1, pg_sleep(300)"""
|
|||
user input. If button is already unchecked, no action will be taken"""
|
||||
if option == 'auto_commit':
|
||||
check_status = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_commit_check_status)
|
||||
QueryToolLocators.btn_auto_commit_check_status)
|
||||
if 'visibility-hidden' not in check_status.get_attribute('class'):
|
||||
self.page.find_by_css_selector(QueryToolLocatorsCss.
|
||||
self.page.find_by_css_selector(QueryToolLocators.
|
||||
btn_auto_commit).click()
|
||||
if option == 'auto_rollback':
|
||||
check_status = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_auto_rollback_check_status)
|
||||
QueryToolLocators.btn_auto_rollback_check_status)
|
||||
if 'visibility-hidden' not in check_status.get_attribute('class'):
|
||||
self.page.find_by_css_selector(QueryToolLocatorsCss.
|
||||
self.page.find_by_css_selector(QueryToolLocators.
|
||||
btn_auto_rollback).click()
|
||||
|
||||
|
||||
|
|
|
@ -40,9 +40,13 @@ class TableDdlFeatureTest(BaseFeatureTest):
|
|||
self.page.select_tree_item(self.test_table_name)
|
||||
self.page.click_tab("SQL")
|
||||
|
||||
self.page.find_by_xpath(
|
||||
# Wait till data is displayed in SQL Tab
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
"//*[contains(@class,'CodeMirror-lines') and "
|
||||
"contains(.,'CREATE TABLE public.%s')]" % self.test_table_name)
|
||||
"contains(.,'CREATE TABLE public.%s')]" % self.test_table_name,
|
||||
10), "No data displayed in SQL tab")
|
||||
|
||||
def after(self):
|
||||
self.page.remove_server(self.server)
|
||||
test_utils.delete_table(
|
||||
self.server, self.test_db, self.test_table_name)
|
||||
|
|
|
@ -18,7 +18,8 @@ from selenium.webdriver.common.by import By
|
|||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from .locators import QueryToolLocatorsCss
|
||||
from regression.feature_utils.locators import QueryToolLocators, \
|
||||
NavMenuLocators
|
||||
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
@ -26,8 +27,6 @@ config_data = config_data_json = {}
|
|||
# try:
|
||||
with open(CURRENT_PATH + '/test_data.json') as data_file:
|
||||
config_data_json = json.load(data_file)
|
||||
# except Exception as e:
|
||||
# print(str(e))
|
||||
|
||||
|
||||
class CheckForViewDataTest(BaseFeatureTest):
|
||||
|
@ -121,7 +120,13 @@ CREATE TABLE public.nonintpkey
|
|||
def runTest(self):
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
self.page.add_server(self.server)
|
||||
self._tables_node_expandable()
|
||||
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
self.page.toggle_open_tree_item('Schemas')
|
||||
self.page.toggle_open_tree_item('public')
|
||||
self.page.toggle_open_tree_item('Tables')
|
||||
|
||||
self._load_config_data('table_insert_update_cases')
|
||||
# iterate on both tables
|
||||
|
@ -134,6 +139,10 @@ CREATE TABLE public.nonintpkey
|
|||
|
||||
def after(self):
|
||||
self.page.remove_server(self.server)
|
||||
for cnt in (1, 2):
|
||||
test_utils.delete_table(
|
||||
self.server, self.test_db, 'defaults_{0}'.format(str(cnt)))
|
||||
test_utils.delete_table(self.server, self.test_db, 'nonintpkey')
|
||||
|
||||
@staticmethod
|
||||
def _get_cell_xpath(cell, row):
|
||||
|
@ -227,14 +236,14 @@ CREATE TABLE public.nonintpkey
|
|||
send_keys(Keys.ENTER).perform()
|
||||
elif cell_type in ['text', 'json', 'text[]', 'boolean[]']:
|
||||
text_area_ele = self.page.find_by_css_selector(
|
||||
".pg-text-editor > textarea")
|
||||
QueryToolLocators.row_editor_text_area_css)
|
||||
text_area_ele.clear()
|
||||
text_area_ele.click()
|
||||
text_area_ele.send_keys(value)
|
||||
|
||||
# Click on editor's Save button
|
||||
self.page.find_by_css_selector(
|
||||
'.btn.btn-primary.long_text_editor').click()
|
||||
QueryToolLocators.text_editor_ok_btn_css).click()
|
||||
else:
|
||||
# Boolean editor test for to True click
|
||||
if data[1] == 'true':
|
||||
|
@ -250,24 +259,19 @@ CREATE TABLE public.nonintpkey
|
|||
# Sets false
|
||||
ActionChains(self.driver).click(checkbox_el).perform()
|
||||
|
||||
def _tables_node_expandable(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item(self.test_db)
|
||||
self.page.toggle_open_tree_item('Schemas')
|
||||
self.page.toggle_open_tree_item('public')
|
||||
self.page.toggle_open_tree_item('Tables')
|
||||
|
||||
def _view_data_grid(self, table_name):
|
||||
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("View/Edit Data")
|
||||
self.page.driver.find_element_by_link_text(
|
||||
NavMenuLocators.view_data_link_text)
|
||||
).perform()
|
||||
self.page.find_by_partial_link_text("All Rows").click()
|
||||
time.sleep(1)
|
||||
|
||||
# wait until datagrid frame is loaded.
|
||||
self.page.wait_for_query_tool_loading_indicator_to_appear()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab(table_name)
|
||||
|
||||
|
@ -284,8 +288,10 @@ CREATE TABLE public.nonintpkey
|
|||
row0_cell0_xpath = CheckForViewDataTest._get_cell_xpath("r0", 1)
|
||||
|
||||
self.page.find_by_xpath(row0_cell0_xpath).click()
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
self.page.find_by_xpath("//*[@id='btn-paste-row']").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.copy_button_css).click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.paste_button_css).click()
|
||||
|
||||
# Update primary key of copied cell
|
||||
self._add_update_save_row(config_data['copy'], row=2)
|
||||
|
@ -305,7 +311,7 @@ CREATE TABLE public.nonintpkey
|
|||
time.sleep(0.2)
|
||||
self._update_cell(cell_xpath, data[str(idx)])
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.btn_save_data).click()
|
||||
QueryToolLocators.btn_save_data).click()
|
||||
# There should be some delay after save button is clicked, as it
|
||||
# takes some time to complete save ajax call otherwise discard unsaved
|
||||
# changes dialog will appear if we try to execute query before previous
|
||||
|
@ -320,11 +326,12 @@ CREATE TABLE public.nonintpkey
|
|||
|
||||
def _verify_messsages(self, text):
|
||||
messages_ele = self.page.find_by_css_selector(
|
||||
QueryToolLocatorsCss.query_messages_panel)
|
||||
QueryToolLocators.query_messages_panel)
|
||||
self.assertEquals(text, messages_ele.text)
|
||||
|
||||
def _verify_row_data(self, is_new_row, config_check_data):
|
||||
self.page.find_by_id("btn-flash").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
# First row if row height = 0, second row if its 25
|
||||
row_height = 0 if is_new_row else 25
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import random
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from selenium.webdriver import ActionChains
|
||||
import sys
|
||||
from selenium.common.exceptions import StaleElementReferenceException
|
||||
from regression.feature_utils.locators import QueryToolLocators
|
||||
|
||||
|
||||
class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
|
@ -32,7 +35,8 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
|||
scenarios = [
|
||||
("Test XSS check for panels and query tool", dict())
|
||||
]
|
||||
test_table_name = "<h1>X"
|
||||
test_table_name = "<h1>X" + str(random.randint(1000, 3000))
|
||||
# test_table_name = "<h1>X"
|
||||
test_type_name = '"<script>alert(1)</script>"'
|
||||
|
||||
def before(self):
|
||||
|
@ -85,6 +89,8 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
|||
|
||||
def after(self):
|
||||
self.page.remove_server(self.server)
|
||||
test_utils.delete_table(
|
||||
self.server, self.test_db, self.test_table_name)
|
||||
|
||||
def _tables_node_expandable(self):
|
||||
self.page.toggle_open_server(self.server['name'])
|
||||
|
@ -198,7 +204,8 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
|||
self.page.fill_codemirror_area_with(
|
||||
"select '<script>alert(1)</script>"
|
||||
)
|
||||
self.page.find_by_id("btn-flash").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.click_tab('Query History')
|
||||
|
||||
|
@ -227,13 +234,17 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
|||
'<script>alert(1)</script>',
|
||||
"Query tool (History Details-Message)"
|
||||
)
|
||||
|
||||
# Check for history details error message
|
||||
history_ele = self.page.find_by_css_selector(
|
||||
".query-detail .history-error-text"
|
||||
)
|
||||
|
||||
source_code = history_ele.get_attribute('innerHTML')
|
||||
retry = 2
|
||||
while retry > 0:
|
||||
try:
|
||||
# Check for history details error message
|
||||
history_ele = self.page.find_by_css_selector(
|
||||
".query-detail .history-error-text"
|
||||
)
|
||||
source_code = history_ele.get_attribute('innerHTML')
|
||||
break
|
||||
except StaleElementReferenceException:
|
||||
retry -= 1
|
||||
|
||||
self._check_escaped_characters(
|
||||
source_code,
|
||||
|
@ -272,7 +283,8 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
|||
'select * from "{0}"'.format(self.test_table_name)
|
||||
)
|
||||
|
||||
self.page.find_by_id("btn-explain").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_explain).click()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Explain')
|
||||
|
||||
|
|
|
@ -88,7 +88,18 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
|||
|
||||
# If debugger plugin is not found
|
||||
if is_error and is_error.text == "Debugger Error":
|
||||
self.page.click_modal('OK')
|
||||
click = True
|
||||
while click:
|
||||
try:
|
||||
self.page.click_modal('OK')
|
||||
wait.until(EC.invisibility_of_element(
|
||||
(By.XPATH, "//div[contains(@class, 'alertify') and "
|
||||
"not(contains(@class, 'ajs-hidden'))]//div["
|
||||
"contains(@class,'ajs-header')]")
|
||||
))
|
||||
click = False
|
||||
except TimeoutException:
|
||||
pass
|
||||
self.skipTest(
|
||||
"Please make sure that debugger plugin is properly configured"
|
||||
)
|
||||
|
|
|
@ -10,6 +10,10 @@ import random
|
|||
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
from regression.feature_utils.locators import NavMenuLocators
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
|
||||
class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
|
||||
|
@ -36,6 +40,7 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
|
|||
self.role)
|
||||
test_utils.create_role(self.server, "postgres",
|
||||
"<h1>test</h1>")
|
||||
self.wait = WebDriverWait(self.page.driver, 20)
|
||||
|
||||
def runTest(self):
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
|
@ -56,9 +61,11 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
|
|||
self.page.select_tree_item(role)
|
||||
|
||||
def _check_role_membership_control(self):
|
||||
self.page.driver.find_element_by_link_text("Object").click()
|
||||
self.page.driver.find_element_by_link_text("Properties...").click()
|
||||
# self.page.find_by_partial_link_text("Membership").click()
|
||||
self.page.driver.find_element_by_link_text(
|
||||
NavMenuLocators.object_menu_link_text).click()
|
||||
property_object = self.wait.until(EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, NavMenuLocators.properties_obj_css)))
|
||||
property_object.click()
|
||||
self.click_membership_tab()
|
||||
# Fetch the source code for our custom control
|
||||
source_code = self.page.find_by_xpath(
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
|
||||
class BrowserToolBarLocators():
|
||||
"""This will contains element locators for browser tool bar"""
|
||||
|
||||
open_query_tool_button_css = \
|
||||
".wcFrameButton[title='Query Tool']:not(.disabled)"
|
||||
|
||||
query_tool_panel_css = ".wcPanelTab .wcTabIcon.fa.fa-bolt"
|
||||
|
||||
view_table_data_button_css = \
|
||||
".wcFrameButton[title='View Data']:not(.disabled)"
|
||||
|
||||
view_data_panel_css = ".wcPanelTab .wcTabIcon.fa.fa-table"
|
||||
|
||||
filter_data_button_css = \
|
||||
".wcFrameButton[title='Filtered Rows']:not(.disabled)"
|
||||
|
||||
filter_alertify_box_css = ".alertify .ajs-header[data-title~='Filter']"
|
||||
|
||||
|
||||
class NavMenuLocators:
|
||||
"This will contains element locators of navigation menu bar"
|
||||
|
||||
file_menu_css = "#mnu_file"
|
||||
|
||||
preference_menu_item_css = "#mnu_preferences"
|
||||
|
||||
tools_menu_link_text = "Tools"
|
||||
|
||||
view_data_link_text = "View/Edit Data"
|
||||
|
||||
object_menu_link_text = "Object"
|
||||
|
||||
properties_obj_css = "#show_obj_properties.dropdown-item:not(.disabled)"
|
||||
|
||||
backup_obj_css = "#backup_object.dropdown-item:not(.disabled)"
|
||||
|
||||
restore_obj_css = "#restore_object.dropdown-item:not(.disabled)"
|
||||
|
||||
maintenance_obj_css = "#maintenance.dropdown-item:not(.disabled)"
|
||||
|
||||
show_system_objects_pref_label_xpath = \
|
||||
"//label[contains(text(), 'Show system objects?')]"
|
||||
|
||||
maximize_pref_dialogue_css = ".ajs-dialog.pg-el-container .ajs-maximize"
|
||||
|
||||
specified_pref_node_exp_status = \
|
||||
"//div[div[span[span[(@class='aciTreeText')and " \
|
||||
"(text()='{0} ' or text()='{0}')]]]]"
|
||||
|
||||
specified_preference_tree_node = \
|
||||
"//div//span[(@class='aciTreeText')and " \
|
||||
"(text()='{0} ' or text()='{0}')]"
|
||||
|
||||
specified_sub_node_of_pref_tree_node = \
|
||||
"//span[text()='{0}']//following::span[text()='{1}']"
|
||||
|
||||
insert_bracket_pair_switch_btn = \
|
||||
"//div[label[normalize-space(text())='Insert bracket pairs?']]" \
|
||||
"//div[contains(@class,'toggle btn')]"
|
||||
|
||||
backup_filename_txt_box_name = "file"
|
||||
|
||||
restore_file_name_txt_box_name = "file"
|
||||
|
||||
backup_btn_xpath = \
|
||||
"//button[contains(@class,'fa-save')and contains(.,'Backup')]"
|
||||
|
||||
bcg_process_status_alertifier_css = \
|
||||
".ajs-message.ajs-bg-bgprocess.ajs-visible"
|
||||
|
||||
status_alertifier_more_btn_css = ".pg-bg-more-details"
|
||||
|
||||
process_watcher_alertfier = \
|
||||
"//div[contains(@class,'wcFrameTitleBar')]" \
|
||||
"//div[contains(text(),'Process Watcher')]"
|
||||
|
||||
process_watcher_detailed_command_canvas_css = \
|
||||
".bg-process-details .bg-detailed-desc"
|
||||
|
||||
process_watcher_close_button_xpath = \
|
||||
"//div[contains(@class,'wcFloatingFocus')]//" \
|
||||
"div[contains(@class,'fa-close')]"
|
||||
|
||||
restore_file_name_xpath = "//div[contains(text(),'Restore')]" \
|
||||
"//following::input[@name='file']"
|
||||
|
||||
restore_button_xpath = \
|
||||
"//button[contains(@class,'fa-upload') and contains(.,'Restore')]"
|
||||
|
||||
maintenance_operation = "//label[text()='Maintenance operation']"
|
||||
|
||||
select_tab_xpath = \
|
||||
"//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') " \
|
||||
"and contains(.,'{}')]"
|
||||
|
||||
|
||||
class QueryToolLocators:
|
||||
btn_save_file = "#btn-save-file"
|
||||
|
||||
btn_save_data = "#btn-save-data"
|
||||
|
||||
btn_query_dropdown = "#btn-query-dropdown"
|
||||
|
||||
btn_auto_rollback = "#btn-auto-rollback"
|
||||
|
||||
btn_auto_rollback_check_status = "#btn-auto-rollback > i"
|
||||
|
||||
btn_auto_commit = "#btn-auto-commit"
|
||||
|
||||
btn_auto_commit_check_status = "#btn-auto-commit > i"
|
||||
|
||||
btn_cancel_query = "#btn-cancel-query"
|
||||
|
||||
btn_explain = "#btn-explain"
|
||||
|
||||
btn_explain_analyze = "#btn-explain-analyze"
|
||||
|
||||
btn_explain_options_dropdown = "#btn-explain-options-dropdown"
|
||||
|
||||
btn_explain_verbose = "#btn-explain-verbose"
|
||||
|
||||
btn_explain_costs = "#btn-explain-costs"
|
||||
|
||||
btn_explain_buffers = "#btn-explain-buffers"
|
||||
|
||||
btn_explain_timing = "#btn-explain-timing"
|
||||
|
||||
btn_clear_dropdown = "#btn-clear-dropdown"
|
||||
|
||||
btn_clear_history = "#btn-clear-history"
|
||||
|
||||
btn_clear = "#btn-clear"
|
||||
|
||||
query_editor_panel = "#output-panel"
|
||||
|
||||
query_history_selected = "#query_list .selected"
|
||||
|
||||
query_history_entries = "#query_list>.query-group>ul>li"
|
||||
|
||||
query_history_specific_entry = \
|
||||
"#query_list>.query-group>ul>li:nth-child({})"
|
||||
|
||||
query_history_detail = "#query_detail"
|
||||
|
||||
invalid_query_history_entry_css = "#query_list .entry.error .query"
|
||||
|
||||
editor_panel = "#output-panel"
|
||||
|
||||
query_messages_panel = ".sql-editor-message"
|
||||
|
||||
output_row_xpath = "//div[contains(@class, 'slick-row')][{}]/*[1]"
|
||||
|
||||
output_column_header_css = "[data-column-id='{}']"
|
||||
|
||||
output_column_data_xpath = "//div[contains(@class, 'slick-cell')]" \
|
||||
"[contains(., '{}')]"
|
||||
output_cell_xpath = "//div[contains(@class, 'slick-cell') and " \
|
||||
"contains(@class, 'l{0} r{1}')]"
|
||||
|
||||
select_all_column = \
|
||||
"//div[contains(@id,'row-header-column')]"
|
||||
|
||||
new_row_xpath = "//div[contains(@class, 'new-row')]"
|
||||
|
||||
copy_button_css = "#btn-copy-row"
|
||||
|
||||
paste_button_css = "#btn-paste-row"
|
||||
|
||||
row_editor_text_area_css = ".pg-text-editor > textarea"
|
||||
|
||||
text_editor_ok_btn_css = ".btn.btn-primary.long_text_editor"
|
||||
|
||||
btn_load_file_css = "#btn-load-file"
|
||||
|
||||
btn_execute_query_css = "#btn-flash"
|
||||
|
||||
input_file_path_css = "input#file-input-path"
|
||||
|
||||
select_file_content_css = "table#contents"
|
||||
|
||||
query_output_canvas_css = "#datagrid .slick-viewport .grid-canvas"
|
||||
|
||||
query_output_cells = ".slick-cell"
|
||||
|
||||
sql_editor_message = "//div[contains(@class, 'sql-editor-message') and " \
|
||||
"contains(string(), '{}')]"
|
||||
|
||||
code_mirror_hint_box_xpath = "//ul[@class='CodeMirror-hints default']"
|
||||
|
||||
code_mirror_hint_item_xpath = \
|
||||
"//ul[contains(@class, 'CodeMirror-hints') and contains(., '{}')]"
|
||||
|
||||
code_mirror_data_xpath = "//pre[@class=' CodeMirror-line ']/span"
|
||||
|
||||
save_data_icon = "icon-save-data-changes"
|
||||
|
||||
commit_icon = "icon-commit"
|
||||
|
||||
execute_icon = "fa-bolt"
|
||||
|
||||
explain_icon = "fa-hand-pointer-o"
|
||||
|
||||
explain_analyze_icon = "fa-list-alt"
|
||||
|
||||
query_history_selected_icon = '#query_list .selected #query_source_icon'
|
||||
|
||||
btn_commit = "#btn-commit"
|
||||
|
||||
show_query_internally_btn = \
|
||||
"//div[label[normalize-space(" \
|
||||
"text())='Show queries generated internally by pgAdmin?']]" \
|
||||
"//div[contains(@class,'toggle btn')]"
|
|
@ -8,6 +8,7 @@
|
|||
##########################################################################
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import NoSuchElementException, \
|
||||
|
@ -18,6 +19,9 @@ from selenium.webdriver.common.keys import Keys
|
|||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.common.by import By
|
||||
from regression.feature_utils.locators import QueryToolLocators, \
|
||||
NavMenuLocators
|
||||
from regression.feature_utils.tree_area_locators import TreeAreaLocators
|
||||
|
||||
|
||||
class PgadminPage:
|
||||
|
@ -84,8 +88,10 @@ class PgadminPage:
|
|||
self.find_by_css_selector("button[type='save'].btn.btn-primary").\
|
||||
click()
|
||||
|
||||
self.find_by_xpath(
|
||||
"//*[@id='tree']//*[.='" + server_config['name'] + "']")
|
||||
WebDriverWait(self.driver, 10).until(
|
||||
EC.visibility_of_element_located(
|
||||
(By.XPATH,
|
||||
"//*[@id='tree']//*[.='" + server_config['name'] + "']")))
|
||||
|
||||
def open_query_tool(self):
|
||||
self.driver.find_element_by_link_text("Tools").click()
|
||||
|
@ -151,6 +157,24 @@ class PgadminPage:
|
|||
'contains(.,"Don\'t save")]'))
|
||||
self.driver.switch_to.default_content()
|
||||
|
||||
def clear_query_tool(self):
|
||||
self.click_element(
|
||||
self.find_by_css_selector(QueryToolLocators.btn_clear_dropdown)
|
||||
)
|
||||
ActionChains(self.driver).move_to_element(
|
||||
self.find_by_css_selector(QueryToolLocators.btn_clear)).perform()
|
||||
self.click_element(
|
||||
self.find_by_css_selector(QueryToolLocators.btn_clear)
|
||||
)
|
||||
self.click_modal('Yes')
|
||||
|
||||
def execute_query(self, query):
|
||||
self.fill_codemirror_area_with(query)
|
||||
execute_button = self.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css)
|
||||
execute_button.click()
|
||||
self.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
def close_data_grid(self):
|
||||
self.driver.switch_to_default_content()
|
||||
xpath = "//*[@id='dockerContainer']/div/div[3]/div/div[2]/div[1]"
|
||||
|
@ -188,14 +212,40 @@ class PgadminPage:
|
|||
if attempts == 0:
|
||||
raise Exception(e)
|
||||
|
||||
def get_expansion_status_of_node(self, xpath_node):
|
||||
"""get the expansion status for a node through xpath"""
|
||||
node_is_expanded = False
|
||||
element = self.find_by_xpath(xpath_node)
|
||||
if element.get_attribute("aria-expanded") == 'true':
|
||||
node_is_expanded = True
|
||||
return node_is_expanded
|
||||
|
||||
def toggle_open_servers_group(self):
|
||||
"""This will open Servers group to display underlying nodes"""
|
||||
is_expanded = False
|
||||
self.wait_for_spinner_to_disappear()
|
||||
server_group = self.find_by_xpath(
|
||||
"//div[@id='tree']//span[@class='aciTreeItem']"
|
||||
"/span[(@class='aciTreeText') and starts-with(text(),'Servers ') "
|
||||
"or starts-with(text(), 'Servers')]")
|
||||
ActionChains(self.driver).double_click(server_group).perform()
|
||||
if self.check_if_element_exist_by_xpath(
|
||||
TreeAreaLocators.server_group_node):
|
||||
if self.get_expansion_status_of_node(
|
||||
TreeAreaLocators.server_group_node_exp_status):
|
||||
is_expanded = True
|
||||
else:
|
||||
webdriver.ActionChains(self.driver).double_click(
|
||||
self.find_by_xpath(
|
||||
TreeAreaLocators.
|
||||
server_group_node)).perform()
|
||||
if self.check_if_element_exist_by_xpath(
|
||||
TreeAreaLocators.server_group_sub_nodes):
|
||||
is_expanded = True
|
||||
else:
|
||||
print(
|
||||
"(toggle_open_servers_group)The Server Group "
|
||||
"node is clicked to expand but it is not expanded",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
print("The Server Group node is not visible",
|
||||
file=sys.stderr)
|
||||
return is_expanded
|
||||
|
||||
def toggle_open_tree_item(self, tree_item_text):
|
||||
# 'sleep' here helps in cases where underlying nodes are auto opened.
|
||||
|
@ -203,8 +253,7 @@ class PgadminPage:
|
|||
# even if the underlying node to be clicked was Opened.
|
||||
time.sleep(.6)
|
||||
item_with_text = self.find_by_xpath(
|
||||
"//div[@id='tree']//span[@class='aciTreeItem']/span["
|
||||
"(@class='aciTreeText') and text()='" + tree_item_text + "']")
|
||||
TreeAreaLocators.specified_tree_node.format(tree_item_text))
|
||||
|
||||
self.driver.execute_script("arguments[0].scrollIntoView()",
|
||||
item_with_text)
|
||||
|
@ -215,6 +264,17 @@ class PgadminPage:
|
|||
item = item_with_text.find_element_by_xpath(
|
||||
".//parent::*[@class='aciTreeItem']")
|
||||
ActionChains(self.driver).double_click(item).perform()
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
try:
|
||||
WebDriverWait(self.driver, 5).until((lambda item_with_text: (
|
||||
item_with_text.find_element_by_xpath(
|
||||
".//ancestor::*[@class='aciTreeLine']").
|
||||
get_attribute("aria-expanded") == 'true')))
|
||||
break
|
||||
except TimeoutException:
|
||||
retry -= 1
|
||||
pass
|
||||
|
||||
def toggle_open_tables_node(self):
|
||||
"""The function will be used for opening Tables node only"""
|
||||
|
@ -454,15 +514,14 @@ class PgadminPage:
|
|||
|
||||
def click_tab(self, tab_name):
|
||||
WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable(
|
||||
(By.XPATH, "//*[contains(@class,'wcTabTop')]//"
|
||||
"*[contains(@class,'wcPanelTab') "
|
||||
"and contains(.,'" + tab_name + "')]")))
|
||||
|
||||
tab = self.find_by_xpath("//*[contains(@class,'wcTabTop')]//"
|
||||
"*[contains(@class,'wcPanelTab') "
|
||||
"and contains(.,'" + tab_name + "')]")
|
||||
|
||||
self.click_element(tab)
|
||||
(By.XPATH, NavMenuLocators.select_tab_xpath.format(tab_name))))
|
||||
click_tab = True
|
||||
while click_tab:
|
||||
tab = self.find_by_xpath(
|
||||
NavMenuLocators.select_tab_xpath.format(tab_name))
|
||||
self.click_element(tab)
|
||||
if 'wcPanelTabActive' in tab.get_attribute('class'):
|
||||
break
|
||||
|
||||
def wait_for_input_by_element(self, element, content):
|
||||
def input_field_has_content(driver):
|
||||
|
@ -539,7 +598,7 @@ class PgadminPage:
|
|||
except NoSuchElementException:
|
||||
return True
|
||||
|
||||
self._wait_for("spinner to disappear", spinner_has_disappeared)
|
||||
self._wait_for("spinner to disappear", spinner_has_disappeared, 20)
|
||||
|
||||
def wait_for_query_tool_loading_indicator_to_disappear(self):
|
||||
def spinner_has_disappeared(driver):
|
||||
|
@ -553,7 +612,12 @@ class PgadminPage:
|
|||
time.sleep(0.5)
|
||||
return True
|
||||
|
||||
self._wait_for("spinner to disappear", spinner_has_disappeared)
|
||||
self._wait_for("spinner to disappear", spinner_has_disappeared, 20)
|
||||
|
||||
def wait_for_query_tool_loading_indicator_to_appear(self):
|
||||
self.check_if_element_exist_by_xpath(
|
||||
"//div[@id='editor-panel']//"
|
||||
"div[@class='pg-sp-container sql-editor-busy-fetching']")
|
||||
|
||||
def wait_for_app(self):
|
||||
def page_shows_app(driver):
|
||||
|
@ -574,10 +638,98 @@ class PgadminPage:
|
|||
return element_selector(self.driver)
|
||||
|
||||
def _wait_for(self, waiting_for_message, condition_met_function,
|
||||
timeout=3):
|
||||
timeout=5):
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
return WebDriverWait(self.driver, timeout, 0.01).until(
|
||||
condition_met_function,
|
||||
"Timed out waiting for " + waiting_for_message
|
||||
)
|
||||
|
||||
def wait_for_elements(self, find_method_with_args):
|
||||
"""Using xpath, it will wait for elements"""
|
||||
def element_if_it_exists(driver):
|
||||
try:
|
||||
element = find_method_with_args(driver)
|
||||
if len(element) > 0 and element[0].is_displayed() and element[
|
||||
0].is_enabled():
|
||||
return element
|
||||
except NoSuchElementException:
|
||||
return False
|
||||
|
||||
return self._wait_for("element to exist", element_if_it_exists)
|
||||
|
||||
def find_by_xpath_list(self, xpath):
|
||||
"""This will find out list of elements through a single xpath"""
|
||||
return self.wait_for_elements(
|
||||
lambda driver: driver.find_elements_by_xpath(xpath))
|
||||
|
||||
def get_index_of_element(self, element_list, target_string):
|
||||
"""it will return index of an element from provided element list"""
|
||||
index_of_required_server = -1
|
||||
if len(element_list) > 0:
|
||||
for index, element in enumerate(element_list):
|
||||
if element.text.startswith(target_string) and (
|
||||
target_string in element.text):
|
||||
index_of_required_server = index
|
||||
break
|
||||
else:
|
||||
print("There seems no record in the provided element list")
|
||||
return index_of_required_server
|
||||
|
||||
def set_switch_box_status(self, switch_box, required_status):
|
||||
"""it will change switch box status to required one. Two elements
|
||||
of the switch boxes are to be provided i) button which is needed to
|
||||
toggle ii) Yes for True or No for False"""
|
||||
status_changed_successfully = False
|
||||
switch_box_element = self.find_by_xpath(switch_box)
|
||||
|
||||
if required_status == 'Yes':
|
||||
if 'off' in switch_box_element.get_attribute("class"):
|
||||
switch_box_element.click()
|
||||
time.sleep(1)
|
||||
if 'success' in switch_box_element.get_attribute("class"):
|
||||
status_changed_successfully = True
|
||||
else:
|
||||
print(
|
||||
"(set_switch_box_status)Clicked the "
|
||||
"element to change its status but "
|
||||
"it did not changed",
|
||||
file=sys.stderr)
|
||||
elif 'success' in switch_box_element.get_attribute("class"):
|
||||
status_changed_successfully = True
|
||||
else:
|
||||
if 'success' in switch_box_element.get_attribute("class"):
|
||||
switch_box_element.click()
|
||||
if 'off' in switch_box_element.get_attribute("class"):
|
||||
status_changed_successfully = True
|
||||
else:
|
||||
print(
|
||||
"(set_switch_box_status)Clicked the element to "
|
||||
"change its status but it did not changed",
|
||||
file=sys.stderr)
|
||||
elif 'off' in switch_box_element.get_attribute("class"):
|
||||
status_changed_successfully = True
|
||||
return status_changed_successfully
|
||||
|
||||
def retry_click_operation(self, element_to_click,
|
||||
element_to_verify_after_click):
|
||||
"""This will attempt to click add button multiple time,
|
||||
some different exception encountered while clicking, so handled
|
||||
through this"""
|
||||
|
||||
click_status = False
|
||||
attempt = 0
|
||||
|
||||
while click_status is not True and attempt < 5:
|
||||
try:
|
||||
if element_to_verify_after_click.is_displayed():
|
||||
click_status = True
|
||||
element_to_click.click()
|
||||
if element_to_verify_after_click.is_displayed():
|
||||
click_status = True
|
||||
except Exception:
|
||||
print("The click operation is not performed for "
|
||||
"attempt %s, will try 5 attempts" % attempt)
|
||||
attempt = +1
|
||||
return click_status
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
|
||||
class TreeAreaLocators():
|
||||
"""This will contains element locators for tree area, will also contain
|
||||
parametrized xpath where applicable"""
|
||||
|
||||
server_group_node = \
|
||||
"//div[@id='tree']//span[@class='aciTreeItem']" \
|
||||
"/span[(@class='aciTreeText') and starts-with(text(),'Servers ') or " \
|
||||
"starts-with(text(), 'Servers')]"
|
||||
|
||||
server_group_node_exp_status = "//div[div[span[span[" \
|
||||
"(@class='aciTreeText') and " \
|
||||
"(text()='Servers ' or " \
|
||||
"text()='Servers')]]]]"
|
||||
|
||||
server_group_sub_nodes = \
|
||||
"//div[div[span[span[contains(text(),'Servers')]]]]/" \
|
||||
"following-sibling::ul/li/div/div/div/span[2]/" \
|
||||
"span[@class='aciTreeText']"
|
||||
|
||||
specified_tree_node = \
|
||||
"//div[@id='tree']//span[@class='aciTreeItem']/" \
|
||||
"span[(@class='aciTreeText') and text()='{}']"
|
|
@ -12,42 +12,32 @@ def close_bgprocess_popup(tester):
|
|||
"""
|
||||
Allows us to close the background process popup window
|
||||
"""
|
||||
screen_shot_taken = False
|
||||
# In cases where backup div is not closed (sometime due to some error)
|
||||
try:
|
||||
if tester.driver.find_element_by_css_selector(
|
||||
".ajs-message.ajs-bg-bgprocess.ajs-visible"):
|
||||
tester._screenshot()
|
||||
screen_shot_taken = True
|
||||
tester.driver.find_element_by_css_selector(
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click()
|
||||
tester.page.wait_for_element_to_disappear(
|
||||
lambda x: tester.driver.find_element_by_xpath(
|
||||
".ajs-message.ajs-bg-bgprocess.ajs-visible"))
|
||||
except Exception:
|
||||
pass
|
||||
tester.driver.find_element_by_css_selector(
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click()
|
||||
|
||||
# In cases where restore div is not closed (sometime due to some error)
|
||||
try:
|
||||
if tester.driver.find_element_by_xpath(
|
||||
tester.page.wait_for_element_to_disappear(
|
||||
lambda x: tester.driver.find_element_by_xpath(
|
||||
"//div[@class='card-header bg-primary d-flex']/div"
|
||||
"[contains(text(), 'Restoring backup')]"):
|
||||
tester._screenshot()
|
||||
screen_shot_taken = True
|
||||
tester.driver.find_element_by_css_selector(
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click()
|
||||
"[contains(text(), 'Restoring backup')]"))
|
||||
except Exception:
|
||||
pass
|
||||
tester.driver.find_element_by_css_selector(
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click()
|
||||
|
||||
# In cases where maintenance window is not closed (sometime due to some
|
||||
# error)
|
||||
try:
|
||||
if tester.driver.find_element_by_xpath(
|
||||
tester.page.wait_for_element_to_disappear(
|
||||
lambda x: tester.driver.find_element_by_xpath(
|
||||
"//div[@class='card-header bg-primary d-flex']/div"
|
||||
"[contains(text(), 'Maintenance')]"):
|
||||
tester._screenshot()
|
||||
screen_shot_taken = True
|
||||
tester.driver.find_element_by_css_selector(
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click()
|
||||
"[contains(text(), 'Maintenance')]"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not screen_shot_taken:
|
||||
tester._screenshot()
|
||||
tester.driver.find_element_by_css_selector(
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click()
|
||||
|
|
|
@ -194,6 +194,35 @@ def create_table(server, db_name, table_name, extra_columns=[]):
|
|||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def delete_table(server, db_name, table_name):
|
||||
"""
|
||||
This function delete the table in given database
|
||||
:param server: server details
|
||||
:type server: dict
|
||||
:param db_name: database name
|
||||
:type db_name: str
|
||||
:param table_name: table name
|
||||
:type table_name: str
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
connection = get_db_connection(
|
||||
db_name,
|
||||
server['username'],
|
||||
server['db_password'],
|
||||
server['host'],
|
||||
server['port'],
|
||||
server['sslmode']
|
||||
)
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute(
|
||||
'''DROP TABLE IF EXISTS "%s"''' % table_name)
|
||||
connection.commit()
|
||||
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def create_table_with_query(server, db_name, query):
|
||||
"""
|
||||
This function create the table in given database name
|
||||
|
@ -774,22 +803,28 @@ def configure_preferences(default_binary_path=None):
|
|||
|
||||
|
||||
def reset_layout_db(user_id=None):
|
||||
conn = sqlite3.connect(config.TEST_SQLITE_PATH)
|
||||
cur = conn.cursor()
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
try:
|
||||
conn = sqlite3.connect(config.TEST_SQLITE_PATH)
|
||||
cur = conn.cursor()
|
||||
|
||||
if user_id is None:
|
||||
cur.execute(
|
||||
'DELETE FROM SETTING WHERE SETTING in '
|
||||
'("Browser/Layout", "SQLEditor/Layout", "Debugger/Layout")'
|
||||
)
|
||||
else:
|
||||
cur.execute(
|
||||
'DELETE FROM SETTING WHERE SETTING in '
|
||||
'("Browser/Layout", "SQLEditor/Layout", "Debugger/Layout")'
|
||||
' AND USER_ID=?', user_id
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if user_id is None:
|
||||
cur.execute(
|
||||
'DELETE FROM SETTING WHERE SETTING in '
|
||||
'("Browser/Layout", "SQLEditor/Layout", "Debugger/Layout")'
|
||||
)
|
||||
else:
|
||||
cur.execute(
|
||||
'DELETE FROM SETTING WHERE SETTING in '
|
||||
'("Browser/Layout", "SQLEditor/Layout", "Debugger/Layout")'
|
||||
' AND USER_ID=?', user_id
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
break
|
||||
except Exception:
|
||||
retry -= 1
|
||||
|
||||
|
||||
def remove_db_file():
|
||||
|
|
Loading…
Reference in New Issue