diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index 1becb0a5c..81bafed98 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -112,6 +112,7 @@ def regression(self, before, after): login.login() self.context.grafana_version = None + Feature(run=load("testflows.tests.automated.window_functions", "feature")) Feature(run=load("testflows.tests.automated.sql_editor", "feature")) Feature(run=load("testflows.tests.automated.data_source_setup", "feature")) Feature(run=load("testflows.tests.automated.e2e", "feature")) diff --git a/tests/testflows/steps/dashboard/view.py b/tests/testflows/steps/dashboard/view.py index 1ae43d3ba..d0a00fae5 100644 --- a/tests/testflows/steps/dashboard/view.py +++ b/tests/testflows/steps/dashboard/view.py @@ -61,18 +61,25 @@ def click_add_visualization_button(self): locators.add_visualization.click() +@TestStep(When) +def scroll_to_panel(self, panel_name): + """Scroll until panel is presented.""" + + driver = self.context.driver + driver.execute_script("arguments[0].scrollIntoView();", locators.panel(panel_name=panel_name)) + + @TestStep(When) def open_dropdown_menu_for_panel(self, panel_name): """Open dropdown menu for panel.""" - with By(f"waiting panel menu for {panel_name} to be loaded"): - wait_panel_menu_button(panel_name=panel_name) - - with And("moving cursor to menu button"): - move_cursor_to_menu_button(panel_name=panel_name) + with By("moving cursor to menu button"): + with delay(): + move_cursor_to_menu_button(panel_name=panel_name) with And("clicking menu button"): - click_menu_button_for_panel(panel_name=panel_name) + with delay(): + click_menu_button_for_panel(panel_name=panel_name) @TestStep(When) @@ -151,8 +158,8 @@ def open_panel(self, panel_name): with When(f"I open dropdown menu for panel {panel_name}"): open_dropdown_menu_for_panel(panel_name=panel_name) - with When("I open panel view"): - edit_panel(panel_name=panel_name) + with When("I open panel view"): + edit_panel(panel_name=panel_name) @TestStep(When) diff --git a/tests/testflows/steps/panel/locators.py b/tests/testflows/steps/panel/locators.py index c1fe67288..ad43ff8ff 100644 --- a/tests/testflows/steps/panel/locators.py +++ b/tests/testflows/steps/panel/locators.py @@ -86,6 +86,26 @@ def query_inspector_refresh_button(self): driver: WebDriver = current().context.driver return driver.find_element(SelectBy.CSS_SELECTOR, "[aria-label='Panel inspector Query refresh button']") + @property + def query_inspector_data_tab(self): + driver: WebDriver = current().context.driver + return driver.find_element(SelectBy.CSS_SELECTOR, "[data-testid='data-testid Tab Data']") + + @property + def query_inspector_data_options_expand_button(self): + driver: WebDriver = current().context.driver + return driver.find_element(SelectBy.XPATH, "//*[@aria-label='Panel inspector Data content']//button[@aria-label='Expand query row']") + + @property + def query_inspector_data_options_dropdown(self): + driver: WebDriver = current().context.driver + return driver.find_element(SelectBy.XPATH, "//*[@aria-label='Panel inspector Data content']//input[@aria-label='Select dataframe']") + + @property + def query_inspector_download_csv_button(self): + driver: WebDriver = current().context.driver + return driver.find_element(SelectBy.XPATH, "//*[text()='Download CSV']/../../button") + @property def query_inspector_url(self): driver: WebDriver = current().context.driver diff --git a/tests/testflows/steps/panel/view.py b/tests/testflows/steps/panel/view.py index 7fdbb15ac..b7d90519b 100644 --- a/tests/testflows/steps/panel/view.py +++ b/tests/testflows/steps/panel/view.py @@ -47,6 +47,32 @@ def click_datasource_in_select_datasource_dropdown(self, datasource_name): locators.select_datasource(datasource_name=datasource_name).click() +@TestStep +def click_data_options_expand_button(self): + """Click data options expand button.""" + + locators.query_inspector_data_options_expand_button.click() + + +@TestStep +def enter_data_options_dropdown(self, row): + """Enter data options dropdown.""" + + locators.query_inspector_data_options_dropdown.click() + locators.query_inspector_data_options_dropdown.send_keys(row) + locators.query_inspector_data_options_dropdown.send_keys(Keys.ENTER) + +@TestStep +def change_row_for_download(self, row): + """Change row for download.""" + + with By("clicking data options expand button"): + with delay(): + click_data_options_expand_button() + + with By("entering row name into data options dropdown"): + with delay(): + enter_data_options_dropdown(row=row) @TestStep(When) def click_sql_editor_toggle(self, query_name): @@ -278,6 +304,21 @@ def click_query_inspector_close_button(self): locators.query_inspector_close_button.click() + +@TestStep(When) +def click_query_inspector_data_tab(self): + """Click query inspector data tab.""" + + locators.query_inspector_data_tab.click() + + +@TestStep(When) +def click_query_inspector_download_csv_button(self): + """Click query inspector download csv button.""" + + locators.query_inspector_download_csv_button.click() + + @TestStep(Then) def check_query_inspector_request(self, url_parts): """Check url in query inspector.""" diff --git a/tests/testflows/steps/ui.py b/tests/testflows/steps/ui.py index 6bcde7d78..c1e0fc0a0 100644 --- a/tests/testflows/steps/ui.py +++ b/tests/testflows/steps/ui.py @@ -60,6 +60,7 @@ def create_local_chrome_driver(self, browser, local_webdriver_path, common_optio "download.directory_upgrade": True, "profile.default_content_settings.popups": 0, "download.default_directory": default_download_directory, + "profile.default_content_setting_values.automatic_downloads": 1 }, ) if is_no_sandbox: @@ -119,7 +120,7 @@ def webdriver( local_webdriver_path=None, is_no_sandbox=False, is_headless=False, - incognito=True, + incognito=False, global_wait_time=1, clean_up=True, suite="grafana" @@ -174,7 +175,7 @@ def webdriver( @TestStep(Given) -def create_driver(self, incognito=True, clean_up=True, suite=None): +def create_driver(self, incognito=False, clean_up=True, suite=None): """Create a driver based on the arguments in the context.""" driver = webdriver( diff --git a/tests/testflows/tests/automated/window_functions.py b/tests/testflows/tests/automated/window_functions.py new file mode 100644 index 000000000..d56f53480 --- /dev/null +++ b/tests/testflows/tests/automated/window_functions.py @@ -0,0 +1,160 @@ +from numpy import corrcoef +from testflows.core import * +from steps.delay import delay +from testflows.asserts import error + +import csv +import steps.ui as ui +import steps.actions as actions +import steps.panel.view as panel +import steps.dashboard.view as dashboard +import steps.dashboards.view as dashboards +import steps.panel.sql_editor.view as sql_editor + +from requirements.requirements import * + + +@TestOutline +def window_functions_outline(self, panel_name, panel_names, column=None): + """Check that grafana plugin supports window functions.""" + + with When(f"I download data for {panel_name} panel{'' if column is None else ', ' + column + ' column'}"): + with When("I scroll down to the panel"): + if panel_names.index(panel_name) != 0: + dashboard.scroll_to_panel(panel_name=panel_names[panel_names.index(panel_name) - 1]) + + with When(f"I open panel {panel_name}"): + with delay(): + dashboard.open_panel(panel_name=panel_name) + + with And("I open Query inspector"): + with delay(after=0.5): + panel.click_inspect_query_button() + + with And("I open Data tab in query inspector modal"): + with delay(after=0.5): + panel.click_query_inspector_data_tab() + + if not (column is None): + with And("I change csv file to download"): + with delay(): + panel.change_row_for_download(row="Series joined by time") + + with And("I download csv data file"): + with delay(): + panel.click_query_inspector_download_csv_button() + + with And("I close query inspector"): + with delay(): + panel.click_query_inspector_close_button() + + with And("I click discard changes"): + with delay(): + panel.click_discard_button() + + with When(f"I download data for {panel_name} - without window functions panel{'' if column is None else ', ' + column + ' column'}"): + with When(f"I open panel {panel_name} - without window functions"): + with delay(): + dashboard.open_panel(panel_name=f"{panel_name} - without window functions") + + with And("I open Query inspector"): + with delay(after=0.5): + panel.click_inspect_query_button() + + with And("I open Data tab in query inspector modal"): + with delay(after=0.5): + panel.click_query_inspector_data_tab() + + if not (column is None): + with And("I change csv file to download"): + with delay(): + panel.change_row_for_download(row="Series joined by time") + + with And("I download csv data file"): + with delay(): + panel.click_query_inspector_download_csv_button() + + with And("I close query inspector"): + with delay(): + panel.click_query_inspector_close_button() + + with And("I click discard changes"): + with delay(): + panel.click_discard_button() + + with Then("I save two csv files"): + with delay(): + with By("saving container id"): + r = self.context.cluster.command(None, "docker ps -a | grep '4444/tcp, 5900/tcp'") + container_id = r.output.split(" ")[0] + column_name = '' if column is None else '/column' + column[4] + + with By("moving csv file from docker"): + r = self.context.cluster.command(None, f"rm -rf tests/automated/window_functions/{panel_name[1:]}{column_name}") + r = self.context.cluster.command(None, f"mkdir -p tests/automated/window_functions/{panel_name[1:]}{column_name}") + r = self.context.cluster.command(None, f"docker cp {container_id}:/home/seluser/Downloads/ tests/automated/window_functions/{panel_name[1:]}{column_name}") + r = self.context.cluster.command(None, f"docker exec {container_id} rm -rf /home/seluser/Downloads/") + + with Then("I compare two csv files"): + with delay(): + with By("defining filenames"): + filename_with_window_functions = self.context.cluster.command(None, f"ls tests/automated/window_functions/{panel_name[1:]}{column_name}/Downloads/*{panel_name[1:]}-*").output[1:-1] + filename_without_window_functions = self.context.cluster.command(None, f"ls tests/automated/window_functions/{panel_name[1:]}{column_name}/Downloads/*{panel_name[1:]}\ *").output[1:-1] + + with By("getting values from files"): + with Step("without window functions"): + file_without_window_functions = open(filename_without_window_functions) + data_without_window_functions = [] + for row in csv.reader(file_without_window_functions): + data_without_window_functions.append(row[1]) + file_without_window_functions.close() + + with Step("with window functions"): + file_with_window_functions = open(filename_with_window_functions) + data_with_window_functions = [] + for row in csv.reader(file_with_window_functions): + data_with_window_functions.append(row[1]) + file_with_window_functions.close() + + with By("calculating correlation between this values"): + data_without_window_functions = [0 if i == '' else float(i) for i in data_without_window_functions[1:]] + data_with_window_functions = [0 if i == '' else float(i) for i in data_with_window_functions[1:]] + correlation = corrcoef(data_without_window_functions[1:], data_with_window_functions[1:])[0,1] + note(f"correlation for {panel_name}: {correlation}") + note(data_without_window_functions) + note(data_with_window_functions) + assert correlation > 0.99, error() + +@TestFeature +@Name("window functions") +def feature(self): + """Check that grafana plugin supports window functions.""" + + panel_names = [ + '$delta', + '$deltaColumns', + '$deltaColumnsAggregated', + '$increase', + '$increaseColumns', + '$increaseColumnsAggregated', + '$perSecond', + '$perSecondColumns', + '$perSecondColumnsAggregated', + '$rate', + '$rateColumns', + '$rateColumnsAggregated', + ] + + with Given(f"I open dashboard window functions"): + ui.open_endpoint(endpoint='http://grafana:3000/d/de6482iletr0gc/window-functions') + + for panel_name in panel_names: + if "Columns" in panel_name: + with Scenario(f"{panel_name} function first row"): + window_functions_outline(panel_name=panel_name, panel_names=panel_names, column="test1 (0)") + + with Scenario(f"{panel_name} function second row"): + window_functions_outline(panel_name=panel_name, panel_names=panel_names, column="test2 (1)") + else: + with Scenario(f"{panel_name} function"): + window_functions_outline(panel_name=panel_name, panel_names=panel_names)