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