From 09306b4beec27d09b1c0783de6a92c788bcc0efa Mon Sep 17 00:00:00 2001 From: Ann Date: Thu, 18 Nov 2021 19:18:57 +0300 Subject: [PATCH 01/15] ADCM-2357 add check by api to configs --- tests/ui_tests/app/configuration.py | 29 ++++++++++++++++++- tests/ui_tests/test_configs.py | 10 ++++--- .../test_save_configuration_button.py | 22 ++++++++++---- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/tests/ui_tests/app/configuration.py b/tests/ui_tests/app/configuration.py index 3eaa32b8b2..e60d0eb6ed 100644 --- a/tests/ui_tests/app/configuration.py +++ b/tests/ui_tests/app/configuration.py @@ -15,6 +15,7 @@ import json import allure +import requests from adcm_client.objects import Service from selenium.common.exceptions import NoSuchElementException, TimeoutException from selenium.webdriver.common.action_chains import ActionChains @@ -25,6 +26,18 @@ from tests.ui_tests.app.pages import BasePage +def _make_request(adcm_credentials, app_fs, method: str, path: str, **kwargs) -> requests.Response: + def check_response(response): + assert response.status_code == 200 + + token = requests.post(f'{app_fs.adcm.url}/api/v1/token/', json=adcm_credentials) + check_response(token) + header = {'Authorization': f'Token {token.json()["token"]}'} + response = requests.request(method=method, url=app_fs.adcm.url + path, headers=header, **kwargs) + check_response(response) + return response + + class Configuration(BasePage): # pylint: disable=too-many-public-methods """Class for configuration page""" @@ -55,16 +68,29 @@ def assert_field_is_editable(self, field_element, editable=True) -> None: self.is_element_editable(field_element) == editable ), f"Field {field_element} should {'be editable' if editable else 'not be editable'}" + def get_api_value(self, adcm_credentials, app_fs, field: str): + current_config = _make_request( + adcm_credentials, app_fs, "GET", f"/api/v1/{self.driver.current_url.strip(app_fs.adcm.url)}/current" + ).text + current_config_dict = json.loads(current_config)['config'] + try: + return current_config_dict[field] + except KeyError: + raise AssertionError(f"No parameter {field} found by api") + @allure.step('Assert field: {field} to have value: {expected_value}') - def assert_field_content_equal(self, field_type, field, expected_value): + def assert_field_content_equal(self, adcm_credentials, app_fs, field_type, field, expected_value): """Assert field value based on field type and name""" current_value = self.get_field_value_by_type(field, field_type) + current_api_value = self.get_api_value(adcm_credentials, app_fs, field_type) if field_type in ('password', 'secrettext'): # In case of password we have no raw password in API after writing. if expected_value is not None and expected_value != "": assert current_value is not None, "Password field expected to be filled" + assert current_api_value is not None, "Password field expected to be filled" else: assert current_value is None or current_value == "", "Password have to be empty" + assert current_api_value is None or current_api_value == "", "Password have to be empty" else: if field_type == 'file': expected_value = 'test' @@ -74,6 +100,7 @@ def assert_field_content_equal(self, field_type, field, expected_value): assert set(map_config.values()) == set(map_config.values()) else: assert current_value == expected_value, f"Field value with type {field_type} doesn't equal expected" + assert current_api_value == expected_value, f"Field value with type {field_type} doesn't equal expected" @allure.step('Assert frontend errors presence and text') def assert_alerts_presented(self, field_type): diff --git a/tests/ui_tests/test_configs.py b/tests/ui_tests/test_configs.py index 9aecb23f7b..00b28cd5db 100644 --- a/tests/ui_tests/test_configs.py +++ b/tests/ui_tests/test_configs.py @@ -332,7 +332,7 @@ def _prepare_group_config(config): @pytest.mark.parametrize("config_dict", generate_configs()) @pytest.mark.usefixtures("login_to_adcm_over_api") -def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs): +def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_credentials): """Test UI configuration page without groups. Before start test actions we always create configuration and expected result. All logic for test expected result in functions before this test function. @@ -372,7 +372,9 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs): for field in fields: ui_config.assert_field_is_editable(field, expected['editable']) if expected['content']: - ui_config.assert_field_content_equal(field_type, fields[0], config['config'][0]['default']) + ui_config.assert_field_content_equal( + adcm_credentials, app_fs, field_type, fields[0], config['config'][0]['default'] + ) if expected['alerts']: ui_config.assert_alerts_presented(field_type) else: @@ -381,7 +383,7 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs): @pytest.mark.parametrize(("config_dict", "expected"), generate_group_configs()) @pytest.mark.usefixtures("login_to_adcm_over_api") -def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, app_fs): +def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, app_fs, adcm_credentials): """Test for configuration fields with groups. Before start test actions we always create configuration and expected result. All logic for test expected result in functions before this test function. If we have @@ -441,7 +443,7 @@ def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, a ui_config.assert_field_is_editable(field, expected['editable']) if expected['content']: default_value = config['config'][0]['subs'][0]['default'] - ui_config.assert_field_content_equal(field_type, fields[0], default_value) + ui_config.assert_field_content_equal(adcm_credentials, app_fs, field_type, fields[0], default_value) if expected['alerts']: ui_config.assert_alerts_presented(field_type) if "activatable" in config['config'][0].keys(): diff --git a/tests/ui_tests/test_save_configuration_button.py b/tests/ui_tests/test_save_configuration_button.py index eead7399e3..f3b3ec8c78 100644 --- a/tests/ui_tests/test_save_configuration_button.py +++ b/tests/ui_tests/test_save_configuration_button.py @@ -228,7 +228,9 @@ def _update_config_property(config_page: Configuration, field, field_type: str): assert config_page.save_button_status() -def _test_save_configuration_button(config_page: Configuration, prop_types: list, group_name=None, use_advanced=False): +def _test_save_configuration_button( + adcm_credentials, app_fs, config_page: Configuration, prop_types: list, group_name=None, use_advanced=False +): if use_advanced: config_page.click_advanced() if group_name: @@ -259,7 +261,7 @@ def _test_save_configuration_button(config_page: Configuration, prop_types: list field_type = "string" field = config_page.get_form_field(field) value_to_check = _get_test_value(field_type) - config_page.assert_field_content_equal(field_type, field, value_to_check) + config_page.assert_field_content_equal(adcm_credentials, app_fs, field_type, field, value_to_check) def _get_default_props_list() -> list: @@ -277,10 +279,12 @@ def _get_default_props_list() -> list: entity_type="cluster", prop_types=_get_default_props_list(), ) -def test_cluster_configuration_save_button(bundle_content, cluster_config_page): +def test_cluster_configuration_save_button(adcm_credentials, app_fs, bundle_content, cluster_config_page): """Test cluster configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( + adcm_credentials, + app_fs, cluster_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, @@ -293,10 +297,12 @@ def test_cluster_configuration_save_button(bundle_content, cluster_config_page): entity_type="service", prop_types=_get_default_props_list(), ) -def test_service_configuration_save_button(bundle_content, service_config_page): +def test_service_configuration_save_button(adcm_credentials, app_fs, bundle_content, service_config_page): """Test service configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( + adcm_credentials, + app_fs, service_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, @@ -309,10 +315,12 @@ def test_service_configuration_save_button(bundle_content, service_config_page): entity_type="provider", prop_types=_get_default_props_list(), ) -def test_provider_configuration_save_button(bundle_content, provider_config_page): +def test_provider_configuration_save_button(adcm_credentials, app_fs, bundle_content, provider_config_page): """Test provider configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( + adcm_credentials, + app_fs, provider_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, @@ -325,10 +333,12 @@ def test_provider_configuration_save_button(bundle_content, provider_config_page entity_type="host", prop_types=_get_default_props_list(), ) -def test_host_configuration_save_button(bundle_content, host_config_page): +def test_host_configuration_save_button(adcm_credentials, app_fs, bundle_content, host_config_page): """Test host configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( + adcm_credentials, + app_fs, host_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, From 7babcd9a990d36a4735e5a1d58e5ccb3bbe2bb53 Mon Sep 17 00:00:00 2001 From: Ann Date: Thu, 18 Nov 2021 21:12:11 +0300 Subject: [PATCH 02/15] ADCM-2357 fix remarks --- tests/ui_tests/app/configuration.py | 19 ++++++++++++++++--- .../test_save_configuration_button.py | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/ui_tests/app/configuration.py b/tests/ui_tests/app/configuration.py index e60d0eb6ed..2f83ffc49d 100644 --- a/tests/ui_tests/app/configuration.py +++ b/tests/ui_tests/app/configuration.py @@ -69,20 +69,33 @@ def assert_field_is_editable(self, field_element, editable=True) -> None: ), f"Field {field_element} should {'be editable' if editable else 'not be editable'}" def get_api_value(self, adcm_credentials, app_fs, field: str): + """Gets field value by api""" + current_config = _make_request( - adcm_credentials, app_fs, "GET", f"/api/v1/{self.driver.current_url.strip(app_fs.adcm.url)}/current" + adcm_credentials, app_fs, "GET", f"/api/v1/{self.driver.current_url.replace(app_fs.adcm.url, '')}/current" ).text current_config_dict = json.loads(current_config)['config'] try: - return current_config_dict[field] + if 'group' in current_config_dict: + if field in current_config_dict['group']: + return current_config_dict['group'][field] + else: + return current_config_dict['group']['structure_property'][field] + else: + if field in current_config_dict: + return current_config_dict[field] + else: + return current_config_dict['structure_property'][field] except KeyError: raise AssertionError(f"No parameter {field} found by api") + # pylint:disable=too-many-arguments @allure.step('Assert field: {field} to have value: {expected_value}') def assert_field_content_equal(self, adcm_credentials, app_fs, field_type, field, expected_value): """Assert field value based on field type and name""" + current_value = self.get_field_value_by_type(field, field_type) - current_api_value = self.get_api_value(adcm_credentials, app_fs, field_type) + current_api_value = self.get_api_value(adcm_credentials, app_fs, field.text.split(":")[0]) if field_type in ('password', 'secrettext'): # In case of password we have no raw password in API after writing. if expected_value is not None and expected_value != "": diff --git a/tests/ui_tests/test_save_configuration_button.py b/tests/ui_tests/test_save_configuration_button.py index f3b3ec8c78..2fcfff221a 100644 --- a/tests/ui_tests/test_save_configuration_button.py +++ b/tests/ui_tests/test_save_configuration_button.py @@ -12,7 +12,7 @@ """UI tests for save configuration button""" -# pylint: disable=redefined-outer-name +# pylint: disable=redefined-outer-name,too-many-arguments import itertools import os import shutil From f14cfcf65588988c3b12eef4e789df32a649a01e Mon Sep 17 00:00:00 2001 From: Ann Date: Thu, 18 Nov 2021 21:38:04 +0300 Subject: [PATCH 03/15] ADCM-2357 fix remarks --- tests/ui_tests/app/configuration.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/ui_tests/app/configuration.py b/tests/ui_tests/app/configuration.py index 2f83ffc49d..fcac2dcbd7 100644 --- a/tests/ui_tests/app/configuration.py +++ b/tests/ui_tests/app/configuration.py @@ -28,7 +28,7 @@ def _make_request(adcm_credentials, app_fs, method: str, path: str, **kwargs) -> requests.Response: def check_response(response): - assert response.status_code == 200 + assert response.status_code == 200, f"Response status is {response.status_code}" token = requests.post(f'{app_fs.adcm.url}/api/v1/token/', json=adcm_credentials) check_response(token) @@ -79,15 +79,13 @@ def get_api_value(self, adcm_credentials, app_fs, field: str): if 'group' in current_config_dict: if field in current_config_dict['group']: return current_config_dict['group'][field] - else: - return current_config_dict['group']['structure_property'][field] + return current_config_dict['group']['structure_property'][field] else: if field in current_config_dict: return current_config_dict[field] - else: - return current_config_dict['structure_property'][field] - except KeyError: - raise AssertionError(f"No parameter {field} found by api") + return current_config_dict['structure_property'][field] + except KeyError as error: + raise AssertionError(f"No parameter {field} found by api") from error # pylint:disable=too-many-arguments @allure.step('Assert field: {field} to have value: {expected_value}') From 5d5b3dfc09a0809c12894c8bafbc7ead4293f969 Mon Sep 17 00:00:00 2001 From: Ann Date: Thu, 18 Nov 2021 22:02:23 +0300 Subject: [PATCH 04/15] ADCM-2357 fix remarks --- tests/ui_tests/app/configuration.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui_tests/app/configuration.py b/tests/ui_tests/app/configuration.py index fcac2dcbd7..b7b73f0c91 100644 --- a/tests/ui_tests/app/configuration.py +++ b/tests/ui_tests/app/configuration.py @@ -80,10 +80,10 @@ def get_api_value(self, adcm_credentials, app_fs, field: str): if field in current_config_dict['group']: return current_config_dict['group'][field] return current_config_dict['group']['structure_property'][field] - else: - if field in current_config_dict: - return current_config_dict[field] - return current_config_dict['structure_property'][field] + + if field in current_config_dict: + return current_config_dict[field] + return current_config_dict['structure_property'][field] except KeyError as error: raise AssertionError(f"No parameter {field} found by api") from error From f57bc5e9218a100c7ca22e62f824193cec44c407 Mon Sep 17 00:00:00 2001 From: Ann Date: Fri, 19 Nov 2021 11:26:43 +0300 Subject: [PATCH 05/15] ADCM-2357 fix remarks --- tests/ui_tests/app/locators.py | 2 ++ tests/ui_tests/app/pages.py | 11 +++++++++++ tests/ui_tests/test_configs.py | 7 ++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/ui_tests/app/locators.py b/tests/ui_tests/app/locators.py index 02eb719a12..dae175b496 100644 --- a/tests/ui_tests/app/locators.py +++ b/tests/ui_tests/app/locators.py @@ -17,6 +17,7 @@ from tests.ui_tests.app.helpers import bys + # pylint: disable=too-few-public-methods @@ -86,6 +87,7 @@ class Common: # Common elements all_childs = bys.by_css("*") + common_popup = bys.by_css("simple-snack-bar") class Cluster: diff --git a/tests/ui_tests/app/pages.py b/tests/ui_tests/app/pages.py index c0cb055e2e..cedd082aa2 100644 --- a/tests/ui_tests/app/pages.py +++ b/tests/ui_tests/app/pages.py @@ -238,6 +238,17 @@ def _menu_click(self, locator: tuple): raise InvalidElementStateException return self._click_element(locator) + def is_popup_presented_on_page(self, timeout: int = 5): + """Get popup displayed status""" + try: + return self._getelement(Common.common_popup, timer=timeout).is_displayed() + except TimeoutException: + return False + + def assert_no_popups_displayed(self, timeout: int = 2): + """Assert there is no popups displayed""" + assert not self.is_popup_presented_on_page(timeout=timeout), "There is a popup with error on the page" + class Ui(BasePage): """This class describes main menu and returns specified page in POM""" diff --git a/tests/ui_tests/test_configs.py b/tests/ui_tests/test_configs.py index 00b28cd5db..e92d02e181 100644 --- a/tests/ui_tests/test_configs.py +++ b/tests/ui_tests/test_configs.py @@ -360,7 +360,12 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_cre fields = ui_config.get_app_fields() with allure.step('Check save button status'): save_err_mess = f"Correct status for save button {[expected['save']]}" - assert expected['save'] == ui_config.save_button_status(), save_err_mess + button_state = ui_config.save_button_status() + assert expected['save'] == button_state, save_err_mess + if expected['save']: + ui_config.save_configuration() + ui_config.save_configuration() + ui_config.assert_no_popups_displayed() with allure.step('Check field configuration'): if expected['visible']: if expected['visible_advanced']: From 1af8fac2aa5a4cc55af2f55d3806e3b62c608a30 Mon Sep 17 00:00:00 2001 From: Ann Date: Fri, 19 Nov 2021 11:29:01 +0300 Subject: [PATCH 06/15] ADCM-2357 fix remarks --- tests/ui_tests/test_configs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ui_tests/test_configs.py b/tests/ui_tests/test_configs.py index e92d02e181..88305a13fb 100644 --- a/tests/ui_tests/test_configs.py +++ b/tests/ui_tests/test_configs.py @@ -363,7 +363,6 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_cre button_state = ui_config.save_button_status() assert expected['save'] == button_state, save_err_mess if expected['save']: - ui_config.save_configuration() ui_config.save_configuration() ui_config.assert_no_popups_displayed() with allure.step('Check field configuration'): @@ -420,6 +419,9 @@ def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, a with allure.step('Check save button status'): save_err_mess = f"Correct status for save button {[expected['save']]}" assert expected['save'] == ui_config.save_button_status(), save_err_mess + if expected['save']: + ui_config.save_configuration() + ui_config.assert_no_popups_displayed() with allure.step('Check configuration'): if expected['group_visible'] and not expected['field_visible']: if expected['group_visible_advanced']: From 42d7d41b575c3a8f2d24b99514c4d6c43e837d00 Mon Sep 17 00:00:00 2001 From: Sealwing Date: Tue, 16 Nov 2021 17:55:02 +0500 Subject: [PATCH 07/15] Click on "Save" button added to new "config hell" test --- tests/ui_tests/app/page/common/base_page.py | 27 +- .../app/page/common/configuration/fields.py | 91 + .../app/page/common/configuration/locators.py | 8 + .../app/page/common/configuration/page.py | 32 +- tests/ui_tests/test_ui_config_hell.py | 85 +- .../config_new_hell_service/config.yaml | 2459 +++++++++++++++++ 6 files changed, 2678 insertions(+), 24 deletions(-) create mode 100644 tests/ui_tests/app/page/common/configuration/fields.py create mode 100644 tests/ui_tests/test_ui_config_hell_data/cluster_bundle/config_new_hell_service/config.yaml diff --git a/tests/ui_tests/app/page/common/base_page.py b/tests/ui_tests/app/page/common/base_page.py index 47d6d5b034..ca0b90e726 100644 --- a/tests/ui_tests/app/page/common/base_page.py +++ b/tests/ui_tests/app/page/common/base_page.py @@ -113,6 +113,10 @@ def close_info_popup(self): self.find_and_click(CommonPopupLocators.hide_btn) self.wait_element_hide(CommonPopupLocators.block) + def is_popup_presented_on_page(self) -> bool: + """Check if popup is presented on page""" + return self.is_element_displayed(CommonPopupLocators.block, timeout=5) + def get_info_popup_text(self): """Get text from info popup""" self.wait_element_visible(CommonPopupLocators.block) @@ -153,10 +157,13 @@ def find_children(self, element: WebElement, child: Locator, timeout: int = None loc_timeout = timeout or self.default_loc_timeout with allure.step(f'Find element "{child.name}" on page'): - return WDW(element, loc_timeout).until( - EC.presence_of_all_elements_located([child.by, child.value]), - message=f"Can't find {child.name} on page " f"{self.driver.current_url} for {loc_timeout} seconds", - ) + try: + return WDW(element, loc_timeout).until( + EC.presence_of_all_elements_located([child.by, child.value]), + message=f"Can't find {child.name} on page " f"{self.driver.current_url} for {loc_timeout} seconds", + ) + except TimeoutException: + return [] def find_elements(self, locator: Locator, timeout: int = None) -> [WebElement]: """Find elements on current page.""" @@ -371,6 +378,18 @@ def click_back_button_in_browser(self): """Click back button in browser""" self.driver.back() + @allure.step('Scroll to element') + def scroll_to(self, locator: Optional[Locator] = None, element: Optional[WebElement] = None) -> WebElement: + """Scroll to element""" + element = element or self.find_element(locator) + # Hack for firefox because of move_to_element does not scroll to the element + # https://github.com/mozilla/geckodriver/issues/776 + if self.driver.capabilities['browserName'] == 'firefox': + self.driver.execute_script('arguments[0].scrollIntoView(true)', element) + action = ActionChains(self.driver) + action.move_to_element(element).perform() + return element + class PageHeader(BasePageObject): """Class for header manipulating.""" diff --git a/tests/ui_tests/app/page/common/configuration/fields.py b/tests/ui_tests/app/page/common/configuration/fields.py new file mode 100644 index 0000000000..5a9284653d --- /dev/null +++ b/tests/ui_tests/app/page/common/configuration/fields.py @@ -0,0 +1,91 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Manipulations with different type of configuration parameters +""" +from contextlib import contextmanager +from typing import Dict + +from selenium.webdriver.remote.webdriver import WebElement +from adcm_pytest_plugin.utils import wait_until_step_succeeds + +from tests.ui_tests.app.page.common.base_page import BasePageObject +from tests.ui_tests.app.page.common.configuration.locators import CommonConfigMenu + + +class ConfigFieldsManipulator(BasePageObject): + """ + Class for handling different types of inputs on configuration page. + """ + + def fill_password(self, password: str, row: WebElement, *, confirmation: str = None): + """ + Fill password and confirm password field with same value if confirmation is not provided explicitly + """ + password_input = self.find_child(row, CommonConfigMenu.ConfigRow.password) + password_input.send_keys(password) + if confirmation is not None: + confirm_input = self.find_child(row, CommonConfigMenu.ConfigRow.confirm_password) + confirm_input.send_keys(password) + + def add_list_values(self, values: list, row: WebElement): + """Add list values to config parameter in row""" + add_button = self.find_child(row, CommonConfigMenu.ConfigRow.add_item_btn) + for value in values: + with self._with_items_added(row): + self.scroll_to(element=add_button) + add_button.click() + item_to_fill = self._get_first_empty_input(row) + item_to_fill.send_keys(value) + + def add_map_values(self, values: Dict[str, str], row: WebElement): + """Add map values to config parameter in row""" + add_button = self.find_child(row, CommonConfigMenu.ConfigRow.add_item_btn) + for key, value in values.items(): + with self._with_items_added(row): + self.scroll_to(element=add_button) + add_button.click() + item_to_fill = self._get_first_empty_map_input(row) + key_input = self.find_child(item_to_fill, CommonConfigMenu.ConfigRow.map_input_key) + value_input = self.find_child(item_to_fill, CommonConfigMenu.ConfigRow.map_input_value) + key_input.send_keys(key) + value_input.send_keys(value) + + def _get_first_empty_input(self, row: WebElement) -> WebElement: + """Get first empty field (simple search for inputs/textareas in row)""" + for item in self.find_children(row, CommonConfigMenu.ConfigRow.value, timeout=2): + if not item.get_attribute('value'): + return item + raise ValueError('All items in row has "value" or no items are presented') + + def _get_first_empty_map_input(self, row: WebElement) -> WebElement: + """Search for first empty (by key) input element for map and return 'parent' map WebElement""" + for item in self.find_children(row, CommonConfigMenu.ConfigRow.map_item, timeout=2): + key_input = self.find_child(item, CommonConfigMenu.ConfigRow.map_input_key) + if not key_input.get_attribute('value'): + return item + raise ValueError('All items in map has "value" in key input field or no items are presented') + + @contextmanager + def _with_items_added(self, row: WebElement): + """Wait for new items to appear after 'add new' button is clicked""" + before = len(self.find_children(row, CommonConfigMenu.ConfigRow.value, timeout=2)) + + yield + + def check_new_item_appeared(): + assert ( + len(self.find_children(row, CommonConfigMenu.ConfigRow.value, timeout=1)) > before + ), f'New item should appear in {row}, but there are still {before} rows' + + wait_until_step_succeeds(check_new_item_appeared, timeout=10, period=1) diff --git a/tests/ui_tests/app/page/common/configuration/locators.py b/tests/ui_tests/app/page/common/configuration/locators.py index 9a51201cb7..be96626b87 100644 --- a/tests/ui_tests/app/page/common/configuration/locators.py +++ b/tests/ui_tests/app/page/common/configuration/locators.py @@ -70,6 +70,14 @@ class ConfigRow: history = Locator(By.CSS_SELECTOR, "mat-list-item span.accent", "Row history") reset_btn = Locator(By.CSS_SELECTOR, "button[mattooltip='Reset to default']", "Reset button") + # complex parameters + add_item_btn = Locator( + By.XPATH, ".//button//mat-icon[text()='add_circle_outline']", "Add item to parameter button" + ) + map_item = Locator(By.CSS_SELECTOR, "div.item", "Map parameter item") + map_input_key = Locator(By.XPATH, ".//input[@formcontrolname='key']", "Map input key input") + map_input_value = Locator(By.XPATH, ".//input[@formcontrolname='value']", "Map input value input") + class ConfigGroup: """Configuration menu configuration group locators""" diff --git a/tests/ui_tests/app/page/common/configuration/page.py b/tests/ui_tests/app/page/common/configuration/page.py index 0fd1d6d669..a642aec3ff 100644 --- a/tests/ui_tests/app/page/common/configuration/page.py +++ b/tests/ui_tests/app/page/common/configuration/page.py @@ -24,6 +24,7 @@ from tests.ui_tests.app.page.common.base_page import BasePageObject from tests.ui_tests.app.page.common.configuration.locators import CommonConfigMenu +from tests.ui_tests.app.page.common.configuration.fields import ConfigFieldsManipulator @dataclass @@ -40,27 +41,30 @@ class CommonConfigMenuObj(BasePageObject): def __init__(self, driver, base_url, config_class_locators=CommonConfigMenu): super().__init__(driver, base_url) self.locators = config_class_locators + self.fields = ConfigFieldsManipulator(self.driver, self.base_url) - def get_all_config_rows(self) -> List[WebElement]: + def get_all_config_rows(self, *, displayed_only: bool = True) -> List[WebElement]: """Return all config field rows""" try: - return [r for r in self.find_elements(CommonConfigMenu.config_row, timeout=5) if r.is_displayed()] + if displayed_only: + return [r for r in self.find_elements(CommonConfigMenu.config_row, timeout=5) if r.is_displayed()] + return self.find_elements(CommonConfigMenu.config_row, timeout=5) except TimeoutException: return [] def get_config_row(self, display_name: str) -> WebElement: """Return config field row with provided display name""" - row_name = f'{display_name}:' + row_name = f'{display_name}:' if not display_name.endswith(':') else display_name for row in self.get_all_config_rows(): if self.find_child(row, CommonConfigMenu.ConfigRow.name).text == row_name: return row raise AssertionError(f'Configuration field with name {display_name} was not found') @allure.step('Saving configuration') - def save_config(self): + def save_config(self, load_timeout: int = 2): """Save current configuration""" self.find_and_click(self.locators.save_btn) - self.wait_element_hide(self.locators.loading_text, timeout=2) + self.wait_element_hide(self.locators.loading_text, timeout=load_timeout) @allure.step('Setting configuration description to {description}') def set_description(self, description: str) -> str: @@ -87,6 +91,11 @@ def compare_versions(self, compare_with: str, base_compare_version: Optional[str # to hide select panel so it won't block other actions self.find_element(self.locators.compare_to_select).send_keys(Keys.ESCAPE) + def click_on_advanced(self): + """Click on advanced button and wait rows changed""" + with self.wait_rows_change(): + self.find_and_click(CommonConfigMenu.advanced_label) + def get_input_value( self, row: WebElement, @@ -175,7 +184,7 @@ def _click_group(): group.click() assert ( _is_group_expanded(self.find_element(self.locators.group_btn(title))) != is_expanded - ), f"Group should be{'' if _is_group_expanded else ' not '}expanded" + ), f"Group should be{'' if is_expanded else ' not '}expanded" wait_until_step_succeeds(_click_group, period=1, timeout=10) @@ -274,3 +283,14 @@ def _assert_value(): assert self.get_history_in_row(row)[0] == value, "History row should contain old value" wait_until_step_succeeds(_assert_value, timeout=4, period=0.5) + + @allure.step('Scroll to group "{display_name}"') + def scroll_to_group(self, display_name: str) -> WebElement: + """Scroll to parameter group by display name""" + return self.scroll_to(CommonConfigMenu.group_btn(display_name)) + + @allure.step('Scroll to field "{display_name}"') + def scroll_to_field(self, display_name: str) -> WebElement: + """Scroll to parameter field by display name""" + row = self.get_config_row(display_name) + return self.scroll_to(element=row) diff --git a/tests/ui_tests/test_ui_config_hell.py b/tests/ui_tests/test_ui_config_hell.py index cd19b49dfc..3ae0ba0685 100644 --- a/tests/ui_tests/test_ui_config_hell.py +++ b/tests/ui_tests/test_ui_config_hell.py @@ -14,54 +14,111 @@ # pylint: disable=redefined-outer-name import os +from typing import Set, Tuple import allure import pytest +from adcm_client.objects import Service from adcm_pytest_plugin.utils import get_data_dir +from tests.ui_tests.app.app import ADCMTest +from tests.ui_tests.app.page.service.page import ServiceConfigPage from tests.ui_tests.app.configuration import Configuration DATADIR = get_data_dir(__file__) BUNDLES = os.path.join(os.path.dirname(__file__), "../stack/") +SERVICE_NAME = 'new_ui_config_hell' +SERVICE_DISPLAY_NAME = 'New UI Config Hell' + @pytest.fixture() @allure.step('Upload bundle, create cluster and add service') -def ui_hell_fs(sdk_client_fs): +def ui_hell_fs(sdk_client_fs) -> Service: """Upload bundle, create cluster and add service""" bundle = sdk_client_fs.upload_from_fs(DATADIR) cluster = bundle.cluster_create(name='my cluster') - cluster.service_add(name='ui_config_hell') - service = cluster.service(name="ui_config_hell") - return service + return cluster.service_add(name=SERVICE_NAME) @pytest.fixture() @allure.title("Prepare prototype display names") -def prototype_display_names(ui_hell_fs): +def prototype_display_names(ui_hell_fs: Service) -> Tuple[str, str]: """Prepare prototype display names""" display_header_name = ui_hell_fs.display_name display_names = {config['display_name'] for config in ui_hell_fs.prototype().config} return display_header_name, display_names +@pytest.fixture() +@allure.title('Open service configuration page') +def config_page( + app_fs: ADCMTest, ui_hell_fs: Service, login_to_adcm_over_api # pylint: disable=unused-argument +) -> ServiceConfigPage: + """Get config page from service""" + return ServiceConfigPage(app_fs.driver, app_fs.adcm.url, ui_hell_fs.cluster().id, ui_hell_fs.id).open() + + @pytest.fixture() @allure.title('Get display names from service config page') -def ui_display_names(app_fs, ui_hell_fs, login_to_adcm_over_api): # pylint: disable=unused-argument +def ui_display_names( + app_fs: ADCMTest, ui_hell_fs, login_to_adcm_over_api # pylint: disable=unused-argument +) -> Set[str]: """Get display names from service config page""" - ui_config = Configuration.from_service(app_fs, ui_hell_fs) - return ui_config.get_display_names() + return Configuration.from_service(app_fs, ui_hell_fs).get_display_names() -def test_display_names(prototype_display_names, ui_display_names): - """Scenario: +def test_save_configuration_hell( + config_page: ServiceConfigPage, + prototype_display_names: Tuple[str, str], + ui_display_names: Set[str], +): + """ + Scenario: 1. Get Service configuration 2. Get display names from UI 3. Check that config name in prototype is correct 4. Check that in UI we have full list of display names from prototype + 5. Fill required fields and try to save """ + service_display_name_from_prototype, parameters_display_names = prototype_display_names with allure.step('Check that config name in prototype is correct'): - assert prototype_display_names[0] == "UI Config Hell" - with allure.step('Check that in UI we have full list of display names from prototype'): - for d_name in ui_display_names: - assert d_name in prototype_display_names[1] + assert service_display_name_from_prototype == SERVICE_DISPLAY_NAME + with allure.step('Check that in UI we have full list of group display names from prototype'): + group_names = filter( + lambda name: 'group' in name + and ('invisible' not in name or 'not invisible' in name) + and ('advanced' not in name or 'not advanced' in name), + parameters_display_names, + ) + for display_name in group_names: + assert display_name in ui_display_names, f"Group named '{display_name}' should be presented in config" + _fill_required_fields(config_page) + config_page.config.save_config(load_timeout=15) + with allure.step('Ensure page is still opened'): + config_page.wait_page_is_opened(timeout=1) + with allure.step('Check that popup does not contain info about error'): + assert not config_page.is_popup_presented_on_page(), 'No popup should be shown after save' + + +@allure.step('Fill required fields') +def _fill_required_fields(config_page: ServiceConfigPage): + simple_fill_method = config_page.config.type_in_config_field + + required_fields = { + 'integer not default required:': ('2', simple_fill_method), + 'float not default required:': ('2.2', simple_fill_method), + 'string not default required:': ('Ein neuer Tag beginnt', simple_fill_method), + 'password not default required no confirm:': ('strongestpasswordever', config_page.config.fields.fill_password), + 'text not default required:': ('This is\nthe day', simple_fill_method), + 'file not default required:': ('My only\nfriend', simple_fill_method), + 'json not default required:': ('{"Where": "the long shadow falls"}', simple_fill_method), + 'list not default required:': (['Silencer'], config_page.config.fields.add_list_values), + 'map not default required:': ({'Poccolus': 'Ragana'}, config_page.config.fields.add_map_values), + } + + for param_display_name, value in required_fields.items(): + value_to_fill, fill_method = value + row = config_page.config.get_config_row(param_display_name) + config_page.scroll_to(element=row) + fill_method(value_to_fill, row) diff --git a/tests/ui_tests/test_ui_config_hell_data/cluster_bundle/config_new_hell_service/config.yaml b/tests/ui_tests/test_ui_config_hell_data/cluster_bundle/config_new_hell_service/config.yaml new file mode 100644 index 0000000000..0649478a5b --- /dev/null +++ b/tests/ui_tests/test_ui_config_hell_data/cluster_bundle/config_new_hell_service/config.yaml @@ -0,0 +1,2459 @@ +- config: + - description: a lot of noise + display_name: required fields here + name: required_fields_group + subs: &required + - description: 'integer not default required + + integer not default required + + integer not default required + + integer not default required + + integer not default required' + display_name: integer not default required + max: 100500 + min: -4 + name: integer_not_default_required + required: true + type: integer + - description: 'float not default required + + float not default required + + float not default required + + float not default required + + float not default required' + display_name: float not default required + max: 9000 + min: -8 + name: float_not_default_required + required: true + type: float + - description: 'string not default required + + string not default required + + string not default required + + string not default required + + string not default required' + display_name: string not default required + name: string_not_default_required + required: true + type: string + - description: 'password not default required no confirm + + password not default required no confirm + + password not default required no confirm + + password not default required no confirm + + password not default required no confirm' + display_name: password not default required no confirm + name: password_not_default_required_no_confirm + required: true + type: password + ui_options: + no_confirm: true + - description: 'text not default required + + text not default required + + text not default required + + text not default required + + text not default required' + display_name: text not default required + name: text_not_default_required + required: true + type: text + - description: 'file not default required + + file not default required + + file not default required + + file not default required + + file not default required' + display_name: file not default required + name: file_not_default_required + required: true + type: file + - description: 'json not default required + + json not default required + + json not default required + + json not default required + + json not default required' + display_name: json not default required + name: json_not_default_required + required: true + type: json + - description: 'map not default required + + map not default required + + map not default required + + map not default required + + map not default required' + display_name: map not default required + name: map_not_default_required + required: true + type: map + - description: 'list not default required + + list not default required + + list not default required + + list not default required + + list not default required' + display_name: list not default required + name: list_not_default_required + required: true + type: list + type: group + - description: 'group not default + + group not default + + group not default + + group not default + + group not default' + display_name: group not default + name: group_not_default + subs: &id013 + - &id014 + default: 7 + description: 'integer default + + integer default + + integer default + + integer default + + integer default' + display_name: integer default + max: 100500 + min: -4 + name: integer_default + type: integer + - &id015 + default: 7 + description: 'integer default + + integer default + + integer default + + integer default + + integer default' + display_name: integer default + name: integer_default + type: integer + - &id016 + default: 7 + description: 'integer default advanced invisible + + integer default advanced invisible + + integer default advanced invisible + + integer default advanced invisible + + integer default advanced invisible' + display_name: integer default advanced invisible + max: 100500 + min: -4 + name: integer_default_advanced_invisible + type: integer + ui_options: &id001 + advanced: true + invisible: true + - &id017 + default: 7 + description: 'integer default not advanced invisible + + integer default not advanced invisible + + integer default not advanced invisible + + integer default not advanced invisible + + integer default not advanced invisible' + display_name: integer default not advanced invisible + max: 100500 + min: -4 + name: integer_default_not_advanced_invisible + type: integer + ui_options: &id002 + advanced: false + invisible: true + - &id018 + default: 7 + description: 'integer default advanced not invisible + + integer default advanced not invisible + + integer default advanced not invisible + + integer default advanced not invisible + + integer default advanced not invisible' + display_name: integer default advanced not invisible + max: 100500 + min: -4 + name: integer_default_advanced_not_invisible + type: integer + ui_options: &id003 + advanced: true + invisible: false + - &id019 + default: 7 + description: 'integer default not advanced not invisible + + integer default not advanced not invisible + + integer default not advanced not invisible + + integer default not advanced not invisible + + integer default not advanced not invisible' + display_name: integer default not advanced not invisible + max: 100500 + min: -4 + name: integer_default_not_advanced_not_invisible + type: integer + ui_options: &id004 + advanced: false + invisible: false + - &id020 + default: 7 + description: 'integer default invisible + + integer default invisible + + integer default invisible + + integer default invisible + + integer default invisible' + display_name: integer default invisible + max: 100500 + min: -4 + name: integer_default_invisible + type: integer + ui_options: &id005 + invisible: true + - &id021 + default: 7 + description: 'integer default not invisible + + integer default not invisible + + integer default not invisible + + integer default not invisible + + integer default not invisible' + display_name: integer default not invisible + max: 100500 + min: -4 + name: integer_default_not_invisible + type: integer + ui_options: &id006 + invisible: false + - &id022 + default: 7 + description: 'integer default advanced + + integer default advanced + + integer default advanced + + integer default advanced + + integer default advanced' + display_name: integer default advanced + max: 100500 + min: -4 + name: integer_default_advanced + type: integer + ui_options: &id007 + advanced: true + - &id023 + default: 7 + description: 'integer default not advanced + + integer default not advanced + + integer default not advanced + + integer default not advanced + + integer default not advanced' + display_name: integer default not advanced + max: 100500 + min: -4 + name: integer_default_not_advanced + type: integer + ui_options: &id008 + advanced: false + - &id024 + default: 7 + description: 'integer default required + + integer default required + + integer default required + + integer default required + + integer default required' + display_name: integer default required + max: 100500 + min: -4 + name: integer_default_required + required: true + type: integer + + - &id026 + default: 7 + description: 'integer default not required + + integer default not required + + integer default not required + + integer default not required + + integer default not required' + display_name: integer default not required + max: 100500 + min: -4 + name: integer_default_not_required + required: false + type: integer + - &id027 + description: 'integer not default not required + + integer not default not required + + integer not default not required + + integer not default not required + + integer not default not required' + display_name: integer not default not required + max: 100500 + min: -4 + name: integer_not_default_not_required + required: false + type: integer + - &id028 + default: 7 + description: 'integer default + + integer default + + integer default + + integer default + + integer default' + display_name: integer default + max: 100500 + min: -4 + name: integer_default + read_only: any + type: integer + - &empty_readonly_int + required: false + description: 'integer default readonly + + integer default readonly + + integer default readonly + + integer default readonly + + integer default readonly' + display_name: integer not default readonly + max: 100500 + min: -4 + name: integer_not_default_readonly + read_only: any + type: integer + - &id029 + default: 7.0 + description: 'float default + + float default + + float default + + float default + + float default' + display_name: float default + max: 9000 + min: -8 + name: float_default + type: float + - &id030 + default: 7.0 + description: 'float default + + float default + + float default + + float default + + float default' + display_name: float default + name: float_default + type: float + - &id031 + default: 7.0 + description: 'float default advanced invisible + + float default advanced invisible + + float default advanced invisible + + float default advanced invisible + + float default advanced invisible' + display_name: float default advanced invisible + max: 9000 + min: -8 + name: float_default_advanced_invisible + type: float + ui_options: *id001 + - &id032 + default: 7.0 + description: 'float default not advanced invisible + + float default not advanced invisible + + float default not advanced invisible + + float default not advanced invisible + + float default not advanced invisible' + display_name: float default not advanced invisible + max: 9000 + min: -8 + name: float_default_not_advanced_invisible + type: float + ui_options: *id002 + - &id033 + default: 7.0 + description: 'float default advanced not invisible + + float default advanced not invisible + + float default advanced not invisible + + float default advanced not invisible + + float default advanced not invisible' + display_name: float default advanced not invisible + max: 9000 + min: -8 + name: float_default_advanced_not_invisible + type: float + ui_options: *id003 + - &id034 + default: 7.0 + description: 'float default not advanced not invisible + + float default not advanced not invisible + + float default not advanced not invisible + + float default not advanced not invisible + + float default not advanced not invisible' + display_name: float default not advanced not invisible + max: 9000 + min: -8 + name: float_default_not_advanced_not_invisible + type: float + ui_options: *id004 + - &id035 + default: 7.0 + description: 'float default invisible + + float default invisible + + float default invisible + + float default invisible + + float default invisible' + display_name: float default invisible + max: 9000 + min: -8 + name: float_default_invisible + type: float + ui_options: *id005 + - &id036 + default: 7.0 + description: 'float default not invisible + + float default not invisible + + float default not invisible + + float default not invisible + + float default not invisible' + display_name: float default not invisible + max: 9000 + min: -8 + name: float_default_not_invisible + type: float + ui_options: *id006 + - &id037 + default: 7.0 + description: 'float default advanced + + float default advanced + + float default advanced + + float default advanced + + float default advanced' + display_name: float default advanced + max: 9000 + min: -8 + name: float_default_advanced + type: float + ui_options: *id007 + - &id038 + default: 7.0 + description: 'float default not advanced + + float default not advanced + + float default not advanced + + float default not advanced + + float default not advanced' + display_name: float default not advanced + max: 9000 + min: -8 + name: float_default_not_advanced + type: float + ui_options: *id008 + - &id039 + default: 7.0 + description: 'float default required + + float default required + + float default required + + float default required + + float default required' + display_name: float default required + max: 9000 + min: -8 + name: float_default_required + required: true + type: float + + - &id041 + default: 7.0 + description: 'float default not required + + float default not required + + float default not required + + float default not required + + float default not required' + display_name: float default not required + max: 9000 + min: -8 + name: float_default_not_required + required: false + type: float + - &id042 + description: 'float not default not required + + float not default not required + + float not default not required + + float not default not required + + float not default not required' + display_name: float not default not required + max: 9000 + min: -8 + name: float_not_default_not_required + required: false + type: float + - &id043 + default: 7.0 + description: 'float default + + float default + + float default + + float default + + float default' + display_name: float default + max: 9000 + min: -8 + name: float_default + read_only: any + type: float + - &empty_readonly_float + required: false + description: 'float default + + float default + + float default + + float default + + float default' + display_name: float not default + max: 9000 + min: -8 + name: float_not_default + read_only: any + type: float + - &id044 + default: AZAZA Lalka + description: 'string default + + string default + + string default + + string default + + string default' + display_name: string default + name: string_default + type: string + - &id045 + default: AZAZA Lalka + description: 'string default advanced invisible + + string default advanced invisible + + string default advanced invisible + + string default advanced invisible + + string default advanced invisible' + display_name: string default advanced invisible + name: string_default_advanced_invisible + type: string + ui_options: *id001 + - &id046 + default: AZAZA Lalka + description: 'string default not advanced invisible + + string default not advanced invisible + + string default not advanced invisible + + string default not advanced invisible + + string default not advanced invisible' + display_name: string default not advanced invisible + name: string_default_not_advanced_invisible + type: string + ui_options: *id002 + - &id047 + default: AZAZA Lalka + description: 'string default advanced not invisible + + string default advanced not invisible + + string default advanced not invisible + + string default advanced not invisible + + string default advanced not invisible' + display_name: string default advanced not invisible + name: string_default_advanced_not_invisible + type: string + ui_options: *id003 + - &id048 + default: AZAZA Lalka + description: 'string default not advanced not invisible + + string default not advanced not invisible + + string default not advanced not invisible + + string default not advanced not invisible + + string default not advanced not invisible' + display_name: string default not advanced not invisible + name: string_default_not_advanced_not_invisible + type: string + ui_options: *id004 + - &id049 + default: AZAZA Lalka + description: 'string default invisible + + string default invisible + + string default invisible + + string default invisible + + string default invisible' + display_name: string default invisible + name: string_default_invisible + type: string + ui_options: *id005 + - &id050 + default: AZAZA Lalka + description: 'string default not invisible + + string default not invisible + + string default not invisible + + string default not invisible + + string default not invisible' + display_name: string default not invisible + name: string_default_not_invisible + type: string + ui_options: *id006 + - &id051 + default: AZAZA Lalka + description: 'string default advanced + + string default advanced + + string default advanced + + string default advanced + + string default advanced' + display_name: string default advanced + name: string_default_advanced + type: string + ui_options: *id007 + - &id052 + default: AZAZA Lalka + description: 'string default not advanced + + string default not advanced + + string default not advanced + + string default not advanced + + string default not advanced' + display_name: string default not advanced + name: string_default_not_advanced + type: string + ui_options: *id008 + - &id053 + default: AZAZA Lalka + description: 'string default required + + string default required + + string default required + + string default required + + string default required' + display_name: string default required + name: string_default_required + required: true + type: string + + - &id055 + default: AZAZA Lalka + description: 'string default not required + + string default not required + + string default not required + + string default not required + + string default not required' + display_name: string default not required + name: string_default_not_required + required: false + type: string + - &id056 + description: 'string not default not required + + string not default not required + + string not default not required + + string not default not required + + string not default not required' + display_name: string not default not required + name: string_not_default_not_required + required: false + type: string + - &id057 + default: AZAZA Lalka + description: 'string default + + string default + + string default + + string default + + string default' + display_name: string default + name: string_default + read_only: any + type: string + - &empty_readonly_string + required: false + description: 'string default + + string default + + string default + + string default + + string default' + display_name: string not default + name: string_not_default + read_only: any + type: string + - &id058 + default: 'that is it: a password' + description: 'password default no confirm + + password default no confirm + + password default no confirm + + password default no confirm + + password default no confirm' + display_name: password default no confirm + name: password_default_no_confirm + type: password + ui_options: &id009 + no_confirm: true + - &id059 + default: 'that is it: a password' + description: 'password default + + password default + + password default + + password default + + password default' + display_name: password default + name: password_default + type: password + - &id060 + default: 'that is it: a password' + description: 'password default advanced invisible + + password default advanced invisible + + password default advanced invisible + + password default advanced invisible + + password default advanced invisible' + display_name: password default advanced invisible + name: password_default_advanced_invisible + type: password + ui_options: *id001 + - &id061 + default: 'that is it: a password' + description: 'password default not advanced invisible + + password default not advanced invisible + + password default not advanced invisible + + password default not advanced invisible + + password default not advanced invisible' + display_name: password default not advanced invisible + name: password_default_not_advanced_invisible + type: password + ui_options: *id002 + - &id062 + default: 'that is it: a password' + description: 'password default advanced not invisible + + password default advanced not invisible + + password default advanced not invisible + + password default advanced not invisible + + password default advanced not invisible' + display_name: password default advanced not invisible + name: password_default_advanced_not_invisible + type: password + ui_options: *id003 + - &id063 + default: 'that is it: a password' + description: 'password default not advanced not invisible + + password default not advanced not invisible + + password default not advanced not invisible + + password default not advanced not invisible + + password default not advanced not invisible' + display_name: password default not advanced not invisible + name: password_default_not_advanced_not_invisible + type: password + ui_options: *id004 + - &id064 + default: 'that is it: a password' + description: 'password default invisible + + password default invisible + + password default invisible + + password default invisible + + password default invisible' + display_name: password default invisible + name: password_default_invisible + type: password + ui_options: *id005 + - &id065 + default: 'that is it: a password' + description: 'password default not invisible + + password default not invisible + + password default not invisible + + password default not invisible + + password default not invisible' + display_name: password default not invisible + name: password_default_not_invisible + type: password + ui_options: *id006 + - &id066 + default: 'that is it: a password' + description: 'password default advanced + + password default advanced + + password default advanced + + password default advanced + + password default advanced' + display_name: password default advanced + name: password_default_advanced + type: password + ui_options: *id007 + - &id067 + default: 'that is it: a password' + description: 'password default not advanced + + password default not advanced + + password default not advanced + + password default not advanced + + password default not advanced' + display_name: password default not advanced + name: password_default_not_advanced + type: password + ui_options: *id008 + - &id068 + default: 'that is it: a password' + description: 'password default required no confirm + + password default required no confirm + + password default required no confirm + + password default required no confirm + + password default required no confirm' + display_name: password default required no confirm + name: password_default_required_no_confirm + required: true + type: password + ui_options: *id009 + + - &id070 + default: 'that is it: a password' + description: 'password default not required no confirm + + password default not required no confirm + + password default not required no confirm + + password default not required no confirm + + password default not required no confirm' + display_name: password default not required no confirm + name: password_default_not_required_no_confirm + required: false + type: password + ui_options: *id009 + - &id071 + description: 'password not default not required no confirm + + password not default not required no confirm + + password not default not required no confirm + + password not default not required no confirm + + password not default not required no confirm' + display_name: password not default not required no confirm + name: password_not_default_not_required_no_confirm + required: false + type: password + ui_options: *id009 + - &id072 + default: 'that is it: a password' + description: 'password default no confirm + + password default no confirm + + password default no confirm + + password default no confirm + + password default no confirm' + display_name: password default no confirm + name: password_default_no_confirm + read_only: any + type: password + ui_options: *id009 + - &empty_readonly_password + required: false + default: 'that is it: a password' + description: 'password default no confirm + + password default no confirm + + password default no confirm + + password default no confirm + + password default no confirm' + display_name: password not default no confirm + name: password_not_default_no_confirm + read_only: any + type: password + ui_options: *id009 + - &id073 + default: " Text\n text\n text\n " + description: 'text default + + text default + + text default + + text default + + text default' + display_name: text default + name: text_default + type: text + - &id074 + default: " Text\n text\n text\n " + description: 'text default advanced invisible + + text default advanced invisible + + text default advanced invisible + + text default advanced invisible + + text default advanced invisible' + display_name: text default advanced invisible + name: text_default_advanced_invisible + type: text + ui_options: *id001 + - &id075 + default: " Text\n text\n text\n " + description: 'text default not advanced invisible + + text default not advanced invisible + + text default not advanced invisible + + text default not advanced invisible + + text default not advanced invisible' + display_name: text default not advanced invisible + name: text_default_not_advanced_invisible + type: text + ui_options: *id002 + - &id076 + default: " Text\n text\n text\n " + description: 'text default advanced not invisible + + text default advanced not invisible + + text default advanced not invisible + + text default advanced not invisible + + text default advanced not invisible' + display_name: text default advanced not invisible + name: text_default_advanced_not_invisible + type: text + ui_options: *id003 + - &id077 + default: " Text\n text\n text\n " + description: 'text default not advanced not invisible + + text default not advanced not invisible + + text default not advanced not invisible + + text default not advanced not invisible + + text default not advanced not invisible' + display_name: text default not advanced not invisible + name: text_default_not_advanced_not_invisible + type: text + ui_options: *id004 + - &id078 + default: " Text\n text\n text\n " + description: 'text default invisible + + text default invisible + + text default invisible + + text default invisible + + text default invisible' + display_name: text default invisible + name: text_default_invisible + type: text + ui_options: *id005 + - &id079 + default: " Text\n text\n text\n " + description: 'text default not invisible + + text default not invisible + + text default not invisible + + text default not invisible + + text default not invisible' + display_name: text default not invisible + name: text_default_not_invisible + type: text + ui_options: *id006 + - &id080 + default: " Text\n text\n text\n " + description: 'text default advanced + + text default advanced + + text default advanced + + text default advanced + + text default advanced' + display_name: text default advanced + name: text_default_advanced + type: text + ui_options: *id007 + - &id081 + default: " Text\n text\n text\n " + description: 'text default not advanced + + text default not advanced + + text default not advanced + + text default not advanced + + text default not advanced' + display_name: text default not advanced + name: text_default_not_advanced + type: text + ui_options: *id008 + - &id082 + default: " Text\n text\n text\n " + description: 'text default required + + text default required + + text default required + + text default required + + text default required' + display_name: text default required + name: text_default_required + required: true + type: text + + - &id084 + default: " Text\n text\n text\n " + description: 'text default not required + + text default not required + + text default not required + + text default not required + + text default not required' + display_name: text default not required + name: text_default_not_required + required: false + type: text + - &id085 + description: 'text not default not required + + text not default not required + + text not default not required + + text not default not required + + text not default not required' + display_name: text not default not required + name: text_not_default_not_required + required: false + type: text + - &id086 + default: " Text\n text\n text\n " + description: 'text default + + text default + + text default + + text default + + text default' + display_name: text default + name: text_default + read_only: any + type: text + - &empty_readonly_text + required: false + description: 'text default + + text default + + text default + + text default + + text default' + display_name: text not default + name: text_not_default + read_only: any + type: text + - &id087 + default: file.txt + description: 'file default + + file default + + file default + + file default + + file default' + display_name: file default + name: file_default + type: file + - &id088 + default: file.txt + description: 'file default advanced invisible + + file default advanced invisible + + file default advanced invisible + + file default advanced invisible + + file default advanced invisible' + display_name: file default advanced invisible + name: file_default_advanced_invisible + type: file + ui_options: *id001 + - &id089 + default: file.txt + description: 'file default not advanced invisible + + file default not advanced invisible + + file default not advanced invisible + + file default not advanced invisible + + file default not advanced invisible' + display_name: file default not advanced invisible + name: file_default_not_advanced_invisible + type: file + ui_options: *id002 + - &id090 + default: file.txt + description: 'file default advanced not invisible + + file default advanced not invisible + + file default advanced not invisible + + file default advanced not invisible + + file default advanced not invisible' + display_name: file default advanced not invisible + name: file_default_advanced_not_invisible + type: file + ui_options: *id003 + - &id091 + default: file.txt + description: 'file default not advanced not invisible + + file default not advanced not invisible + + file default not advanced not invisible + + file default not advanced not invisible + + file default not advanced not invisible' + display_name: file default not advanced not invisible + name: file_default_not_advanced_not_invisible + type: file + ui_options: *id004 + - &id092 + default: file.txt + description: 'file default invisible + + file default invisible + + file default invisible + + file default invisible + + file default invisible' + display_name: file default invisible + name: file_default_invisible + type: file + ui_options: *id005 + - &id093 + default: file.txt + description: 'file default not invisible + + file default not invisible + + file default not invisible + + file default not invisible + + file default not invisible' + display_name: file default not invisible + name: file_default_not_invisible + type: file + ui_options: *id006 + - &id094 + default: file.txt + description: 'file default advanced + + file default advanced + + file default advanced + + file default advanced + + file default advanced' + display_name: file default advanced + name: file_default_advanced + type: file + ui_options: *id007 + - &id095 + default: file.txt + description: 'file default not advanced + + file default not advanced + + file default not advanced + + file default not advanced + + file default not advanced' + display_name: file default not advanced + name: file_default_not_advanced + type: file + ui_options: *id008 + - &id096 + default: file.txt + description: 'file default required + + file default required + + file default required + + file default required + + file default required' + display_name: file default required + name: file_default_required + required: true + type: file + + - &id098 + default: file.txt + description: 'file default not required + + file default not required + + file default not required + + file default not required + + file default not required' + display_name: file default not required + name: file_default_not_required + required: false + type: file + - &id099 + description: 'file not default not required + + file not default not required + + file not default not required + + file not default not required + + file not default not required' + display_name: file not default not required + name: file_not_default_not_required + required: false + type: file + - &id100 + default: file.txt + description: 'file default + + file default + + file default + + file default + + file default' + display_name: file default + name: file_default + read_only: any + type: file + - &empty_readonly_file + required: false + description: 'file default + + file default + + file default + + file default + + file default' + display_name: file not default + name: file_not_default + read_only: any + type: file + - &id101 + default: &id010 + xxx: yyyy + zzz: + - 1 + - 2 + - 3 + description: 'json default + + json default + + json default + + json default + + json default' + display_name: json default + name: json_default + type: json + - &id102 + default: *id010 + description: 'json default advanced invisible + + json default advanced invisible + + json default advanced invisible + + json default advanced invisible + + json default advanced invisible' + display_name: json default advanced invisible + name: json_default_advanced_invisible + type: json + ui_options: *id001 + - &id103 + default: *id010 + description: 'json default not advanced invisible + + json default not advanced invisible + + json default not advanced invisible + + json default not advanced invisible + + json default not advanced invisible' + display_name: json default not advanced invisible + name: json_default_not_advanced_invisible + type: json + ui_options: *id002 + - &id104 + default: *id010 + description: 'json default advanced not invisible + + json default advanced not invisible + + json default advanced not invisible + + json default advanced not invisible + + json default advanced not invisible' + display_name: json default advanced not invisible + name: json_default_advanced_not_invisible + type: json + ui_options: *id003 + - &id105 + default: *id010 + description: 'json default not advanced not invisible + + json default not advanced not invisible + + json default not advanced not invisible + + json default not advanced not invisible + + json default not advanced not invisible' + display_name: json default not advanced not invisible + name: json_default_not_advanced_not_invisible + type: json + ui_options: *id004 + - &id106 + default: *id010 + description: 'json default invisible + + json default invisible + + json default invisible + + json default invisible + + json default invisible' + display_name: json default invisible + name: json_default_invisible + type: json + ui_options: *id005 + - &id107 + default: *id010 + description: 'json default not invisible + + json default not invisible + + json default not invisible + + json default not invisible + + json default not invisible' + display_name: json default not invisible + name: json_default_not_invisible + type: json + ui_options: *id006 + - &id108 + default: *id010 + description: 'json default advanced + + json default advanced + + json default advanced + + json default advanced + + json default advanced' + display_name: json default advanced + name: json_default_advanced + type: json + ui_options: *id007 + - &id109 + default: *id010 + description: 'json default not advanced + + json default not advanced + + json default not advanced + + json default not advanced + + json default not advanced' + display_name: json default not advanced + name: json_default_not_advanced + type: json + ui_options: *id008 + - &id110 + default: *id010 + description: 'json default required + + json default required + + json default required + + json default required + + json default required' + display_name: json default required + name: json_default_required + required: true + type: json + + - &id112 + default: *id010 + description: 'json default not required + + json default not required + + json default not required + + json default not required + + json default not required' + display_name: json default not required + name: json_default_not_required + required: false + type: json + - &id113 + description: 'json not default not required + + json not default not required + + json not default not required + + json not default not required + + json not default not required' + display_name: json not default not required + name: json_not_default_not_required + required: false + type: json + - &id114 + default: *id010 + description: 'json default + + json default + + json default + + json default + + json default' + display_name: json default + name: json_default + read_only: any + type: json + - &empty_readonly_json + required: false + description: 'json default + + json default + + json default + + json default + + json default' + display_name: json not default + name: json_not_default + read_only: any + type: json + - &id115 + default: &id011 + one: two + three: four + description: 'map default + + map default + + map default + + map default + + map default' + display_name: map default + name: map_default + type: map + - &id116 + default: *id011 + description: 'map default advanced invisible + + map default advanced invisible + + map default advanced invisible + + map default advanced invisible + + map default advanced invisible' + display_name: map default advanced invisible + name: map_default_advanced_invisible + type: map + ui_options: *id001 + - &id117 + default: *id011 + description: 'map default not advanced invisible + + map default not advanced invisible + + map default not advanced invisible + + map default not advanced invisible + + map default not advanced invisible' + display_name: map default not advanced invisible + name: map_default_not_advanced_invisible + type: map + ui_options: *id002 + - &id118 + default: *id011 + description: 'map default advanced not invisible + + map default advanced not invisible + + map default advanced not invisible + + map default advanced not invisible + + map default advanced not invisible' + display_name: map default advanced not invisible + name: map_default_advanced_not_invisible + type: map + ui_options: *id003 + - &id119 + default: *id011 + description: 'map default not advanced not invisible + + map default not advanced not invisible + + map default not advanced not invisible + + map default not advanced not invisible + + map default not advanced not invisible' + display_name: map default not advanced not invisible + name: map_default_not_advanced_not_invisible + type: map + ui_options: *id004 + - &id120 + default: *id011 + description: 'map default invisible + + map default invisible + + map default invisible + + map default invisible + + map default invisible' + display_name: map default invisible + name: map_default_invisible + type: map + ui_options: *id005 + - &id121 + default: *id011 + description: 'map default not invisible + + map default not invisible + + map default not invisible + + map default not invisible + + map default not invisible' + display_name: map default not invisible + name: map_default_not_invisible + type: map + ui_options: *id006 + - &id122 + default: *id011 + description: 'map default advanced + + map default advanced + + map default advanced + + map default advanced + + map default advanced' + display_name: map default advanced + name: map_default_advanced + type: map + ui_options: *id007 + - &id123 + default: *id011 + description: 'map default not advanced + + map default not advanced + + map default not advanced + + map default not advanced + + map default not advanced' + display_name: map default not advanced + name: map_default_not_advanced + type: map + ui_options: *id008 + - &id124 + default: *id011 + description: 'map default required + + map default required + + map default required + + map default required + + map default required' + display_name: map default required + name: map_default_required + required: true + type: map + + - &id126 + default: *id011 + description: 'map default not required + + map default not required + + map default not required + + map default not required + + map default not required' + display_name: map default not required + name: map_default_not_required + required: false + type: map + - &id127 + description: 'map not default not required + + map not default not required + + map not default not required + + map not default not required + + map not default not required' + display_name: map not default not required + name: map_not_default_not_required + required: false + type: map + - &id128 + default: *id011 + description: 'map default + + map default + + map default + + map default + + map default' + display_name: map default + name: map_default + read_only: any + type: map + - &empty_readonly_map + required: false + description: 'map default + + map default + + map default + + map default + + map default' + display_name: map not default + name: map_not_default + read_only: any + type: map + - &id129 + default: &id012 + - A B C D E F G + - John is hiding far from me + - Looking here, looking there + - I can't see him enywhere + description: 'list default + + list default + + list default + + list default + + list default' + display_name: list default + name: list_default + type: list + - &id130 + default: *id012 + description: 'list default advanced invisible + + list default advanced invisible + + list default advanced invisible + + list default advanced invisible + + list default advanced invisible' + display_name: list default advanced invisible + name: list_default_advanced_invisible + type: list + ui_options: *id001 + - &id131 + default: *id012 + description: 'list default not advanced invisible + + list default not advanced invisible + + list default not advanced invisible + + list default not advanced invisible + + list default not advanced invisible' + display_name: list default not advanced invisible + name: list_default_not_advanced_invisible + type: list + ui_options: *id002 + - &id132 + default: *id012 + description: 'list default advanced not invisible + + list default advanced not invisible + + list default advanced not invisible + + list default advanced not invisible + + list default advanced not invisible' + display_name: list default advanced not invisible + name: list_default_advanced_not_invisible + type: list + ui_options: *id003 + - &id133 + default: *id012 + description: 'list default not advanced not invisible + + list default not advanced not invisible + + list default not advanced not invisible + + list default not advanced not invisible + + list default not advanced not invisible' + display_name: list default not advanced not invisible + name: list_default_not_advanced_not_invisible + type: list + ui_options: *id004 + - &id134 + default: *id012 + description: 'list default invisible + + list default invisible + + list default invisible + + list default invisible + + list default invisible' + display_name: list default invisible + name: list_default_invisible + type: list + ui_options: *id005 + - &id135 + default: *id012 + description: 'list default not invisible + + list default not invisible + + list default not invisible + + list default not invisible + + list default not invisible' + display_name: list default not invisible + name: list_default_not_invisible + type: list + ui_options: *id006 + - &id136 + default: *id012 + description: 'list default advanced + + list default advanced + + list default advanced + + list default advanced + + list default advanced' + display_name: list default advanced + name: list_default_advanced + type: list + ui_options: *id007 + - &id137 + default: *id012 + description: 'list default not advanced + + list default not advanced + + list default not advanced + + list default not advanced + + list default not advanced' + display_name: list default not advanced + name: list_default_not_advanced + type: list + ui_options: *id008 + - &id138 + default: *id012 + description: 'list default required + + list default required + + list default required + + list default required + + list default required' + display_name: list default required + name: list_default_required + required: true + type: list + + - &id140 + default: *id012 + description: 'list default not required + + list default not required + + list default not required + + list default not required + + list default not required' + display_name: list default not required + name: list_default_not_required + required: false + type: list + - &id141 + description: 'list not default not required + + list not default not required + + list not default not required + + list not default not required + + list not default not required' + display_name: list not default not required + name: list_not_default_not_required + required: false + type: list + - &id142 + default: *id012 + description: 'list default + + list default + + list default + + list default + + list default' + display_name: list default + name: list_default + read_only: any + type: list + - &empty_readonly_list + required: false + description: 'list default + + list default + + list default + + list default + + list default' + display_name: list not default + name: list_not_default + read_only: any + type: list + type: group + - description: 'group not default + + group not default + + group not default + + group not default + + group not default' + name: group_not_default_no_display_name + subs: *id013 + type: group + - description: 'group not default advanced invisible + + group not default advanced invisible + + group not default advanced invisible + + group not default advanced invisible + + group not default advanced invisible' + display_name: group not default advanced invisible + name: group_not_default_advanced_invisible + subs: *id013 + type: group + ui_options: *id001 + - description: 'group not default not advanced invisible + + group not default not advanced invisible + + group not default not advanced invisible + + group not default not advanced invisible + + group not default not advanced invisible' + display_name: group not default not advanced invisible + name: group_not_default_not_advanced_invisible + subs: *id013 + type: group + ui_options: *id002 + - description: 'group not default advanced not invisible + + group not default advanced not invisible + + group not default advanced not invisible + + group not default advanced not invisible + + group not default advanced not invisible' + display_name: group not default advanced not invisible + name: group_not_default_advanced_not_invisible + subs: *id013 + type: group + ui_options: *id003 + - description: 'group not default not advanced not invisible + + group not default not advanced not invisible + + group not default not advanced not invisible + + group not default not advanced not invisible + + group not default not advanced not invisible' + display_name: group not default not advanced not invisible + name: group_not_default_not_advanced_not_invisible + subs: *id013 + type: group + ui_options: *id004 + - description: 'group not default invisible + + group not default invisible + + group not default invisible + + group not default invisible + + group not default invisible' + display_name: group not default invisible + name: group_not_default_invisible + subs: *id013 + type: group + ui_options: *id005 + - description: 'group not default not invisible + + group not default not invisible + + group not default not invisible + + group not default not invisible + + group not default not invisible' + display_name: group not default not invisible + name: group_not_default_not_invisible + subs: *id013 + type: group + ui_options: *id006 + - description: 'group not default advanced + + group not default advanced + + group not default advanced + + group not default advanced + + group not default advanced' + display_name: group not default advanced + name: group_not_default_advanced + subs: *id013 + type: group + ui_options: *id007 + - description: 'group not default not advanced + + group not default not advanced + + group not default not advanced + + group not default not advanced + + group not default not advanced' + display_name: group not default not advanced + name: group_not_default_not_advanced + subs: *id013 + type: group + ui_options: *id008 + - activatable: true + active: true + description: 'group not default activatable active + + group not default activatable active + + group not default activatable active + + group not default activatable active + + group not default activatable active' + display_name: group not default activatable active + name: group_not_default_activatable_active + subs: *id013 + type: group + - activatable: true + active: false + description: 'group not default activatable not active + + group not default activatable not active + + group not default activatable not active + + group not default activatable not active + + group not default activatable not active' + display_name: group not default activatable not active + name: group_not_default_activatable_not_active + subs: *id013 + type: group + - activatable: true + active: false + name: group_activatable_not_active_required_fields + display_name: group activatable not active with required fields + subs: *required + type: group + - activatable: false + description: 'group not default not activatable + + group not default not activatable + + group not default not activatable + + group not default not activatable + + group not default not activatable' + display_name: group not default not activatable + name: group_not_default_not_activatable + subs: *id013 + type: group + - *id014 + - *id015 + - *id016 + - *id017 + - *id018 + - *id019 + - *id020 + - *id021 + - *id022 + - *id023 + - *id024 + - *id026 + - *id027 + - *id028 + - *id029 + - *id030 + - *id031 + - *id032 + - *id033 + - *id034 + - *id035 + - *id036 + - *id037 + - *id038 + - *id039 + - *id041 + - *id042 + - *id043 + - *id044 + - *id045 + - *id046 + - *id047 + - *id048 + - *id049 + - *id050 + - *id051 + - *id052 + - *id053 + - *id055 + - *id056 + - *id057 + - *id058 + - *id059 + - *id060 + - *id061 + - *id062 + - *id063 + - *id064 + - *id065 + - *id066 + - *id067 + - *id068 + - *id070 + - *id071 + - *id072 + - *id073 + - *id074 + - *id075 + - *id076 + - *id077 + - *id078 + - *id079 + - *id080 + - *id081 + - *id082 + - *id084 + - *id085 + - *id086 + - *id087 + - *id088 + - *id089 + - *id090 + - *id091 + - *id092 + - *id093 + - *id094 + - *id095 + - *id096 + - *id098 + - *id099 + - *id100 + - *id101 + - *id102 + - *id103 + - *id104 + - *id105 + - *id106 + - *id107 + - *id108 + - *id109 + - *id110 + - *id112 + - *id113 + - *id114 + - *id115 + - *id116 + - *id117 + - *id118 + - *id119 + - *id120 + - *id121 + - *id122 + - *id123 + - *id124 + - *id126 + - *id127 + - *id128 + - *id129 + - *id130 + - *id131 + - *id132 + - *id133 + - *id134 + - *id135 + - *id136 + - *id137 + - *id138 + - *id140 + - *id141 + - *id142 + - *empty_readonly_file + - *empty_readonly_list + - *empty_readonly_map + - *empty_readonly_json + - *empty_readonly_float + - *empty_readonly_int + - *empty_readonly_password + - *empty_readonly_string + - *empty_readonly_text + display_name: New UI Config Hell + name: new_ui_config_hell + type: service + version: '100500' From 949640418ed10a3679e7f0e2a33217918e6efaf3 Mon Sep 17 00:00:00 2001 From: Ann Date: Fri, 19 Nov 2021 13:25:26 +0300 Subject: [PATCH 08/15] ADCM-2357 fix remarks --- tests/ui_tests/app/configuration.py | 53 ++++++++----------- tests/ui_tests/test_configs.py | 14 +++-- .../test_save_configuration_button.py | 40 ++++++++------ 3 files changed, 52 insertions(+), 55 deletions(-) diff --git a/tests/ui_tests/app/configuration.py b/tests/ui_tests/app/configuration.py index b7b73f0c91..4844ac841b 100644 --- a/tests/ui_tests/app/configuration.py +++ b/tests/ui_tests/app/configuration.py @@ -13,10 +13,16 @@ """Page objects for ConfigurationPage""" import json +from typing import Union import allure -import requests -from adcm_client.objects import Service +from adcm_client.objects import ( + Service, + Component, + Cluster, + Host, + Provider, +) from selenium.common.exceptions import NoSuchElementException, TimeoutException from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.remote.webelement import WebElement @@ -26,18 +32,6 @@ from tests.ui_tests.app.pages import BasePage -def _make_request(adcm_credentials, app_fs, method: str, path: str, **kwargs) -> requests.Response: - def check_response(response): - assert response.status_code == 200, f"Response status is {response.status_code}" - - token = requests.post(f'{app_fs.adcm.url}/api/v1/token/', json=adcm_credentials) - check_response(token) - header = {'Authorization': f'Token {token.json()["token"]}'} - response = requests.request(method=method, url=app_fs.adcm.url + path, headers=header, **kwargs) - check_response(response) - return response - - class Configuration(BasePage): # pylint: disable=too-many-public-methods """Class for configuration page""" @@ -68,32 +62,29 @@ def assert_field_is_editable(self, field_element, editable=True) -> None: self.is_element_editable(field_element) == editable ), f"Field {field_element} should {'be editable' if editable else 'not be editable'}" - def get_api_value(self, adcm_credentials, app_fs, field: str): + def get_config_field_value_by_api(self, item: Union[Cluster, Service, Component, Host, Provider], field: str): """Gets field value by api""" - - current_config = _make_request( - adcm_credentials, app_fs, "GET", f"/api/v1/{self.driver.current_url.replace(app_fs.adcm.url, '')}/current" - ).text - current_config_dict = json.loads(current_config)['config'] + current_config = item.config() try: - if 'group' in current_config_dict: - if field in current_config_dict['group']: - return current_config_dict['group'][field] - return current_config_dict['group']['structure_property'][field] - - if field in current_config_dict: - return current_config_dict[field] - return current_config_dict['structure_property'][field] + if 'group' in current_config: + if field in current_config['group']: + return current_config['group'][field] + return current_config['group']['structure_property'][field] + + if field in current_config: + return current_config[field] + return current_config['structure_property'][field] except KeyError as error: raise AssertionError(f"No parameter {field} found by api") from error - # pylint:disable=too-many-arguments @allure.step('Assert field: {field} to have value: {expected_value}') - def assert_field_content_equal(self, adcm_credentials, app_fs, field_type, field, expected_value): + def assert_field_content_equal( + self, item: Union[Cluster, Service, Component, Host, Provider], field_type, field, expected_value + ): """Assert field value based on field type and name""" current_value = self.get_field_value_by_type(field, field_type) - current_api_value = self.get_api_value(adcm_credentials, app_fs, field.text.split(":")[0]) + current_api_value = self.get_config_field_value_by_api(item, field.text.split(":")[0]) if field_type in ('password', 'secrettext'): # In case of password we have no raw password in API after writing. if expected_value is not None and expected_value != "": diff --git a/tests/ui_tests/test_configs.py b/tests/ui_tests/test_configs.py index 88305a13fb..ba06827231 100644 --- a/tests/ui_tests/test_configs.py +++ b/tests/ui_tests/test_configs.py @@ -332,7 +332,7 @@ def _prepare_group_config(config): @pytest.mark.parametrize("config_dict", generate_configs()) @pytest.mark.usefixtures("login_to_adcm_over_api") -def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_credentials): +def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs): """Test UI configuration page without groups. Before start test actions we always create configuration and expected result. All logic for test expected result in functions before this test function. @@ -355,7 +355,7 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_cre ) field_type = config['config'][0]['type'] - _, ui_config = prepare_cluster_and_get_config(sdk_client_fs, path, app_fs) + cluster, ui_config = prepare_cluster_and_get_config(sdk_client_fs, path, app_fs) fields = ui_config.get_app_fields() with allure.step('Check save button status'): @@ -376,9 +376,7 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_cre for field in fields: ui_config.assert_field_is_editable(field, expected['editable']) if expected['content']: - ui_config.assert_field_content_equal( - adcm_credentials, app_fs, field_type, fields[0], config['config'][0]['default'] - ) + ui_config.assert_field_content_equal(cluster, field_type, fields[0], config['config'][0]['default']) if expected['alerts']: ui_config.assert_alerts_presented(field_type) else: @@ -387,7 +385,7 @@ def test_configs_fields(sdk_client_fs: ADCMClient, config_dict, app_fs, adcm_cre @pytest.mark.parametrize(("config_dict", "expected"), generate_group_configs()) @pytest.mark.usefixtures("login_to_adcm_over_api") -def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, app_fs, adcm_credentials): +def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, app_fs): """Test for configuration fields with groups. Before start test actions we always create configuration and expected result. All logic for test expected result in functions before this test function. If we have @@ -412,7 +410,7 @@ def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, a field_type = config['config'][0]['subs'][0]['type'] - _, ui_config = prepare_cluster_and_get_config(sdk_client_fs, path, app_fs) + cluster, ui_config = prepare_cluster_and_get_config(sdk_client_fs, path, app_fs) groups = ui_config.get_group_elements() fields = ui_config.get_app_fields() @@ -450,7 +448,7 @@ def test_group_configs_field(sdk_client_fs: ADCMClient, config_dict, expected, a ui_config.assert_field_is_editable(field, expected['editable']) if expected['content']: default_value = config['config'][0]['subs'][0]['default'] - ui_config.assert_field_content_equal(adcm_credentials, app_fs, field_type, fields[0], default_value) + ui_config.assert_field_content_equal(cluster, field_type, fields[0], default_value) if expected['alerts']: ui_config.assert_alerts_presented(field_type) if "activatable" in config['config'][0].keys(): diff --git a/tests/ui_tests/test_save_configuration_button.py b/tests/ui_tests/test_save_configuration_button.py index 2fcfff221a..c00a2fc123 100644 --- a/tests/ui_tests/test_save_configuration_button.py +++ b/tests/ui_tests/test_save_configuration_button.py @@ -12,15 +12,23 @@ """UI tests for save configuration button""" -# pylint: disable=redefined-outer-name,too-many-arguments +# pylint: disable=redefined-outer-name import itertools import os import shutil +from typing import Union import allure import pytest import yaml -from adcm_client.objects import ADCMClient, Service, Host, Cluster, Bundle, Provider +from adcm_client.objects import ADCMClient, Bundle +from adcm_client.objects import ( + Service, + Component, + Cluster, + Host, + Provider, +) from adcm_pytest_plugin.utils import random_string, get_data_dir from tests.ui_tests.app.configuration import Configuration @@ -229,7 +237,11 @@ def _update_config_property(config_page: Configuration, field, field_type: str): def _test_save_configuration_button( - adcm_credentials, app_fs, config_page: Configuration, prop_types: list, group_name=None, use_advanced=False + item: Union[Cluster, Service, Component, Host, Provider], + config_page: Configuration, + prop_types: list, + group_name=None, + use_advanced=False, ): if use_advanced: config_page.click_advanced() @@ -261,7 +273,7 @@ def _test_save_configuration_button( field_type = "string" field = config_page.get_form_field(field) value_to_check = _get_test_value(field_type) - config_page.assert_field_content_equal(adcm_credentials, app_fs, field_type, field, value_to_check) + config_page.assert_field_content_equal(item, field_type, field, value_to_check) def _get_default_props_list() -> list: @@ -279,12 +291,11 @@ def _get_default_props_list() -> list: entity_type="cluster", prop_types=_get_default_props_list(), ) -def test_cluster_configuration_save_button(adcm_credentials, app_fs, bundle_content, cluster_config_page): +def test_cluster_configuration_save_button(bundle_content, cluster_config_page, cluster): """Test cluster configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( - adcm_credentials, - app_fs, + cluster, cluster_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, @@ -297,12 +308,11 @@ def test_cluster_configuration_save_button(adcm_credentials, app_fs, bundle_cont entity_type="service", prop_types=_get_default_props_list(), ) -def test_service_configuration_save_button(adcm_credentials, app_fs, bundle_content, service_config_page): +def test_service_configuration_save_button(bundle_content, service_config_page, service): """Test service configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( - adcm_credentials, - app_fs, + service, service_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, @@ -315,12 +325,11 @@ def test_service_configuration_save_button(adcm_credentials, app_fs, bundle_cont entity_type="provider", prop_types=_get_default_props_list(), ) -def test_provider_configuration_save_button(adcm_credentials, app_fs, bundle_content, provider_config_page): +def test_provider_configuration_save_button(bundle_content, provider_config_page, provider): """Test provider configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( - adcm_credentials, - app_fs, + provider, provider_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, @@ -333,12 +342,11 @@ def test_provider_configuration_save_button(adcm_credentials, app_fs, bundle_con entity_type="host", prop_types=_get_default_props_list(), ) -def test_host_configuration_save_button(adcm_credentials, app_fs, bundle_content, host_config_page): +def test_host_configuration_save_button(bundle_content, host_config_page, host): """Test host configuration save button""" (selected_opts, prop_types), _ = bundle_content _test_save_configuration_button( - adcm_credentials, - app_fs, + host, host_config_page, group_name=GROUP_NAME if CONFIG_USE_GROUP in selected_opts else None, use_advanced=CONFIG_USE_ADVANCED in selected_opts, From c876e8c6dea76e492adc1149b1dd4559ce36c21b Mon Sep 17 00:00:00 2001 From: Ann Date: Fri, 19 Nov 2021 13:27:41 +0300 Subject: [PATCH 09/15] ADCM-2357 fix remarks --- tests/ui_tests/app/pages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui_tests/app/pages.py b/tests/ui_tests/app/pages.py index cedd082aa2..aa0edc2b34 100644 --- a/tests/ui_tests/app/pages.py +++ b/tests/ui_tests/app/pages.py @@ -245,7 +245,7 @@ def is_popup_presented_on_page(self, timeout: int = 5): except TimeoutException: return False - def assert_no_popups_displayed(self, timeout: int = 2): + def assert_no_popups_displayed(self, timeout: int = 3): """Assert there is no popups displayed""" assert not self.is_popup_presented_on_page(timeout=timeout), "There is a popup with error on the page" From 94e2c7b702d4b78b361d453c803b3a69b1ab1032 Mon Sep 17 00:00:00 2001 From: Ann Date: Fri, 19 Nov 2021 14:35:16 +0300 Subject: [PATCH 10/15] ADCM-2357 fix remarks --- tests/ui_tests/app/configuration.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/ui_tests/app/configuration.py b/tests/ui_tests/app/configuration.py index 4844ac841b..5a1b4cf50f 100644 --- a/tests/ui_tests/app/configuration.py +++ b/tests/ui_tests/app/configuration.py @@ -62,7 +62,8 @@ def assert_field_is_editable(self, field_element, editable=True) -> None: self.is_element_editable(field_element) == editable ), f"Field {field_element} should {'be editable' if editable else 'not be editable'}" - def get_config_field_value_by_api(self, item: Union[Cluster, Service, Component, Host, Provider], field: str): + @staticmethod + def get_config_field_value(item: Union[Cluster, Service, Component, Host, Provider], field: str): """Gets field value by api""" current_config = item.config() try: @@ -84,7 +85,7 @@ def assert_field_content_equal( """Assert field value based on field type and name""" current_value = self.get_field_value_by_type(field, field_type) - current_api_value = self.get_config_field_value_by_api(item, field.text.split(":")[0]) + current_api_value = self.get_config_field_value(item, field.text.split(":")[0]) if field_type in ('password', 'secrettext'): # In case of password we have no raw password in API after writing. if expected_value is not None and expected_value != "": From c57379703cd174f29bfc5f0fe6b3862ca3af174b Mon Sep 17 00:00:00 2001 From: Sealwing Date: Sat, 20 Nov 2021 09:35:42 +0500 Subject: [PATCH 11/15] Save finish waiting timeout increased to 30 seconds --- tests/ui_tests/test_ui_config_hell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui_tests/test_ui_config_hell.py b/tests/ui_tests/test_ui_config_hell.py index 3ae0ba0685..ea0bd1d600 100644 --- a/tests/ui_tests/test_ui_config_hell.py +++ b/tests/ui_tests/test_ui_config_hell.py @@ -94,7 +94,7 @@ def test_save_configuration_hell( for display_name in group_names: assert display_name in ui_display_names, f"Group named '{display_name}' should be presented in config" _fill_required_fields(config_page) - config_page.config.save_config(load_timeout=15) + config_page.config.save_config(load_timeout=30) with allure.step('Ensure page is still opened'): config_page.wait_page_is_opened(timeout=1) with allure.step('Check that popup does not contain info about error'): From 4535052471f6d6f381229980332626f9f2c8dc70 Mon Sep 17 00:00:00 2001 From: Aleksandr Lihih Date: Sun, 21 Nov 2021 12:52:02 +0300 Subject: [PATCH 12/15] fix error with undefined control in config group --- .../group-keys/group-keys-wrapper.component.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts b/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts index 4f850d2b80..5cecfc6bf7 100644 --- a/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts +++ b/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts @@ -5,7 +5,7 @@ import { ConfigAttributeNames, ConfigAttributeOptions } from '@app/shared/configuration/attributes/attribute.service'; -import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; +import { FormControl, FormGroup } from '@angular/forms'; import { IFieldOptions } from '@app/shared/configuration/types'; import { BaseDirective } from '@adwp-ui/widgets'; import { MatCheckboxChange } from '@angular/material/checkbox'; @@ -57,19 +57,20 @@ export class GroupKeysWrapperComponent extends BaseDirective implements Attribut } private _resolveAndSetupControls(attributeForm: FormGroup, parametersForm: FormGroup, fieldOptions: IFieldOptions): void { - let attributeControl: AbstractControl = attributeForm; - let parameterControl: AbstractControl = parametersForm; + let attributeControl: FormGroup = attributeForm; + let parameterControl: FormGroup = parametersForm; let disabled = this._attributeSrv.attributes.get(ConfigAttributeNames.CUSTOM_GROUP_KEYS).value; let text = this._attributeSrv.attributes.get(ConfigAttributeNames.CUSTOM_GROUP_KEYS).options.tooltipText; + fieldOptions.key?.split('/').reverse().forEach((key) => { - attributeControl = attributeControl.get(key); - parameterControl = parameterControl.get(key); + parameterControl = parameterControl.controls[key] as FormGroup; + attributeControl = attributeControl.controls[key] as FormGroup; disabled = disabled[key]; }); - this.groupControl = attributeControl as FormControl; - this.parameterControl = parameterControl as FormControl; + this.groupControl = attributeControl as unknown as FormControl; + this.parameterControl = parameterControl as unknown as FormControl; if (!disabled) { attributeControl.disable(); From 68766e631ea0c912137e303199c04bf127edbaa9 Mon Sep 17 00:00:00 2001 From: Aleksandr Lihih Date: Sun, 21 Nov 2021 16:03:07 +0300 Subject: [PATCH 13/15] replace native control search with GET method and path --- .../group-keys/group-keys-wrapper.component.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts b/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts index 5cecfc6bf7..8d712ee1e1 100644 --- a/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts +++ b/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts @@ -62,15 +62,10 @@ export class GroupKeysWrapperComponent extends BaseDirective implements Attribut let disabled = this._attributeSrv.attributes.get(ConfigAttributeNames.CUSTOM_GROUP_KEYS).value; let text = this._attributeSrv.attributes.get(ConfigAttributeNames.CUSTOM_GROUP_KEYS).options.tooltipText; + const path = fieldOptions.key?.split('/').reverse(); - fieldOptions.key?.split('/').reverse().forEach((key) => { - parameterControl = parameterControl.controls[key] as FormGroup; - attributeControl = attributeControl.controls[key] as FormGroup; - disabled = disabled[key]; - }); - - this.groupControl = attributeControl as unknown as FormControl; - this.parameterControl = parameterControl as unknown as FormControl; + this.groupControl = attributeControl.get(path) as FormControl; + this.parameterControl = parameterControl.get(path) as FormControl; if (!disabled) { attributeControl.disable(); From 94d0218abab9d540e2c609096764447829722582 Mon Sep 17 00:00:00 2001 From: Aleksandr Lihih Date: Sun, 21 Nov 2021 16:15:01 +0300 Subject: [PATCH 14/15] provide group checkbox disable state --- .../attributes/group-keys/group-keys-wrapper.component.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts b/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts index 8d712ee1e1..120f876ad1 100644 --- a/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts +++ b/web/src/app/shared/configuration/attributes/attributes/group-keys/group-keys-wrapper.component.ts @@ -67,6 +67,10 @@ export class GroupKeysWrapperComponent extends BaseDirective implements Attribut this.groupControl = attributeControl.get(path) as FormControl; this.parameterControl = parameterControl.get(path) as FormControl; + path.forEach((part) => { + disabled = disabled[part]; + }); + if (!disabled) { attributeControl.disable(); parameterControl.disable(); From b5e11c73241e6d71606f4f03b85f3beeee9ac39d Mon Sep 17 00:00:00 2001 From: Sealwing Date: Mon, 22 Nov 2021 10:41:41 +0500 Subject: [PATCH 15/15] Allure steps fixes in test_save_configuration_hell and timeout on save increased --- tests/ui_tests/app/page/common/base_page.py | 11 ++++++----- tests/ui_tests/test_ui_config_hell.py | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/ui_tests/app/page/common/base_page.py b/tests/ui_tests/app/page/common/base_page.py index ca0b90e726..e53f35eaf0 100644 --- a/tests/ui_tests/app/page/common/base_page.py +++ b/tests/ui_tests/app/page/common/base_page.py @@ -186,7 +186,8 @@ def _find_element(): ) element_name = element.name if isinstance(element, Locator) else element.text - return self._is_displayed(element_name, _find_element) + with allure.step(f'Check if {element_name} is displayed'): + return self._is_displayed(_find_element) def is_child_displayed(self, parent: WebElement, child: Locator, timeout: Optional[int] = None) -> bool: """Checks if child element is displayed""" @@ -194,7 +195,8 @@ def is_child_displayed(self, parent: WebElement, child: Locator, timeout: Option def _find_child(): return self.find_child(parent, child, timeout=timeout or self.default_loc_timeout) - return self._is_displayed(child.name, _find_child) + with allure.step(f'Check if {child.name} is displayed'): + return self._is_displayed(_find_child) def assert_displayed_elements(self, locators: List[Locator]) -> None: """Asserts that list of elements is displayed.""" @@ -360,11 +362,10 @@ def hover_element(self, element: Union[Locator, WebElement]): hover.perform() @staticmethod - def _is_displayed(element_name: str, find_element_func: Callable[[], WebElement]) -> bool: + def _is_displayed(find_element_func: Callable[[], WebElement]) -> bool: """Calls `is_displayed` method on element returned by passed function""" try: - with allure.step(f'Check {element_name}'): - return find_element_func().is_displayed() + return find_element_func().is_displayed() except ( TimeoutException, NoSuchElementException, diff --git a/tests/ui_tests/test_ui_config_hell.py b/tests/ui_tests/test_ui_config_hell.py index ea0bd1d600..e975e96c9a 100644 --- a/tests/ui_tests/test_ui_config_hell.py +++ b/tests/ui_tests/test_ui_config_hell.py @@ -94,10 +94,10 @@ def test_save_configuration_hell( for display_name in group_names: assert display_name in ui_display_names, f"Group named '{display_name}' should be presented in config" _fill_required_fields(config_page) - config_page.config.save_config(load_timeout=30) + config_page.config.save_config(load_timeout=40) with allure.step('Ensure page is still opened'): config_page.wait_page_is_opened(timeout=1) - with allure.step('Check that popup does not contain info about error'): + with allure.step('Check that popup is not presented on page'): assert not config_page.is_popup_presented_on_page(), 'No popup should be shown after save'