mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Fixed the following issues:
1. Feature test cases for the new query tool. 2. Query tool panel rename is not persisting.
This commit is contained in:
parent
31be1ae026
commit
ca8e14455f
@ -54,16 +54,17 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
self._copies_columns()
|
||||
self._copies_row_using_keyboard_shortcut()
|
||||
self._copies_column_using_keyboard_shortcut()
|
||||
self._copies_rectangular_selection()
|
||||
self._shift_resizes_rectangular_selection()
|
||||
# The below calls is commented since the new data grid does not
|
||||
# support range selection. This can be enabled once the
|
||||
# range selection is implemented.
|
||||
# self._copies_rectangular_selection()
|
||||
# self._shift_resizes_rectangular_selection()
|
||||
|
||||
self._shift_resizes_column_selection()
|
||||
self._mouseup_outside_grid_still_makes_a_selection()
|
||||
self._mouseup_outside_grid_does_not_make_a_selection()
|
||||
self._copies_rows_with_header()
|
||||
|
||||
def paste_values_to_scratch_pad(self):
|
||||
self.page.driver.switch_to.default_content()
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element(By.TAG_NAME, "iframe"))
|
||||
scratch_pad_ele = self.page.find_by_css_selector(
|
||||
QueryToolLocators.scratch_pad_css)
|
||||
self.page.paste_values(scratch_pad_ele)
|
||||
@ -73,7 +74,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
|
||||
def _copies_rows(self):
|
||||
first_row = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_row_xpath.format(1))
|
||||
QueryToolLocators.output_cell_xpath.format(2, 1))
|
||||
first_row.click()
|
||||
|
||||
copy_button = self.page.find_by_css_selector(
|
||||
@ -85,8 +86,10 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
clipboard_text)
|
||||
|
||||
def _copies_rows_with_header(self):
|
||||
self.page.find_by_css_selector('#btn-copy-row-dropdown').click()
|
||||
self.page.find_by_css_selector('a#btn-copy-with-header').click()
|
||||
self.page.find_by_css_selector(QueryToolLocators.copy_options_css)\
|
||||
.click()
|
||||
self.page.find_by_css_selector(QueryToolLocators.copy_headers_btn_css)\
|
||||
.click()
|
||||
|
||||
select_all = self.page.find_by_xpath(
|
||||
QueryToolLocators.select_all_column)
|
||||
@ -122,7 +125,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
|
||||
def _copies_row_using_keyboard_shortcut(self):
|
||||
first_row = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_row_xpath.format(1))
|
||||
QueryToolLocators.output_cell_xpath.format(2, 1))
|
||||
first_row.click()
|
||||
|
||||
ActionChains(self.page.driver).key_down(
|
||||
@ -216,7 +219,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
'"Some-Name"\t6\n"Some-Other-Name"\t22\n"Yet-Another-Name"\t14',
|
||||
clipboard_text)
|
||||
|
||||
def _mouseup_outside_grid_still_makes_a_selection(self):
|
||||
def _mouseup_outside_grid_does_not_make_a_selection(self):
|
||||
bottom_right_cell = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_column_data_xpath.format('cool info')
|
||||
)
|
||||
@ -233,7 +236,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
|
||||
clipboard_text = self.paste_values_to_scratch_pad()
|
||||
|
||||
self.assertIn('"cool info"', clipboard_text)
|
||||
self.assertNotIn('"cool info"', clipboard_text)
|
||||
|
||||
def after(self):
|
||||
self.page.close_query_tool()
|
||||
|
@ -35,7 +35,9 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
|
||||
select_query = "SELECT * FROM %s"
|
||||
query_history_tab_name = "Query History"
|
||||
query_history_tab_id = "id-history"
|
||||
query_editor_tab_name = "Query Editor"
|
||||
query_editor_tab_id = "id-query"
|
||||
|
||||
def before(self):
|
||||
self.test_table_name = "test_table" + str(random.randint(1000, 3000))
|
||||
@ -98,8 +100,10 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
|
||||
select_row = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_row_xpath.format('1'))
|
||||
# row index starts with 2
|
||||
select_row = self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_row_col.format('2', '1'))
|
||||
|
||||
select_row.click()
|
||||
|
||||
copy_row = self.page.find_by_css_selector(
|
||||
@ -133,10 +137,6 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
QueryToolLocators.copy_button_css)
|
||||
copy_btn.click()
|
||||
|
||||
self.page.driver.switch_to.default_content()
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
|
||||
scratch_pad_ele = self.page.find_by_css_selector(
|
||||
QueryToolLocators.scratch_pad_css)
|
||||
self.page.paste_values(scratch_pad_ele)
|
||||
@ -149,16 +149,19 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
scratch_pad_ele.clear()
|
||||
|
||||
def _test_history_tab(self):
|
||||
self.page.clear_query_tool()
|
||||
self.page.driver.switch_to.default_content()
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
self.page.clear_query_tool()
|
||||
|
||||
editor_input = self.page.find_by_css_selector(
|
||||
QueryToolLocators.query_editor_panel)
|
||||
self.page.click_element(editor_input)
|
||||
self.page.execute_query(self.select_query % self.invalid_table_name)
|
||||
|
||||
self.page.click_tab(self.query_history_tab_name)
|
||||
self.page.click_tab(self.query_history_tab_id, rc_dock=True)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear(
|
||||
container_id="id-history")
|
||||
selected_history_entry = self.page.find_by_css_selector(
|
||||
QueryToolLocators.query_history_selected)
|
||||
self.assertIn(self.select_query % self.invalid_table_name,
|
||||
@ -168,7 +171,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
QueryToolLocators.query_history_detail)
|
||||
|
||||
self.assertIn(
|
||||
"Error Message relation \"%s\" does not exist"
|
||||
"ERROR: relation \"%s\" does not exist"
|
||||
% self.invalid_table_name,
|
||||
failed_history_detail_pane.text
|
||||
)
|
||||
@ -198,10 +201,8 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
self.assertIn(self.select_query % self.invalid_table_name,
|
||||
invalid_history_entry.text)
|
||||
|
||||
self.page.click_tab(self.query_editor_tab_name)
|
||||
self.page.click_tab(self.query_editor_tab_id, rc_dock=True)
|
||||
self.page.clear_query_tool()
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
self.page.click_element(editor_input)
|
||||
|
||||
# Check if 15 more query executed then the history should contain 17
|
||||
@ -212,7 +213,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab(self.query_history_tab_name)
|
||||
self.page.click_tab(self.query_history_tab_id, rc_dock=True)
|
||||
|
||||
query_list = self.page.wait_for_elements(
|
||||
lambda driver: driver.find_elements(
|
||||
@ -226,20 +227,18 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
self._test_toggle_generated_queries()
|
||||
|
||||
def _test_history_query_sources(self):
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
self.page.click_tab(self.query_editor_tab_name)
|
||||
self.page.click_tab(self.query_editor_tab_id, rc_dock=True)
|
||||
self._execute_sources_test_queries()
|
||||
|
||||
self.page.click_tab(self.query_history_tab_name)
|
||||
self.page.click_tab(self.query_history_tab_id, rc_dock=True)
|
||||
|
||||
history_entries_icons = [
|
||||
QueryToolLocators.commit_icon,
|
||||
QueryToolLocators.save_data_icon,
|
||||
QueryToolLocators.save_data_icon,
|
||||
QueryToolLocators.execute_icon,
|
||||
QueryToolLocators.explain_analyze_icon,
|
||||
QueryToolLocators.explain_icon
|
||||
'CommitIcon',
|
||||
'SaveDataIcon',
|
||||
'SaveDataIcon',
|
||||
'ExecuteIcon',
|
||||
'ExplainAnalyzeIcon',
|
||||
'ExplainIcon',
|
||||
]
|
||||
|
||||
history_entries_queries = [
|
||||
@ -256,7 +255,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
history_entries_icons)
|
||||
|
||||
def _test_toggle_generated_queries(self):
|
||||
xpath = '//li[contains(@class, "pgadmin-query-history-entry")]'
|
||||
xpath = "//li[@data-label='history-entry'][@data-pgadmin='true']"
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(xpath))
|
||||
self.page.set_switch_box_status(
|
||||
QueryToolLocators.show_query_internally_btn, 'No')
|
||||
@ -266,9 +265,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
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(self.query_editor_tab_name)
|
||||
self.page.click_tab(self.query_editor_tab_id, rc_dock=True)
|
||||
|
||||
# Select all data
|
||||
# (contains the primary key -> all columns should be editable)
|
||||
@ -295,19 +292,18 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
self._check_query_results_editable(query,
|
||||
[True, True, False, False, False])
|
||||
|
||||
# discard edits
|
||||
self.page.execute_query('SELECT 1')
|
||||
self.page.click_modal('Yes', True)
|
||||
|
||||
def _test_is_editable_columns_icons(self):
|
||||
if self.driver_version < 2.8:
|
||||
return
|
||||
self.page.click_tab(self.query_editor_tab_name)
|
||||
self.page.click_tab(self.query_editor_tab_id, rc_dock=True)
|
||||
|
||||
self.page.clear_query_tool()
|
||||
query = "SELECT pk_column FROM %s" % self.test_editable_table_name
|
||||
self.page.execute_query(query)
|
||||
# Discard changes made by previous test to data grid
|
||||
self.page.driver.switch_to.default_content()
|
||||
self.page.click_modal('Yes', True)
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
|
||||
icon_exists = self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.editable_column_icon_xpath
|
||||
@ -344,33 +340,38 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
query_options.click()
|
||||
self.page.uncheck_execute_option("auto_commit")
|
||||
|
||||
self._update_numeric_cell(2, 10)
|
||||
self._update_numeric_cell(10)
|
||||
time.sleep(0.5)
|
||||
|
||||
self._commit_transaction()
|
||||
self.page.wait_for_spinner_to_disappear()
|
||||
|
||||
# Turn on autocommit
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
query_options = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_options.click()
|
||||
expanded = query_options.get_attribute("aria-expanded")
|
||||
if expanded == "false":
|
||||
print("query option not yet expanded clicking commit again",
|
||||
file=sys.stderr)
|
||||
self._commit_transaction()
|
||||
time.sleep(0.5)
|
||||
query_options.click()
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
# self.page.check_execute_option("auto_commit")
|
||||
# query_options = self.page.find_by_css_selector(
|
||||
# QueryToolLocators.btn_query_dropdown)
|
||||
# query_options.click()
|
||||
# retry = 3
|
||||
# while retry > 0:
|
||||
# query_options = self.page.find_by_css_selector(
|
||||
# QueryToolLocators.btn_query_dropdown)
|
||||
# query_options.click()
|
||||
# expanded = query_options.get_attribute("aria-expanded")
|
||||
# if expanded == "false":
|
||||
# print("query option not yet expanded clicking commit again",
|
||||
# file=sys.stderr)
|
||||
# self._commit_transaction()
|
||||
# time.sleep(0.5)
|
||||
# query_options.click()
|
||||
# break
|
||||
# else:
|
||||
# retry -= 1
|
||||
self.page.check_execute_option("auto_commit")
|
||||
|
||||
def _check_history_queries_and_icons(self, history_queries, history_icons):
|
||||
# Select first query history entry
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.query_history_specific_entry.format(1)).click()
|
||||
QueryToolLocators.query_history_specific_entry.format(2)).click()
|
||||
for icon, query in zip(history_icons, history_queries):
|
||||
# Check query
|
||||
query_history_selected_item = self.page.find_by_css_selector(
|
||||
@ -382,27 +383,20 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
# Check source icon
|
||||
query_history_selected_icon = self.page.find_by_css_selector(
|
||||
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 or 'icon-save_data_changes' in
|
||||
icon_classes or 'icon-commit' in icon_classes or
|
||||
'fa-play' in icon_classes)
|
||||
icon == query_history_selected_icon.get_attribute(
|
||||
'data-label'))
|
||||
# Move to next entry
|
||||
ActionChains(self.page.driver) \
|
||||
.send_keys(Keys.ARROW_DOWN) \
|
||||
.perform()
|
||||
|
||||
def _update_numeric_cell(self, cell_index, value):
|
||||
def _update_numeric_cell(self, value):
|
||||
"""
|
||||
Updates a numeric cell in the first row of the resultset
|
||||
"""
|
||||
cell_xpath = "//div[contains(@style, 'top:0px')]//" \
|
||||
"div[contains(@class,'l{0} r{1}')]". \
|
||||
format(cell_index, cell_index)
|
||||
|
||||
self.page.check_if_element_exist_by_xpath(cell_xpath)
|
||||
cell_el = self.page.find_by_xpath(cell_xpath)
|
||||
cell_el = self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_row_col.format(2, 3))
|
||||
ActionChains(self.driver).double_click(cell_el).perform()
|
||||
ActionChains(self.driver).send_keys(value). \
|
||||
send_keys(Keys.ENTER).perform()
|
||||
@ -410,7 +404,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
QueryToolLocators.btn_save_data).click()
|
||||
|
||||
def _insert_data_into_test_editable_table(self):
|
||||
self.page.click_tab(self.query_editor_tab_name)
|
||||
self.page.click_tab(self.query_editor_tab_id, rc_dock=True)
|
||||
self.page.clear_query_tool()
|
||||
self.page.execute_query(
|
||||
"INSERT INTO %s VALUES (1, 1), (2, 2);"
|
||||
@ -418,18 +412,13 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
)
|
||||
|
||||
def __clear_query_history(self):
|
||||
self.page.click_tab(self.query_history_tab_id, rc_dock=True)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear(
|
||||
container_id="id-history")
|
||||
self.page.click_element(
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_clear_dropdown)
|
||||
QueryToolLocators.btn_history_remove_all)
|
||||
)
|
||||
ActionChains(self.driver) \
|
||||
.move_to_element(
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_clear_history)).perform()
|
||||
self.page.click_element(
|
||||
self.page.find_by_css_selector(QueryToolLocators.btn_clear_history)
|
||||
)
|
||||
self.driver.switch_to.default_content()
|
||||
self.page.click_modal('Yes', True)
|
||||
|
||||
def _navigate_to_query_tool(self):
|
||||
@ -460,12 +449,10 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
discard_changes_modal=False):
|
||||
self.page.execute_query(query)
|
||||
if discard_changes_modal:
|
||||
self.driver.switch_to.default_content()
|
||||
self.page.click_modal('Yes', True)
|
||||
self.page.driver.switch_to.frame(
|
||||
self.page.driver.find_element_by_tag_name("iframe"))
|
||||
|
||||
enumerated_should_be_editable = enumerate(cols_should_be_editable, 1)
|
||||
# first column is rownum
|
||||
enumerated_should_be_editable = enumerate(cols_should_be_editable, 2)
|
||||
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
@ -475,9 +462,8 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
|
||||
def _check_cell_editable(self, cell_index):
|
||||
"""Checks if a cell in the first row of the resultset is editable"""
|
||||
cell_el = self.page.find_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_css_selector(
|
||||
QueryToolLocators.output_row_col.format(2, cell_index))
|
||||
|
||||
# Get existing value
|
||||
cell_value = int(cell_el.text)
|
||||
@ -489,9 +475,9 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
||||
|
||||
# Check if the value was updated
|
||||
# Finding element again to avoid stale element reference exception
|
||||
cell_el = self.page.find_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_css_selector(QueryToolLocators.
|
||||
output_row_col.format(2, cell_index))
|
||||
return int(cell_el.text) == new_value
|
||||
|
||||
def _check_can_add_row(self):
|
||||
|
@ -30,7 +30,7 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
||||
scenarios = [
|
||||
("Query tool feature test", dict())
|
||||
]
|
||||
data_output_tab_name = 'Data Output'
|
||||
data_output_tab_id = 'id-dataoutput'
|
||||
table_creation_fail_error = '"CREATE TABLE message does not displayed"'
|
||||
|
||||
def before(self):
|
||||
@ -45,10 +45,10 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
||||
self.wait = WebDriverWait(self.page.driver, 10)
|
||||
|
||||
def runTest(self):
|
||||
skip_warning = "Skipped."
|
||||
# on demand result set on scrolling.
|
||||
print("\nOn demand query result... ",
|
||||
file=sys.stderr, end="")
|
||||
skip_warning = "Skipped."
|
||||
self._on_demand_result()
|
||||
self.page.clear_query_tool()
|
||||
|
||||
@ -128,8 +128,7 @@ class QueryToolFeatureTest(BaseFeatureTest):
|
||||
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'):
|
||||
if btn.get_attribute('data-checked') == 'true':
|
||||
btn.click()
|
||||
|
||||
query_op = self.page.find_by_css_selector(
|
||||
@ -234,17 +233,16 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format(
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.output_column_header_css.format('id1'))
|
||||
column_1.click()
|
||||
canvas_ele = self.page.find_by_css_selector('.grid-canvas')
|
||||
scrolling_height = canvas_ele.size['height']
|
||||
grid = self.page.find_by_css_selector('.rdg')
|
||||
scrolling_height = grid.size['height']
|
||||
self.driver.execute_script(
|
||||
"pgAdmin.SqlEditor.jquery('.slick-viewport')"
|
||||
".scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas')"
|
||||
".height());"
|
||||
"document.querySelector('.rdg').scrollTop="
|
||||
"document.querySelector('.rdg').scrollHeight"
|
||||
)
|
||||
# Table height takes some time to update, for which their is no
|
||||
# particular way
|
||||
time.sleep(2)
|
||||
if canvas_ele.size['height'] == scrolling_height and \
|
||||
if grid.size['height'] == scrolling_height and \
|
||||
self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.output_column_data_xpath.format(
|
||||
row_id_to_find)):
|
||||
@ -279,12 +277,16 @@ SELECT generate_series(1, 1000) as id order by id desc"""
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab(self.data_output_tab_name)
|
||||
self.page.click_tab(self.data_output_tab_id, rc_dock=True)
|
||||
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css))
|
||||
)
|
||||
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR,
|
||||
QueryToolLocators.query_output_cells)))
|
||||
|
||||
# Search for 'Output' word in result (verbose option)
|
||||
canvas.find_element(By.XPATH, "//*[contains(string(), 'Output')]")
|
||||
|
||||
@ -313,14 +315,14 @@ SELECT generate_series(1, 1000) as id order by id desc"""
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
self.page.click_tab(self.data_output_tab_name)
|
||||
self.page.click_tab(self.data_output_tab_id, rc_dock=True)
|
||||
|
||||
self.wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, QueryToolLocators.output_cell_xpath.format(1, 1)))
|
||||
(By.XPATH, QueryToolLocators.output_cell_xpath.format(2, 2)))
|
||||
)
|
||||
|
||||
result = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_cell_xpath.format(1, 1))
|
||||
QueryToolLocators.output_cell_xpath.format(2, 2))
|
||||
|
||||
# Search for 'Shared Read Blocks' word in result (buffers option)
|
||||
self.assertIn('Shared Read Blocks', result.text)
|
||||
@ -350,7 +352,7 @@ CREATE TABLE public.{}();""".format(table_name)
|
||||
self.page.click_execute_query_button()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('CREATE TABLE')),
|
||||
@ -364,7 +366,7 @@ CREATE TABLE public.{}();""".format(table_name)
|
||||
-- 4. Check if table is *NOT* created.
|
||||
ROLLBACK;"""
|
||||
self.page.execute_query(query)
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('ROLLBACK')),
|
||||
"ROLLBACK message does not displayed")
|
||||
@ -378,7 +380,7 @@ SELECT relname FROM pg_catalog.pg_class
|
||||
WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;"""
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.click_tab(self.data_output_tab_name)
|
||||
self.page.click_tab(self.data_output_tab_id, rc_dock=True)
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
@ -432,7 +434,7 @@ CREATE TABLE public.{}();""".format(table_name)
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('CREATE TABLE')),
|
||||
self.table_creation_fail_error)
|
||||
@ -447,7 +449,7 @@ ROLLBACK;"""
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('ROLLBACK')),
|
||||
"ROLLBACK message does not displayed")
|
||||
@ -462,7 +464,7 @@ SELECT relname FROM pg_catalog.pg_class
|
||||
WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;"""
|
||||
|
||||
self.page.execute_query(query)
|
||||
self.page.click_tab(self.data_output_tab_name)
|
||||
self.page.click_tab(self.data_output_tab_id, rc_dock=True)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
@ -511,7 +513,7 @@ END;"""
|
||||
CREATE TABLE public.{}();""".format(table_name)
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('CREATE TABLE')),
|
||||
self.table_creation_fail_error)
|
||||
@ -526,7 +528,7 @@ CREATE TABLE public.{}();""".format(table_name)
|
||||
SELECT 1/0;"""
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
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")
|
||||
@ -541,7 +543,7 @@ SELECT 1/0;"""
|
||||
END;"""
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.
|
||||
format('Query returned successfully')),
|
||||
@ -558,7 +560,7 @@ SELECT relname FROM pg_catalog.pg_class
|
||||
WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;"""
|
||||
self.page.execute_query(query)
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab(self.data_output_tab_name)
|
||||
self.page.click_tab(self.data_output_tab_id, rc_dock=True)
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css)))
|
||||
|
||||
@ -580,30 +582,20 @@ SELECT 1, pg_sleep(300)"""
|
||||
self.page.fill_codemirror_area_with(query)
|
||||
|
||||
# query_button drop can be disabled so enable
|
||||
commit_button = self.page.find_by_css_selector("#btn-commit")
|
||||
commit_button = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_commit)
|
||||
if not commit_button.get_attribute('disabled'):
|
||||
commit_button.click()
|
||||
time.sleep(0.5)
|
||||
|
||||
query_op = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
query_op.click()
|
||||
|
||||
# enable auto-commit and disable auto-rollback
|
||||
self.page.check_execute_option('auto_commit')
|
||||
self.page.uncheck_execute_option('auto_rollback')
|
||||
# close drop down
|
||||
query_op.click()
|
||||
|
||||
# Execute query
|
||||
retry = 5
|
||||
execute_button = self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css)
|
||||
while retry > 0:
|
||||
execute_button.click()
|
||||
if self.page.wait_for_query_tool_loading_indicator_to_appear():
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
# Providing a second of sleep since clicks on the execute and stop
|
||||
# query button is too quick that the query is not able run properly
|
||||
time.sleep(1)
|
||||
@ -611,12 +603,15 @@ SELECT 1, pg_sleep(300)"""
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_cancel_query).click()
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab('Messages')
|
||||
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!"))]'
|
||||
))
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
self.assertTrue(
|
||||
self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message
|
||||
.format('canceling statement due to user request')) or
|
||||
self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message
|
||||
.format('Execution Cancelled!'))
|
||||
)
|
||||
|
||||
def _supported_server_version(self):
|
||||
connection = test_utils.get_db_connection(
|
||||
@ -632,7 +627,7 @@ SELECT 1, pg_sleep(300)"""
|
||||
def _query_tool_notify_statements(self):
|
||||
print("\n\tListen on an event... ", file=sys.stderr, end="")
|
||||
self.page.execute_query("LISTEN foo;")
|
||||
self.page.click_tab('Messages')
|
||||
self.page.click_tab('id-messages', rc_dock=True)
|
||||
|
||||
self.assertTrue(self.page.check_if_element_exist_by_xpath(
|
||||
QueryToolLocators.sql_editor_message.format('LISTEN')),
|
||||
@ -642,9 +637,9 @@ SELECT 1, pg_sleep(300)"""
|
||||
|
||||
print("\tNotify event without data... ", file=sys.stderr, end="")
|
||||
self.page.execute_query("NOTIFY foo;")
|
||||
self.page.click_tab('Notifications')
|
||||
self.page.click_tab('id-notifications', rc_dock=True)
|
||||
self.wait.until(EC.text_to_be_present_in_element(
|
||||
(By.CSS_SELECTOR, "td.channel"), "foo")
|
||||
(By.CSS_SELECTOR, "td[data-label='channel']"), "foo")
|
||||
)
|
||||
print("OK.", file=sys.stderr)
|
||||
|
||||
@ -652,9 +647,9 @@ SELECT 1, pg_sleep(300)"""
|
||||
if self._supported_server_version():
|
||||
self.page.clear_query_tool()
|
||||
self.page.execute_query("SELECT pg_notify('foo', 'Hello')")
|
||||
self.page.click_tab('Notifications')
|
||||
self.page.click_tab('id-notifications', rc_dock=True)
|
||||
self.wait.until(WaitForAnyElementWithText(
|
||||
(By.CSS_SELECTOR, 'td.payload'), "Hello"))
|
||||
(By.CSS_SELECTOR, "td[data-label='payload']"), "Hello"))
|
||||
print("OK.", file=sys.stderr)
|
||||
else:
|
||||
print("Skipped.", file=sys.stderr)
|
||||
@ -700,8 +695,7 @@ SELECT 1, pg_sleep(300)"""
|
||||
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'):
|
||||
if btn.get_attribute('data-checked') == 'true':
|
||||
btn.click()
|
||||
# click cost button
|
||||
cost_btn = self.page.find_by_css_selector(
|
||||
@ -715,7 +709,7 @@ SELECT 1, pg_sleep(300)"""
|
||||
QueryToolLocators.btn_explain_analyze).click()
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
self.page.click_tab(self.data_output_tab_name)
|
||||
self.page.click_tab(self.data_output_tab_id, rc_dock=True)
|
||||
|
||||
canvas = self.wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, QueryToolLocators.query_output_canvas_css))
|
||||
|
@ -145,19 +145,7 @@ CREATE TABLE public.nonintpkey
|
||||
|
||||
@staticmethod
|
||||
def _get_cell_xpath(cell, row):
|
||||
|
||||
if row == 1:
|
||||
xpath_grid_row = "//*[contains(@class, 'ui-widget-content') " \
|
||||
"and contains(@style, 'top:0px')]"
|
||||
else:
|
||||
xpath_grid_row = "//*[contains(@class, 'ui-widget-content') " \
|
||||
"and contains(@style, 'top:25px')]"
|
||||
|
||||
xpath_row_cell = '//div[contains(@class, "' + cell + '")]'
|
||||
|
||||
xpath_cell = '{0}{1}'.format(xpath_grid_row, xpath_row_cell)
|
||||
|
||||
return xpath_cell
|
||||
return QueryToolLocators.output_cell_xpath.format(row, cell)
|
||||
|
||||
@staticmethod
|
||||
def _load_config_data(config_key):
|
||||
@ -179,22 +167,22 @@ CREATE TABLE public.nonintpkey
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
# Run test to insert a new row in table with default values
|
||||
self._add_row(config_data_local)
|
||||
self._verify_row_data(row_height=0,
|
||||
self._verify_row_data(row=1,
|
||||
config_check_data=config_data_local['add'])
|
||||
|
||||
# Run test to copy/paste a row
|
||||
self._copy_paste_row(config_data_local)
|
||||
|
||||
self._update_row(config_data_local)
|
||||
self.page.click_tab("Messages")
|
||||
self.page.click_tab("id-messages", rc_dock=True)
|
||||
self._verify_messsages("")
|
||||
self.page.click_tab("Data Output")
|
||||
self.page.click_tab("id-dataoutput", rc_dock=True)
|
||||
updated_row_data = {
|
||||
i: config_data_local['update'][i] if i in config_data_local[
|
||||
'update'] else val
|
||||
for i, val in config_data_local['add'].items()
|
||||
}
|
||||
self._verify_row_data(row_height=0,
|
||||
self._verify_row_data(row=1,
|
||||
config_check_data=updated_row_data)
|
||||
|
||||
self.page.close_data_grid()
|
||||
@ -273,14 +261,12 @@ CREATE TABLE public.nonintpkey
|
||||
QueryToolLocators.text_editor_ok_btn_css).click()
|
||||
else:
|
||||
# Boolean editor test for to True click
|
||||
checkbox_el = self.page.find_by_css_selector(
|
||||
QueryToolLocators.row_editor_checkbox_css)
|
||||
if data[1] == 'true':
|
||||
checkbox_el = cell_el.find_element(
|
||||
By.XPATH, ".//*[contains(@class, 'multi-checkbox')]")
|
||||
checkbox_el.click()
|
||||
# Boolean editor test for to False click
|
||||
elif data[1] == 'false':
|
||||
checkbox_el = cell_el.find_element(
|
||||
By.XPATH, ".//*[contains(@class, 'multi-checkbox')]")
|
||||
# Sets true
|
||||
checkbox_el.click()
|
||||
# Sets false
|
||||
@ -312,7 +298,7 @@ CREATE TABLE public.nonintpkey
|
||||
)
|
||||
|
||||
def _copy_paste_row(self, config_data_l):
|
||||
row0_cell0_xpath = CheckForViewDataTest._get_cell_xpath("r0", 1)
|
||||
row0_cell0_xpath = CheckForViewDataTest._get_cell_xpath(1, 1)
|
||||
|
||||
self.page.find_by_xpath(row0_cell0_xpath).click()
|
||||
self.page.find_by_css_selector(
|
||||
@ -321,14 +307,15 @@ CREATE TABLE public.nonintpkey
|
||||
QueryToolLocators.paste_button_css).click()
|
||||
|
||||
# Update primary key of copied cell
|
||||
self._add_update_save_row(config_data_l['copy'], row=2)
|
||||
# Copy pasted rows go to first row
|
||||
self._add_update_save_row(config_data_l['copy'], row=1)
|
||||
|
||||
# Verify row 1 and row 2 data
|
||||
updated_row_data = {
|
||||
i: config_data_l['copy'][i] if i in config_data_l['copy'] else val
|
||||
for i, val in config_data_l['add'].items()
|
||||
}
|
||||
self._verify_row_data(row_height=25,
|
||||
self._verify_row_data(row=2,
|
||||
config_check_data=updated_row_data)
|
||||
|
||||
def _add_update_save_row(self, data, row=1):
|
||||
@ -337,9 +324,9 @@ CREATE TABLE public.nonintpkey
|
||||
items[item] = int(items[item])
|
||||
items.sort(reverse=False)
|
||||
for idx in items:
|
||||
cell_xpath = CheckForViewDataTest._get_cell_xpath(
|
||||
'r' + str(idx), row
|
||||
)
|
||||
# rowindex starts with 2 and 1st colindex is rownum
|
||||
cell_xpath = CheckForViewDataTest\
|
||||
._get_cell_xpath(str(idx + 1), row + 1)
|
||||
time.sleep(0.2)
|
||||
self._update_cell(cell_xpath, data[str(idx)])
|
||||
self.page.find_by_css_selector(
|
||||
@ -351,6 +338,9 @@ CREATE TABLE public.nonintpkey
|
||||
time.sleep(2)
|
||||
|
||||
def _add_row(self, config_data_l):
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_add_row).click()
|
||||
time.sleep(1)
|
||||
self._add_update_save_row(config_data_l['add'], 1)
|
||||
|
||||
def _update_row(self, config_data_l):
|
||||
@ -361,11 +351,13 @@ CREATE TABLE public.nonintpkey
|
||||
QueryToolLocators.query_messages_panel)
|
||||
self.assertEqual(text, messages_ele.text)
|
||||
|
||||
def _verify_row_data(self, row_height, config_check_data):
|
||||
def _verify_row_data(self, row, config_check_data):
|
||||
self.page.click_execute_query_button()
|
||||
self.driver.execute_script(
|
||||
"document.querySelector('.rdg').scrollLeft=0"
|
||||
)
|
||||
|
||||
xpath = "//*[contains(@class, 'ui-widget-content') and " \
|
||||
"contains(@style, 'top:" + str(row_height) + "px')]"
|
||||
xpath = QueryToolLocators.output_row_xpath.format(2)
|
||||
scroll_on_arg_for_js = "arguments[0].scrollIntoView(false)"
|
||||
|
||||
self.page.wait_for_query_tool_loading_indicator_to_disappear()
|
||||
@ -379,9 +371,10 @@ CREATE TABLE public.nonintpkey
|
||||
for idx in actual_list:
|
||||
while retry > 0:
|
||||
try:
|
||||
result_row = self.page.find_by_xpath(xpath)
|
||||
element = \
|
||||
result_row.find_element(By.CLASS_NAME, "r" + str(idx))
|
||||
element = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_cell_xpath
|
||||
.format(row + 1, idx + 1)
|
||||
)
|
||||
self.page.driver.execute_script(
|
||||
scroll_on_arg_for_js, element)
|
||||
break
|
||||
@ -399,6 +392,7 @@ CREATE TABLE public.nonintpkey
|
||||
list_item.sort(reverse=True)
|
||||
for idx in list_item:
|
||||
time.sleep(0.4)
|
||||
element = result_row.find_element(By.CLASS_NAME, "r" + str(idx))
|
||||
element = self.page.find_by_xpath(
|
||||
QueryToolLocators.output_cell_xpath.format(2, idx + 1))
|
||||
self.page.driver.execute_script(
|
||||
scroll_on_arg_for_js, element)
|
||||
|
@ -196,17 +196,12 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
self.page.fill_codemirror_area_with(
|
||||
"select '<img src=\"x\" onerror=\"console.log(1)\">'"
|
||||
)
|
||||
self.page.find_by_id("btn-flash").click()
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
result_row = self.page.find_by_xpath(
|
||||
"//*[contains(@class, 'ui-widget-content') and "
|
||||
"contains(@style, 'top:0px')]"
|
||||
)
|
||||
|
||||
cells = result_row.find_elements(By.TAG_NAME, 'div')
|
||||
|
||||
# remove first element as it is row number.
|
||||
source_code = cells[1].get_attribute('innerHTML')
|
||||
source_code = self.page\
|
||||
.find_by_xpath(QueryToolLocators.output_cell_xpath.format(2, 2))\
|
||||
.get_attribute('innerHTML')
|
||||
|
||||
self._check_escaped_characters(
|
||||
source_code,
|
||||
@ -225,13 +220,12 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
self.page.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css).click()
|
||||
|
||||
self.page.click_tab('Query History')
|
||||
self.page.click_tab('id-history', rc_dock=True)
|
||||
|
||||
# Check for history entry
|
||||
history_ele = self.page.find_by_css_selector(
|
||||
".query-history div.query-group:first-child"
|
||||
" .list-item.selected .query"
|
||||
)
|
||||
history_ele = self.page\
|
||||
.find_by_css_selector(
|
||||
QueryToolLocators.query_history_specific_entry.format(2))
|
||||
|
||||
source_code = history_ele.get_attribute('innerHTML')
|
||||
|
||||
@ -246,7 +240,7 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
try:
|
||||
history_ele = self.driver \
|
||||
.find_element(By.CSS_SELECTOR,
|
||||
".query-detail .content-value")
|
||||
QueryToolLocators.query_history_detail)
|
||||
source_code = history_ele.get_attribute('innerHTML')
|
||||
break
|
||||
except StaleElementReferenceException:
|
||||
@ -258,25 +252,7 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
"Query tool (History Details-Message)"
|
||||
)
|
||||
|
||||
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,
|
||||
self.check_xss_chars_set2,
|
||||
"Query tool (History Details-Error)"
|
||||
)
|
||||
|
||||
self.page.click_tab('Query Editor')
|
||||
self.page.click_tab('id-query', rc_dock=True)
|
||||
|
||||
def _check_xss_view_data(self):
|
||||
print(
|
||||
@ -284,13 +260,11 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
file=sys.stderr, end=""
|
||||
)
|
||||
|
||||
self.page.find_by_css_selector(".slick-header-column")
|
||||
cells = self.driver. \
|
||||
find_elements(By.CSS_SELECTOR, ".slick-header-column")
|
||||
|
||||
# remove first element as it is row number.
|
||||
# currently 4th col
|
||||
source_code = cells[4].get_attribute('innerHTML')
|
||||
source_code = self.page \
|
||||
.find_by_xpath(QueryToolLocators.output_cell_xpath.format(1, 5)) \
|
||||
.get_attribute('innerHTML')
|
||||
|
||||
self._check_escaped_characters(
|
||||
source_code,
|
||||
@ -310,7 +284,7 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
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')
|
||||
self.page.click_tab('id-explain', rc_dock=True)
|
||||
|
||||
for idx in range(3):
|
||||
# Re-try logic
|
||||
@ -318,7 +292,7 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
ActionChains(self.driver).move_to_element(
|
||||
self.driver.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
'div.pgadmin-explain-container > svg > g > g > image')
|
||||
'div#id-explain svg > g > g > image')
|
||||
).click().perform()
|
||||
break
|
||||
except Exception:
|
||||
@ -333,8 +307,8 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
raise
|
||||
|
||||
source_code = self.driver.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
'.pgadmin-explain-details:not(.d-none)').get_attribute('innerHTML')
|
||||
By.CSS_SELECTOR, QueryToolLocators.explain_details)\
|
||||
.get_attribute('innerHTML')
|
||||
|
||||
self._check_escaped_characters(
|
||||
source_code,
|
||||
|
@ -411,7 +411,7 @@ export default function Graphical({planData, ctx}) {
|
||||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
{Boolean(explainPlanDetails) &&
|
||||
<Card className={classes.explainDetails}>
|
||||
<Card className={classes.explainDetails} data-label="explain-details">
|
||||
<CardHeader title={<Box display="flex">
|
||||
{explainPlanTitle}
|
||||
<Box marginLeft="auto">
|
||||
|
@ -94,8 +94,9 @@ export const PrimaryButton = forwardRef((props, ref)=>{
|
||||
allClassName.push(classes.xsButton);
|
||||
}
|
||||
noBorder && allClassName.push(classes.noBorder);
|
||||
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
||||
return (
|
||||
<Button ref={ref} size={size} className={clsx(allClassName)} {...otherProps} variant="contained" color="primary" >{children}</Button>
|
||||
<Button ref={ref} size={size} className={clsx(allClassName)} data-label={dataLabel} {...otherProps} variant="contained" color="primary">{children}</Button>
|
||||
);
|
||||
});
|
||||
PrimaryButton.displayName = 'PrimaryButton';
|
||||
@ -116,8 +117,9 @@ export const DefaultButton = forwardRef((props, ref)=>{
|
||||
allClassName.push(classes.xsButton);
|
||||
}
|
||||
noBorder && allClassName.push(classes.noBorder);
|
||||
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
||||
return (
|
||||
<Button variant="outlined" color="default" ref={ref} size={size} className={clsx(allClassName)} {...otherProps} >{children}</Button>
|
||||
<Button variant="outlined" color="default" ref={ref} size={size} className={clsx(allClassName)} data-label={dataLabel} {...otherProps}>{children}</Button>
|
||||
);
|
||||
});
|
||||
DefaultButton.displayName = 'DefaultButton';
|
||||
@ -144,7 +146,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
||||
return (
|
||||
<PrimaryButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)}
|
||||
accessKey={accesskey} {...props}>
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</PrimaryButton>
|
||||
);
|
||||
@ -152,7 +154,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
||||
return (
|
||||
<DefaultButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)}
|
||||
accessKey={accesskey} {...props}>
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</DefaultButton>
|
||||
);
|
||||
@ -163,7 +165,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
|
||||
<PrimaryButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)}
|
||||
accessKey={accesskey} {...props}>
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</PrimaryButton>
|
||||
</Tooltip>
|
||||
@ -173,7 +175,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
|
||||
<DefaultButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)}
|
||||
accessKey={accesskey} {...props}>
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</DefaultButton>
|
||||
</Tooltip>
|
||||
|
@ -24,47 +24,47 @@ ExternalIcon.propTypes = {
|
||||
Icon: PropTypes.elementType.isRequired,
|
||||
};
|
||||
|
||||
export const QueryToolIcon = ({style})=><ExternalIcon Icon={QueryToolSvg} style={{height: '1rem', ...style}} />;
|
||||
export const QueryToolIcon = ({style})=><ExternalIcon Icon={QueryToolSvg} style={{height: '1rem', ...style}} data-label="QueryToolIcon" />;
|
||||
QueryToolIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const ViewDataIcon = ({style})=><ExternalIcon Icon={ViewDataSvg} style={{height: '0.8rem', ...style}} />;
|
||||
export const ViewDataIcon = ({style})=><ExternalIcon Icon={ViewDataSvg} style={{height: '0.8rem', ...style}} data-label="ViewDataIcon" />;
|
||||
ViewDataIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const SaveDataIcon = ({style})=><ExternalIcon Icon={SaveDataSvg} style={{height: '1rem', ...style}} />;
|
||||
export const SaveDataIcon = ({style})=><ExternalIcon Icon={SaveDataSvg} style={{height: '1rem', ...style}} data-label="SaveDataIcon" />;
|
||||
SaveDataIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const PasteIcon = ({style})=><ExternalIcon Icon={PasteSvg} style={style} />;
|
||||
export const PasteIcon = ({style})=><ExternalIcon Icon={PasteSvg} style={style} data-label="PasteIcon" />;
|
||||
PasteIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const FilterIcon = ({style})=><ExternalIcon Icon={FilterSvg} style={style} />;
|
||||
export const FilterIcon = ({style})=><ExternalIcon Icon={FilterSvg} style={style} data-label="FilterIcon" />;
|
||||
FilterIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const CommitIcon = ({style})=><ExternalIcon Icon={CommitSvg} style={style} />;
|
||||
export const CommitIcon = ({style})=><ExternalIcon Icon={CommitSvg} style={{height: '1.2rem', ...style}} data-label="CommitIcon" />;
|
||||
CommitIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const RollbackIcon = ({style})=><ExternalIcon Icon={RollbackSvg} style={style} />;
|
||||
export const RollbackIcon = ({style})=><ExternalIcon Icon={RollbackSvg} style={{height: '1.2rem', ...style}} data-label="RollbackIcon" />;
|
||||
RollbackIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const ClearIcon = ({style})=><ExternalIcon Icon={ClearSvg} style={style} />;
|
||||
export const ClearIcon = ({style})=><ExternalIcon Icon={ClearSvg} style={style} data-label="ClearIcon" />;
|
||||
ClearIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const ConnectedIcon = ({style})=><ExternalIcon Icon={ConnectedSvg} style={{height: '1rem', ...style}} />;
|
||||
export const ConnectedIcon = ({style})=><ExternalIcon Icon={ConnectedSvg} style={{height: '1rem', ...style}} data-label="ConnectedIcon" />;
|
||||
ConnectedIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const DisonnectedIcon = ({style})=><ExternalIcon Icon={DisconnectedSvg} style={{height: '1rem', ...style}} />;
|
||||
export const DisonnectedIcon = ({style})=><ExternalIcon Icon={DisconnectedSvg} style={{height: '1rem', ...style}} data-label="DisonnectedIcon" />;
|
||||
DisonnectedIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const RegexIcon = ({style})=><ExternalIcon Icon={RegexSvg} style={style} />;
|
||||
export const RegexIcon = ({style})=><ExternalIcon Icon={RegexSvg} style={style} data-label="RegexIcon" />;
|
||||
RegexIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const FormatCaseIcon = ({style})=><ExternalIcon Icon={FormatCaseSvg} style={style} />;
|
||||
export const FormatCaseIcon = ({style})=><ExternalIcon Icon={FormatCaseSvg} style={style} data-label="FormatCaseIcon" />;
|
||||
FormatCaseIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const ExpandDialogIcon = ({style})=><ExternalIcon Icon={Expand} style={{height: '1.2rem', ...style}} />;QueryToolIcon.propTypes = {style: PropTypes.object};
|
||||
export const ExpandDialogIcon = ({style})=><ExternalIcon Icon={Expand} style={{height: '1.2rem', ...style}} data-label="ExpandDialogIcon" />;
|
||||
ExpandDialogIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const MinimizeDialogIcon = ({style})=><ExternalIcon Icon={Collapse} style={{height: '1.4rem', ...style}} />;
|
||||
export const MinimizeDialogIcon = ({style})=><ExternalIcon Icon={Collapse} style={{height: '1.4rem', ...style}} data-label="MinimizeDialogIcon" />;
|
||||
MinimizeDialogIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const AWSIcon = ({style})=><ExternalIcon Icon={AWS} style={{height: '1.4rem', ...style}} />;
|
||||
export const AWSIcon = ({style})=><ExternalIcon Icon={AWS} style={{height: '1.4rem', ...style}} data-label="AWSIcon" />;
|
||||
AWSIcon.propTypes = {style: PropTypes.object};
|
||||
|
@ -40,13 +40,13 @@ const useStyles = makeStyles((theme)=>({
|
||||
}
|
||||
}));
|
||||
|
||||
export default function Loader({message, style}) {
|
||||
export default function Loader({message, style, ...props}) {
|
||||
const classes = useStyles();
|
||||
if(!message) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Box className={classes.root} style={style}>
|
||||
<Box className={classes.root} style={style} data-label="loader" {...props}>
|
||||
<Box className={classes.loaderRoot}>
|
||||
<CircularProgress className={classes.loader} />
|
||||
<Typography className={classes.message}>{message}</Typography>
|
||||
|
@ -24,6 +24,7 @@ const useStyles = makeStyles((theme)=>({
|
||||
},
|
||||
'& .szh-menu__divider': {
|
||||
margin: 0,
|
||||
background: theme.otherVars.borderColor,
|
||||
}
|
||||
},
|
||||
menuItem: {
|
||||
@ -47,13 +48,17 @@ const useStyles = makeStyles((theme)=>({
|
||||
}
|
||||
}));
|
||||
|
||||
export function PgMenu({open, className, ...props}) {
|
||||
export function PgMenu({open, className, label, ...props}) {
|
||||
const classes = useStyles();
|
||||
const state = open ? 'open' : 'closed';
|
||||
props.anchorRef?.current?.setAttribute('data-state', state);
|
||||
return (
|
||||
<ControlledMenu
|
||||
state={open ? 'open' : 'closed'}
|
||||
state={state}
|
||||
{...props}
|
||||
className={clsx(classes.menu, className)}
|
||||
aria-label={label || 'Menu'}
|
||||
data-state={state}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -61,6 +66,8 @@ export function PgMenu({open, className, ...props}) {
|
||||
PgMenu.propTypes = {
|
||||
open: PropTypes.bool,
|
||||
className: CustomPropTypes.className,
|
||||
label: PropTypes.string,
|
||||
anchorRef: CustomPropTypes.ref,
|
||||
};
|
||||
|
||||
export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false, accesskey, shortcut, children, ...props})=>{
|
||||
@ -72,7 +79,8 @@ export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false
|
||||
props.onClick(e);
|
||||
};
|
||||
}
|
||||
return <MenuItem {...props} onClick={onClick} className={classes.menuItem}>
|
||||
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
||||
return <MenuItem {...props} onClick={onClick} className={classes.menuItem} data-label={dataLabel} data-checked={checked}>
|
||||
{hasCheck && <CheckIcon className={classes.checkIcon} style={checked ? {} : {visibility: 'hidden'}} />}
|
||||
{children}
|
||||
{(shortcut || accesskey) && <div className={classes.shortcut}>({shortcutToString(shortcut, accesskey)})</div>}
|
||||
|
@ -53,18 +53,23 @@ function initConnection(api, params, passdata) {
|
||||
}
|
||||
|
||||
function setPanelTitle(panel, title, qtState, dirty=false) {
|
||||
if(title) {
|
||||
title =title.split('\\').pop().split('/').pop();
|
||||
} else if(qtState.current_file) {
|
||||
if(qtState.current_file) {
|
||||
title = qtState.current_file.split('\\').pop().split('/').pop();
|
||||
} else {
|
||||
title = qtState.params.title || 'Untitled';
|
||||
} else if (!qtState.is_new_tab) {
|
||||
if(!title) {
|
||||
title = panel.$titleText?.[0].textContent;
|
||||
if(panel.is_dirty_editor) {
|
||||
// remove asterisk
|
||||
title = title.slice(0, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
title = title + (dirty ? '*': '');
|
||||
if (qtState.is_new_tab) {
|
||||
window.document.title = title;
|
||||
} else {
|
||||
panel.is_dirty_editor = dirty;
|
||||
setQueryToolDockerTitle(panel, true, title, qtState.current_file ? true : false);
|
||||
}
|
||||
}
|
||||
@ -109,6 +114,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
const setQtState = (state)=>{
|
||||
_setQtState((prev)=>({...prev,...evalFunc(null, state, prev)}));
|
||||
};
|
||||
const isDirtyRef = useRef(false); // usefull when conn change.
|
||||
const eventBus = useRef(eventBusObj || (new EventBus()));
|
||||
const docker = useRef(null);
|
||||
const api = useMemo(()=>getApiInstance(), []);
|
||||
@ -411,6 +417,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
[QUERY_TOOL_EVENTS.LOAD_FILE_DONE, fileDone],
|
||||
[QUERY_TOOL_EVENTS.SAVE_FILE_DONE, fileDone],
|
||||
[QUERY_TOOL_EVENTS.QUERY_CHANGED, (isDirty)=>{
|
||||
isDirtyRef.current = isDirty;
|
||||
if(qtState.params.is_query_tool) {
|
||||
setPanelTitle(panel, null, qtState, isDirty);
|
||||
}
|
||||
@ -511,6 +518,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
obtaining_conn: false,
|
||||
};
|
||||
});
|
||||
setPanelTitle(panel, connectionData.title, qtState, isDirtyRef.current);
|
||||
let msg = `${connectionData['server_name']}/${connectionData['database_name']} - Database connected`;
|
||||
Notifier.success(msg);
|
||||
resolve();
|
||||
|
@ -186,7 +186,7 @@ export function TextEditor({row, column, onRowChange, onClose}) {
|
||||
<Portal container={document.body}>
|
||||
<Box ref={(ele)=>{
|
||||
setEditorPosition(getCellElement(column.idx), ele);
|
||||
}} className={classes.textEditor}>
|
||||
}} className={classes.textEditor} data-label="pg-editor">
|
||||
<textarea ref={autoFocusAndSelect} className={classes.textarea} value={localVal} onChange={onChange} />
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<DefaultButton startIcon={<CloseIcon />} onClick={()=>onClose(false)} size="small">
|
||||
@ -287,7 +287,7 @@ export function CheckboxEditor({row, column, onRowChange, onClose}) {
|
||||
className = 'intermediate';
|
||||
}
|
||||
return (
|
||||
<div onClick={changeValue} tabIndex="0" onBlur={onBlur}>
|
||||
<div onClick={changeValue} tabIndex="0" onBlur={onBlur} data-label="pg-checkbox-editor">
|
||||
<span className={clsx(classes.check, className)}></span>
|
||||
</div>
|
||||
);
|
||||
@ -334,7 +334,7 @@ export function JsonTextEditor({row, column, onRowChange, onClose}) {
|
||||
<Portal container={document.body}>
|
||||
<Box ref={(ele)=>{
|
||||
setEditorPosition(getCellElement(column.idx), ele);
|
||||
}} className={classes.jsonEditor}>
|
||||
}} className={classes.jsonEditor} data-label="pg-editor">
|
||||
<JsonEditor
|
||||
value={localVal}
|
||||
options={{
|
||||
|
@ -8,7 +8,7 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box, makeStyles } from '@material-ui/core';
|
||||
import _ from 'lodash';
|
||||
import React, {useState, useEffect, useContext, useRef} from 'react';
|
||||
import React, {useState, useEffect, useContext, useRef, useLayoutEffect} from 'react';
|
||||
import ReactDataGrid, {Row, useRowSelection} from 'react-data-grid';
|
||||
import LockIcon from '@material-ui/icons/Lock';
|
||||
import EditIcon from '@material-ui/icons/Edit';
|
||||
@ -130,23 +130,42 @@ CustomRow.propTypes = {
|
||||
viewportColumns: PropTypes.array,
|
||||
};
|
||||
|
||||
function SelectAllHeaderRenderer(props) {
|
||||
function getCopyShortcutHandler(handleCopy) {
|
||||
return (e)=>{
|
||||
if((e.ctrlKey || e.metaKey) && e.key !== 'Control' && e.keyCode == 67) {
|
||||
handleCopy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function SelectAllHeaderRenderer({onAllRowsSelectionChange, isCellSelected}) {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const cellRef = useRef();
|
||||
const eventBus = useContext(QueryToolEventsContext);
|
||||
const dataGridExtras = useContext(DataGridExtrasContext);
|
||||
const onClick = ()=>{
|
||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, true, ()=>{
|
||||
setChecked(!checked);
|
||||
props.onAllRowsSelectionChange(!checked);
|
||||
onAllRowsSelectionChange(!checked);
|
||||
});
|
||||
};
|
||||
return <div style={{widht: '100%', height: '100%'}} onClick={onClick}></div>;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isCellSelected) return;
|
||||
cellRef.current?.focus({ preventScroll: true });
|
||||
}, [isCellSelected]);
|
||||
|
||||
return <div ref={cellRef} style={{width: '100%', height: '100%'}} onClick={onClick}
|
||||
tabIndex="0" onKeyDown={getCopyShortcutHandler(dataGridExtras.handleCopy)}></div>;
|
||||
}
|
||||
SelectAllHeaderRenderer.propTypes = {
|
||||
onAllRowsSelectionChange: PropTypes.func,
|
||||
isCellSelected: PropTypes.bool,
|
||||
};
|
||||
|
||||
function SelectableHeaderRenderer({column, selectedColumns, onSelectedColumnsChange, isCellSelected}) {
|
||||
const classes = useStyles();
|
||||
const cellRef = useRef();
|
||||
const eventBus = useContext(QueryToolEventsContext);
|
||||
const dataGridExtras = useContext(DataGridExtrasContext);
|
||||
|
||||
@ -168,11 +187,17 @@ function SelectableHeaderRenderer({column, selectedColumns, onSelectedColumnsCha
|
||||
|
||||
const isSelected = selectedColumns.has(column.idx);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isCellSelected) return;
|
||||
cellRef.current?.focus({ preventScroll: true });
|
||||
}, [isCellSelected]);
|
||||
|
||||
return (
|
||||
<Box className={clsx(classes.columnHeader, isSelected ? classes.colHeaderSelected : null)} onClick={onClick}>
|
||||
<Box ref={cellRef} className={clsx(classes.columnHeader, isSelected ? classes.colHeaderSelected : null)} onClick={onClick} tabIndex="0"
|
||||
onKeyDown={getCopyShortcutHandler(dataGridExtras.handleCopy)} data-column-key={column.key}>
|
||||
{(column.column_type_internal == 'geometry' || column.column_type_internal == 'geography') &&
|
||||
<Box>
|
||||
<PgIconButton title={gettext('View all geometries in this column')} icon={<MapIcon />} size="small" style={{marginRight: '0.25rem'}} onClick={(e)=>{
|
||||
<PgIconButton title={gettext('View all geometries in this column')} icon={<MapIcon data-label="MapIcon"/>} size="small" style={{marginRight: '0.25rem'}} onClick={(e)=>{
|
||||
e.stopPropagation();
|
||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_RENDER_GEOMETRIES, column);
|
||||
}}/>
|
||||
@ -182,8 +207,8 @@ function SelectableHeaderRenderer({column, selectedColumns, onSelectedColumnsCha
|
||||
<span>{column.display_type}</span>
|
||||
</Box>
|
||||
<Box marginLeft="4px">{column.can_edit ?
|
||||
<EditIcon fontSize="small" style={{fontSize: '0.875rem'}} />:
|
||||
<LockIcon fontSize="small" style={{fontSize: '0.875rem'}} />
|
||||
<EditIcon fontSize="small" style={{fontSize: '0.875rem'}} data-label="EditIcon"/>:
|
||||
<LockIcon fontSize="small" style={{fontSize: '0.875rem'}} data-label="LockIcon"/>
|
||||
}</Box>
|
||||
</Box>
|
||||
);
|
||||
@ -372,7 +397,7 @@ export default function QueryToolDataGrid({columns, rows, totalRowCount, dataCha
|
||||
}
|
||||
|
||||
return (
|
||||
<DataGridExtrasContext.Provider value={{onSelectedCellChange}}>
|
||||
<DataGridExtrasContext.Provider value={{onSelectedCellChange, handleCopy}}>
|
||||
<ReactDataGrid
|
||||
id="datagrid"
|
||||
columns={readyColumns}
|
||||
|
@ -537,6 +537,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
anchorRef={saveAsMenuRef}
|
||||
open={openMenuName=='menu-saveas'}
|
||||
onClose={onMenuClose}
|
||||
label={gettext('File Menu')}
|
||||
>
|
||||
<PgMenuItem onClick={()=>{saveFile(true);}}>{gettext('Save as')}</PgMenuItem>
|
||||
</PgMenu>
|
||||
@ -544,6 +545,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
anchorRef={editMenuRef}
|
||||
open={openMenuName=='menu-edit'}
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Edit Menu')}
|
||||
>
|
||||
<PgMenuItem shortcut={FIXED_PREF.find}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, false);}}>{gettext('Find')}</PgMenuItem>
|
||||
@ -567,6 +569,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
anchorRef={filterMenuRef}
|
||||
open={openMenuName=='menu-filter'}
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Filter Options Menu')}
|
||||
>
|
||||
<PgMenuItem onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, true);}}>{gettext('Filter by Selection')}</PgMenuItem>
|
||||
<PgMenuItem onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, false);}}>{gettext('Exclude by Selection')}</PgMenuItem>
|
||||
@ -576,6 +579,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
anchorRef={autoCommitMenuRef}
|
||||
open={openMenuName=='menu-autocommit'}
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Execute Options Menu')}
|
||||
>
|
||||
<PgMenuItem hasCheck value="auto_commit" checked={checkedMenuItems['auto_commit']}
|
||||
onClick={checkMenuClick}>{gettext('Auto commit?')}</PgMenuItem>
|
||||
@ -586,6 +590,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
anchorRef={explainMenuRef}
|
||||
open={openMenuName=='menu-explain'}
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Explain Options Menu')}
|
||||
>
|
||||
<PgMenuItem hasCheck value="explain_verbose" checked={checkedMenuItems['explain_verbose']}
|
||||
onClick={checkMenuClick}>{gettext('Verbose')}</PgMenuItem>
|
||||
@ -604,6 +609,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
anchorRef={macrosMenuRef}
|
||||
open={openMenuName=='menu-macros'}
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Macros Menu')}
|
||||
>
|
||||
<PgMenuItem onClick={onManageMacros}>{gettext('Manage macros')}</PgMenuItem>
|
||||
<PgMenuDivider />
|
||||
|
@ -47,10 +47,10 @@ export function Notifications() {
|
||||
<tbody>
|
||||
{notices.map((notice, i)=>{
|
||||
return <tr key={i}>
|
||||
<td>{notice.recorded_time}</td>
|
||||
<td>{notice.channel}</td>
|
||||
<td>{notice.pid}</td>
|
||||
<td>{notice.payload}</td>
|
||||
<td data-label="recorded_time">{notice.recorded_time}</td>
|
||||
<td data-label="channel">{notice.channel}</td>
|
||||
<td data-label="pid">{notice.pid}</td>
|
||||
<td data-label="payload">{notice.payload}</td>
|
||||
</tr>;
|
||||
})}
|
||||
</tbody>
|
||||
|
@ -239,11 +239,11 @@ class QueryHistoryUtils {
|
||||
function QuerySourceIcon({source}) {
|
||||
switch(JSON.stringify(source)) {
|
||||
case JSON.stringify(QuerySources.EXECUTE):
|
||||
return <PlayArrowRoundedIcon style={{marginLeft: '-4px'}}/>;
|
||||
return <PlayArrowRoundedIcon style={{marginLeft: '-4px'}} data-label="ExecuteIcon" />;
|
||||
case JSON.stringify(QuerySources.EXPLAIN):
|
||||
return <ExplicitRoundedIcon/>;
|
||||
return <ExplicitRoundedIcon data-label="ExplainIcon" />;
|
||||
case JSON.stringify(QuerySources.EXPLAIN_ANALYZE):
|
||||
return <AssessmentRoundedIcon/>;
|
||||
return <AssessmentRoundedIcon data-label="ExplainAnalyzeIcon" />;
|
||||
case JSON.stringify(QuerySources.COMMIT):
|
||||
return <CommitIcon style={{marginLeft: '-4px'}}/>;
|
||||
case JSON.stringify(QuerySources.ROLLBACK):
|
||||
@ -262,7 +262,7 @@ QuerySourceIcon.propTypes = {
|
||||
|
||||
function HistoryEntry({entry, formatEntryDate, itemKey, selectedItemKey, onClick}) {
|
||||
const classes = useStyles();
|
||||
return <ListItem tabIndex="0" ref={(ele)=>{
|
||||
return <ListItem tabIndex="0" data-label="history-entry" data-pgadmin={entry.is_pgadmin_query} ref={(ele)=>{
|
||||
selectedItemKey==itemKey && ele && ele.scrollIntoView({
|
||||
block: 'center',
|
||||
behavior: 'smooth',
|
||||
@ -324,7 +324,7 @@ function QueryHistoryDetails({entry}) {
|
||||
return (
|
||||
<>
|
||||
{entry.info && <Box className={classes.infoHeader}>{entry.info}</Box>}
|
||||
<Box padding="0.5rem">
|
||||
<Box padding="0.5rem" data-label="history-detail">
|
||||
<Grid container>
|
||||
<Grid item sm={4}>{entry.start_time.toLocaleDateString() + ' ' + entry.start_time.toLocaleTimeString()}</Grid>
|
||||
<Grid item sm={4}>{entry?.row_affected > 0 && entry.row_affected}</Grid>
|
||||
|
@ -1225,7 +1225,7 @@ export function ResultSet() {
|
||||
return (
|
||||
<Box className={classes.root} ref={containerRef} tabIndex="0">
|
||||
<Loader message={loaderText} />
|
||||
<Loader message={isLoadingMore ? gettext('Loading more rows...') : null} style={{top: 'unset', right: 'unset', padding: '0.5rem 1rem'}}/>
|
||||
<Loader data-label="loader-more-rows" message={isLoadingMore ? gettext('Loading more rows...') : null} style={{top: 'unset', right: 'unset', padding: '0.5rem 1rem'}}/>
|
||||
{!queryData &&
|
||||
<EmptyPanelMessage text={gettext('No data output. Execute a query to get output.')}/>
|
||||
}
|
||||
|
@ -163,6 +163,7 @@ export function ResultSetToolbar({containerRef, canEdit, totalRowCount}) {
|
||||
anchorRef={copyMenuRef}
|
||||
open={menuOpenId=='menu-copyheader'}
|
||||
onClose={handleMenuClose}
|
||||
label={gettext('Copy Options Menu')}
|
||||
>
|
||||
<PgMenuItem hasCheck value="copy_with_headers" checked={checkedMenuItems['copy_with_headers']} onClick={checkMenuClick}>{gettext('Copy with headers')}</PgMenuItem>
|
||||
</PgMenu>
|
||||
|
@ -108,105 +108,127 @@ class NavMenuLocators:
|
||||
"//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') " \
|
||||
"and contains(.,'{}')]"
|
||||
|
||||
rcdock_tab = "div.dock-tab-btn[id$='{0}']"
|
||||
|
||||
process_watcher_error_close_xpath = \
|
||||
".btn.btn-sm-sq.btn-primary.pg-bg-close > i"
|
||||
|
||||
|
||||
class QueryToolLocators:
|
||||
btn_save_file = "#btn-save-file"
|
||||
btn_save_file = "button[data-label='Save File']"
|
||||
|
||||
btn_save_data = "#btn-save-data"
|
||||
btn_save_data = "button[data-label='Save Data Changes']"
|
||||
|
||||
btn_query_dropdown = "#btn-query-dropdown"
|
||||
btn_query_dropdown = "button[data-label='Execute options']"
|
||||
|
||||
btn_auto_rollback = "#btn-auto-rollback"
|
||||
btn_auto_rollback = "li[data-label='Auto rollback on error?']"
|
||||
|
||||
btn_auto_rollback_check_status = "#btn-auto-rollback > i"
|
||||
|
||||
btn_auto_commit = "#btn-auto-commit"
|
||||
btn_auto_commit = "li[data-label='Auto commit?']"
|
||||
|
||||
btn_auto_commit_check_status = "#btn-auto-commit > i"
|
||||
|
||||
btn_cancel_query = "#btn-cancel-query"
|
||||
btn_cancel_query = "button[data-label='Cancel query']"
|
||||
|
||||
btn_explain = "#btn-explain"
|
||||
btn_explain = "button[data-label='Explain']"
|
||||
|
||||
btn_explain_analyze = "#btn-explain-analyze"
|
||||
btn_explain_analyze = "button[data-label='Explain Analyze']"
|
||||
|
||||
btn_explain_options_dropdown = "#btn-explain-options-dropdown"
|
||||
btn_explain_options_dropdown = "button[data-label='Explain Settings']"
|
||||
|
||||
btn_explain_verbose = "#btn-explain-verbose"
|
||||
btn_explain_verbose = "li[data-label='Verbose']"
|
||||
|
||||
btn_explain_costs = "#btn-explain-costs"
|
||||
btn_explain_costs = "li[data-label='Costs']"
|
||||
|
||||
btn_explain_buffers = "#btn-explain-buffers"
|
||||
btn_explain_buffers = "li[data-label='Buffers']"
|
||||
|
||||
btn_explain_timing = "#btn-explain-timing"
|
||||
btn_explain_timing = "li[data-label='Timing']"
|
||||
|
||||
btn_clear_dropdown = "#btn-clear-dropdown"
|
||||
btn_edit_dropdown = "button[data-label='Edit']"
|
||||
|
||||
btn_clear_history = "#btn-clear-history"
|
||||
|
||||
btn_clear = "#btn-clear"
|
||||
btn_clear = "li[data-label='Clear Query']"
|
||||
|
||||
query_editor_panel = "#output-panel"
|
||||
btn_add_row = "button[data-label='Add row']"
|
||||
|
||||
query_history_selected = "#query_list .selected"
|
||||
query_tool_menu = "ul[aria-label='{0}']"
|
||||
|
||||
query_history_entries = "#query_list>.query-group>ul>li"
|
||||
query_editor_panel = "#id-query"
|
||||
|
||||
query_history_selected = \
|
||||
"#id-history li[data-label='history-entry'].Mui-selected"
|
||||
|
||||
query_history_entries = "#id-history li[data-label='history-entry']"
|
||||
|
||||
query_history_specific_entry = \
|
||||
"#query_list>.query-group>ul>li:nth-child({})"
|
||||
"#id-history li[data-label='history-entry']:nth-child({0})"
|
||||
|
||||
query_history_detail = "#query_detail"
|
||||
query_history_detail = "#id-history div[data-label='history-detail']"
|
||||
|
||||
invalid_query_history_entry_css = "#query_list .entry.error .query"
|
||||
query_history_selected_icon = query_history_selected + ' svg'
|
||||
|
||||
invalid_query_history_entry_css = \
|
||||
"#id-history li[data-label='history-entry'][class*='itemError']"
|
||||
|
||||
explain_details = "#id-explain div[data-label='explain-details']"
|
||||
|
||||
editor_panel = "#output-panel"
|
||||
|
||||
query_messages_panel = ".sql-editor-message"
|
||||
query_messages_panel = "#id-messages"
|
||||
|
||||
output_row_xpath = "//div[contains(@class, 'slick-row')][{}]/*[1]"
|
||||
output_row = "#id-dataoutput div.rdg-row[aria-rowindex={0}]"
|
||||
|
||||
output_column_header_css = "[data-column-id='{}']"
|
||||
output_row_col = "#id-dataoutput div.rdg-row[aria-rowindex='{0}']" \
|
||||
" div.rdg-cell[aria-colindex='{1}']"
|
||||
|
||||
output_column_data_xpath = "//div[contains(@class, 'slick-cell')]" \
|
||||
output_column_header_css = \
|
||||
"#id-dataoutput div.rdg-cell div[data-column-key='{0}']"
|
||||
|
||||
output_column_data_xpath = "//div[contains(@class, 'rdg-cell')]" \
|
||||
"[contains(., '{}')]"
|
||||
output_cell_xpath = "//div[contains(@class, 'slick-cell') and " \
|
||||
"contains(@class, 'l{0} r{1}')]"
|
||||
output_row_xpath = "//div[@aria-rowindex='{0}']"
|
||||
output_cell_xpath = "//div[@aria-rowindex='{0}']/div[@aria-colindex='{1}']"
|
||||
|
||||
select_all_column = \
|
||||
"//div[contains(@id,'row-header-column')]"
|
||||
"//div[@role='columnheader'][@aria-colindex='1']"
|
||||
|
||||
new_row_xpath = "//div[contains(@class, 'new-row')]"
|
||||
|
||||
scratch_pad_css = ".sql-scratch > textarea"
|
||||
scratch_pad_css = "#id-scratch textarea"
|
||||
|
||||
copy_button_css = "#btn-copy-row"
|
||||
copy_button_css = "#id-dataoutput button[data-label='Copy']"
|
||||
|
||||
paste_button_css = "#btn-paste-row"
|
||||
copy_options_css = "#id-dataoutput button[data-label='Copy options']"
|
||||
|
||||
row_editor_text_area_css = ".pg-text-editor > textarea"
|
||||
copy_headers_btn_css = "li[data-label='Copy with headers']"
|
||||
|
||||
paste_button_css = "#id-dataoutput button[data-label='Paste']"
|
||||
|
||||
row_editor_text_area_css = "div[data-label='pg-editor'] textarea"
|
||||
|
||||
json_editor_text_area_css = \
|
||||
"div.ace_layer.ace_text-layer .ace_line_group .ace_line"
|
||||
|
||||
text_editor_ok_btn_css = ".btn.btn-primary.long_text_editor"
|
||||
row_editor_checkbox_css = "div[data-label='pg-checkbox-editor']"
|
||||
|
||||
btn_load_file_css = "#btn-load-file"
|
||||
text_editor_ok_btn_css = \
|
||||
"div[data-label='pg-editor'] button[data-label='OK']"
|
||||
|
||||
btn_execute_query_css = "#btn-flash"
|
||||
btn_load_file_css = "button[data-label='Open File']"
|
||||
|
||||
btn_execute_query_css = "button[data-label='Execute/Refresh']"
|
||||
|
||||
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_canvas_css = "#id-dataoutput .rdg"
|
||||
|
||||
query_output_cells = ".slick-cell"
|
||||
query_output_cells = ".rdg-cell[role='gridcell']"
|
||||
|
||||
sql_editor_message = "//div[contains(@class, 'sql-editor-message') and " \
|
||||
"contains(string(), '{}')]"
|
||||
sql_editor_message = "//div[@id='id-messages'][contains(string(), '{}')]"
|
||||
|
||||
code_mirror_hint_box_xpath = "//ul[@class='CodeMirror-hints default']"
|
||||
|
||||
@ -215,32 +237,19 @@ class QueryToolLocators:
|
||||
|
||||
code_mirror_data_xpath = "//pre[@class=' CodeMirror-line ']/span"
|
||||
|
||||
save_data_icon = "icon-save-data-changes"
|
||||
btn_commit = "button[data-label='Commit']"
|
||||
|
||||
commit_icon = "icon-commit"
|
||||
|
||||
execute_icon = "fa-play"
|
||||
|
||||
explain_icon = "fa-hand-pointer"
|
||||
|
||||
explain_analyze_icon = "fa-list-alt"
|
||||
|
||||
query_history_selected_icon = '#query_list .selected #query_source_icon'
|
||||
|
||||
btn_commit = "#btn-commit"
|
||||
btn_history_remove_all = "#id-history button[data-label='Remove All']"
|
||||
|
||||
show_query_internally_btn = \
|
||||
"//div[label[contains(normalize-space(text())," \
|
||||
"'Show queries generated internally by')]]//" \
|
||||
"div[contains(@class,'toggle btn')]"
|
||||
"//div[contains(normalize-space(text())," \
|
||||
"'Show queries generated internally by')]/span/span[1]"
|
||||
|
||||
editable_column_icon_xpath = "//div[contains(@class," \
|
||||
" 'editable-column-header-icon')]" \
|
||||
"/i[contains(@class, 'fa-pencil-alt')]"
|
||||
editable_column_icon_xpath = \
|
||||
"//div[@role='columnheader']/div/div/*[@data-label='EditIcon']"
|
||||
|
||||
read_only_column_icon_xpath = "//div[contains(@class," \
|
||||
" 'editable-column-header-icon')]" \
|
||||
"/i[contains(@class, 'fa-lock')]"
|
||||
read_only_column_icon_xpath = \
|
||||
"//div[@role='columnheader']/div/div/*[@data-label='LockIcon']"
|
||||
|
||||
|
||||
class ConnectToServerDiv:
|
||||
|
@ -87,9 +87,9 @@ class PgadminPage:
|
||||
|
||||
# In case of react dialog we use different xpath
|
||||
if react_dialog:
|
||||
modal_button = self.find_by_xpath(
|
||||
"//button[contains(@class,'MuiButtonBase-root')]"
|
||||
"//span[text()='%s']" % button_text)
|
||||
modal_button = self.find_by_css_selector(
|
||||
".react-draggable button[data-label='{0}']"
|
||||
.format(button_text))
|
||||
else:
|
||||
modal_button = self.find_by_xpath(
|
||||
"//div[contains(@class, 'alertify') and "
|
||||
@ -203,9 +203,7 @@ class PgadminPage:
|
||||
self.driver.switch_to.frame(
|
||||
self.driver.find_elements(By.TAG_NAME, "iframe")[0])
|
||||
time.sleep(.5)
|
||||
self.click_element(self.find_by_xpath(
|
||||
'//button[contains(@class, "ajs-button") and '
|
||||
'contains(.,"Don\'t save")]'))
|
||||
self.find_by_css_selector("button[data-test='dont-save']").click()
|
||||
|
||||
if self.check_if_element_exist_by_xpath(
|
||||
"//button[text()='Rollback']", 1):
|
||||
@ -214,15 +212,22 @@ class PgadminPage:
|
||||
self.driver.switch_to.default_content()
|
||||
|
||||
def clear_query_tool(self):
|
||||
self.click_element(
|
||||
self.find_by_css_selector(QueryToolLocators.btn_clear_dropdown)
|
||||
)
|
||||
retry = 3
|
||||
edit_options = self.find_by_css_selector(
|
||||
QueryToolLocators.btn_edit_dropdown)
|
||||
while retry > 0:
|
||||
self.click_element(edit_options)
|
||||
time.sleep(0.3)
|
||||
if edit_options.get_attribute("data-state") == "open":
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
|
||||
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.driver.switch_to.default_content()
|
||||
self.click_modal('Yes', True)
|
||||
|
||||
def execute_query(self, query):
|
||||
@ -230,90 +235,76 @@ class PgadminPage:
|
||||
self.click_execute_query_button()
|
||||
|
||||
def click_execute_query_button(self, timeout=20):
|
||||
retry = 5
|
||||
execute_button = self.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css)
|
||||
first_click = execute_button.get_attribute('data-click-counter')
|
||||
while retry > 0:
|
||||
execute_button.click()
|
||||
execute_button = self.find_by_css_selector(
|
||||
QueryToolLocators.btn_execute_query_css)
|
||||
second_click = execute_button.get_attribute(
|
||||
'data-click-counter')
|
||||
if first_click != second_click:
|
||||
self.wait_for_query_tool_loading_indicator_to_appear()
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
execute_button.click()
|
||||
self.wait_for_query_tool_loading_indicator_to_disappear(timeout)
|
||||
|
||||
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"""
|
||||
query_options = self.driver.find_element(
|
||||
By.CSS_SELECTOR, QueryToolLocators.btn_query_dropdown)
|
||||
expanded = query_options.get_attribute("aria-expanded")
|
||||
if expanded == "false":
|
||||
query_options.click()
|
||||
menu_btn = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
if menu_btn.get_attribute('data-state') == "closed":
|
||||
menu_btn.click()
|
||||
|
||||
def update_execute_option_setting(
|
||||
css_selector_of_option_status, css_selector_of_option,):
|
||||
def update_execute_option_setting(css_selector_of_option):
|
||||
retry = 3
|
||||
check_status = self.driver.find_element(
|
||||
By.CSS_SELECTOR, css_selector_of_option_status)
|
||||
if 'visibility-hidden' in check_status.get_attribute('class'):
|
||||
menu_option = self.driver.find_element_by_css_selector(
|
||||
css_selector_of_option)
|
||||
if menu_option.get_attribute('data-checked') == 'false':
|
||||
while retry > 0:
|
||||
self.find_by_css_selector(css_selector_of_option).click()
|
||||
menu_option.click()
|
||||
time.sleep(0.2)
|
||||
if 'visibility-hidden' not in \
|
||||
check_status.get_attribute('class'):
|
||||
if menu_option.get_attribute('data-checked') == 'true':
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
|
||||
if option == 'auto_commit':
|
||||
update_execute_option_setting(
|
||||
QueryToolLocators.btn_auto_commit_check_status,
|
||||
QueryToolLocators.btn_auto_commit)
|
||||
if option == 'auto_rollback':
|
||||
update_execute_option_setting(
|
||||
QueryToolLocators.btn_auto_rollback_check_status,
|
||||
QueryToolLocators.btn_auto_rollback)
|
||||
|
||||
if menu_btn.get_attribute('data-state') == "open":
|
||||
menu_btn.click()
|
||||
|
||||
def uncheck_execute_option(self, option):
|
||||
""""This function will uncheck auto commit or auto roll back based on
|
||||
user input. If button is already unchecked, no action will be taken"""
|
||||
query_options = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocators.btn_query_dropdown)
|
||||
expanded = query_options.get_attribute("aria-expanded")
|
||||
if expanded == "false":
|
||||
query_options.click()
|
||||
menu = self.driver.find_element_by_css_selector(
|
||||
QueryToolLocators.query_tool_menu.format('Execute Options Menu'))
|
||||
|
||||
def update_execute_option_setting(
|
||||
css_selector_of_option_status, css_selector_of_option):
|
||||
if menu.get_attribute('data-state') == "closed":
|
||||
self.driver.find_element_by_css_selector(
|
||||
QueryToolLocators.btn_query_dropdown).click()
|
||||
|
||||
def update_execute_option_setting(css_selector_of_option):
|
||||
retry = 3
|
||||
check_status = self.driver.find_element_by_css_selector(
|
||||
css_selector_of_option_status)
|
||||
if 'visibility-hidden' not in check_status.get_attribute('class'):
|
||||
menu_option = self.driver.find_element_by_css_selector(
|
||||
css_selector_of_option)
|
||||
if menu_option.get_attribute('data-checked') == 'true':
|
||||
while retry > 0:
|
||||
self.find_by_css_selector(
|
||||
css_selector_of_option).click()
|
||||
menu_option.click()
|
||||
time.sleep(0.2)
|
||||
if 'visibility-hidden' in \
|
||||
check_status.get_attribute('class'):
|
||||
if menu_option.get_attribute('data-checked') == 'false':
|
||||
break
|
||||
else:
|
||||
retry -= 1
|
||||
|
||||
if option == 'auto_commit':
|
||||
update_execute_option_setting(
|
||||
QueryToolLocators.btn_auto_commit_check_status,
|
||||
QueryToolLocators.btn_auto_commit)
|
||||
if option == 'auto_rollback':
|
||||
update_execute_option_setting(
|
||||
QueryToolLocators.btn_auto_rollback_check_status,
|
||||
QueryToolLocators.btn_auto_rollback)
|
||||
|
||||
if menu.get_attribute('data-state') == "open":
|
||||
self.driver.find_element_by_css_selector(
|
||||
QueryToolLocators.btn_query_dropdown).click()
|
||||
|
||||
def close_data_grid(self):
|
||||
self.driver.switch_to.default_content()
|
||||
xpath = "//*[@id='dockerContainer']/div/div[3]/div/div[2]/div[1]"
|
||||
@ -331,6 +322,7 @@ class PgadminPage:
|
||||
self.click_element(object_menu_item)
|
||||
delete_menu_item = self.find_by_partial_link_text("Remove Server")
|
||||
self.click_element(delete_menu_item)
|
||||
self.driver.switch_to.default_content()
|
||||
self.click_modal('Yes', True)
|
||||
time.sleep(1)
|
||||
else:
|
||||
@ -1054,15 +1046,15 @@ class PgadminPage:
|
||||
driver.switch_to.default_content()
|
||||
driver.switch_to.frame(
|
||||
driver.find_element_by_tag_name("iframe"))
|
||||
element = driver.find_element_by_css_selector(
|
||||
"#output-panel .CodeMirror")
|
||||
element = driver.find_element(
|
||||
By.CSS_SELECTOR, "#sqleditor-container .CodeMirror")
|
||||
if element.is_displayed() and element.is_enabled():
|
||||
return element
|
||||
except (NoSuchElementException, WebDriverException):
|
||||
return False
|
||||
|
||||
time.sleep(1)
|
||||
self.wait_for_query_tool_loading_indicator_to_disappear(12)
|
||||
# self.wait_for_query_tool_loading_indicator_to_disappear(12)
|
||||
|
||||
retry = 2
|
||||
while retry > 0:
|
||||
@ -1071,7 +1063,9 @@ class PgadminPage:
|
||||
WebDriverWait(self.driver, 10).until(
|
||||
EC.frame_to_be_available_and_switch_to_it(
|
||||
(By.TAG_NAME, "iframe")))
|
||||
self.find_by_xpath("//a[text()='Query Editor']").click()
|
||||
self.find_by_css_selector(
|
||||
"div.dock-tab-btn[id$=\"id-query\"]").click()
|
||||
# self.find_by_xpath("//div[text()='Query Editor']").click()
|
||||
|
||||
codemirror_ele = WebDriverWait(
|
||||
self.driver, timeout=self.timeout, poll_frequency=0.01) \
|
||||
@ -1099,11 +1093,16 @@ class PgadminPage:
|
||||
"arguments[0].CodeMirror.lineCount(),0);",
|
||||
codemirror_ele, field_content)
|
||||
|
||||
def click_tab(self, tab_name):
|
||||
def click_tab(self, tab_name, rc_dock=False):
|
||||
if rc_dock:
|
||||
tab = self.find_by_css_selector(
|
||||
NavMenuLocators.rcdock_tab.format(tab_name))
|
||||
self.click_element(tab)
|
||||
return
|
||||
|
||||
WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable(
|
||||
(By.XPATH, NavMenuLocators.select_tab_xpath.format(tab_name))))
|
||||
click_tab = True
|
||||
while click_tab:
|
||||
while True:
|
||||
tab = self.find_by_xpath(
|
||||
NavMenuLocators.select_tab_xpath.format(tab_name))
|
||||
self.click_element(tab)
|
||||
@ -1188,15 +1187,16 @@ class PgadminPage:
|
||||
|
||||
self._wait_for("spinner to disappear", spinner_has_disappeared, 20)
|
||||
|
||||
def wait_for_query_tool_loading_indicator_to_disappear(self, timeout=20):
|
||||
def wait_for_query_tool_loading_indicator_to_disappear(
|
||||
self, timeout=20, container_id="id-dataoutput"):
|
||||
def spinner_has_disappeared(driver):
|
||||
try:
|
||||
# Refer the status message as spinner appears only on the
|
||||
# the data output panel
|
||||
spinner = driver.find_element(
|
||||
driver.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
".sql-editor .sql-editor-busy-text-status")
|
||||
return "d-none" in spinner.get_attribute("class")
|
||||
"#{0} div[data-label='loader']".format(container_id))
|
||||
return False
|
||||
except NoSuchElementException:
|
||||
# wait for loading indicator disappear animation to complete.
|
||||
time.sleep(0.5)
|
||||
@ -1207,8 +1207,7 @@ class PgadminPage:
|
||||
|
||||
def wait_for_query_tool_loading_indicator_to_appear(self):
|
||||
status = self.check_if_element_exist_by_xpath(
|
||||
"//div[@id='editor-panel']//"
|
||||
"div[@class='pg-sp-container sql-editor-busy-fetching']", 1)
|
||||
"//div[@id='id-dataoutput']//div[@data-label='loader']", 1)
|
||||
return status
|
||||
|
||||
def wait_for_app(self):
|
||||
@ -1340,14 +1339,15 @@ class PgadminPage:
|
||||
|
||||
if required_status == 'Yes':
|
||||
status_changed_successfully = \
|
||||
self.toggle_switch_box(switch_box_element,
|
||||
expected_attr_in_class_tag='success',
|
||||
unexpected_attr_in_class_tag='off')
|
||||
self.toggle_switch_box(
|
||||
switch_box_element,
|
||||
expected_attr_in_class_tag='Mui-checked',
|
||||
unexpected_attr_in_class_tag='')
|
||||
else:
|
||||
status_changed_successfully = \
|
||||
self.toggle_switch_box(switch_box_element,
|
||||
expected_attr_in_class_tag='off',
|
||||
unexpected_attr_in_class_tag='success')
|
||||
self.toggle_switch_box(
|
||||
switch_box_element, expected_attr_in_class_tag='',
|
||||
unexpected_attr_in_class_tag='Mui-checked')
|
||||
return status_changed_successfully
|
||||
|
||||
def toggle_switch_box(self, switch_box_ele, expected_attr_in_class_tag,
|
||||
|
@ -298,7 +298,7 @@ def setup_webdriver_specification(arguments):
|
||||
options.add_argument("--window-size=1790,1080")
|
||||
options.add_argument("--disable-infobars")
|
||||
# options.add_experimental_option('w3c', False)
|
||||
driver_local = webdriver.Chrome(chrome_options=options)
|
||||
driver_local = webdriver.Chrome(options=options)
|
||||
|
||||
# maximize browser window
|
||||
driver_local.maximize_window()
|
||||
|
Loading…
Reference in New Issue
Block a user