# # Copyright (C) 2018 FreeIPA Contributors see COPYING for license # """ Test LoginScreen widget and all it's views """ import urllib from ipatests.test_webui.ui_driver import UI_driver from ipatests.test_webui.ui_driver import screenshot import ipatests.test_webui.data_loginscreen as loginscreen try: from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.wait import WebDriverWait except ImportError: pass import pytest @pytest.mark.tier1 class TestLoginScreen(UI_driver): def setup(self, *args, **kwargs): super(TestLoginScreen, self).setup(*args, **kwargs) self.init_app() self.add_test_user() self.logout() def teardown(self, *args, **kwargs): # log out first if (self.logged_in()): self.logout() else: self.load_url(self.get_base_url()) # log in as administrator self.login() self.delete_test_user() super(TestLoginScreen, self).teardown(*args, **kwargs) def delete_test_user(self): """ Delete user for tests """ # User is not logged in assert self.logged_in() self.navigate_to_entity(loginscreen.ENTITY) self.delete_record(loginscreen.PKEY) def add_test_user(self): """ Add user for tests """ # User is not logged in assert self.logged_in() self.add_record(loginscreen.ENTITY, loginscreen.DATA_ITEST_USER, navigate=False) def assert_notification(self, type='success', assert_text=None, link_text=None, link_url=None): """ Assert whether we have a notification of particular type """ notification_type = 'div.validation-summary .alert-{}'.format(type) # wait for a half sec for notification to appear self.wait(0.5) is_present = self.find(notification_type, By.CSS_SELECTOR) # Notification not present assert is_present if assert_text: assert assert_text in is_present.text if link_text and link_url: link = self.find_xelement(".//a", is_present) # Text on link placed on validation widget assert link_text == link.text # URL of link placed on validation widget assert link_url == link.get_attribute('href') def find_xelement(self, expression, parent, strict=True): """ Find element by xpath related to a given parent """ return self.find(expression, By.XPATH, parent, many=False, strict=strict) def find_xelements(self, expression, parent, strict=True): """ Find elements by xpath related to the given parent """ return self.find(expression, By.XPATH, parent, many=True, strict=strict) def button_click_on_login_screen(self, name): """ Find a button with the given name on LoginScreen widget and then click on """ login_screen = self.get_login_screen() button = self.find_xelement(".//button[@name='{}']".format(name), login_screen) assert button.is_displayed() button.click() def get_input_field(self, name, parent): """ Find a input field with the given name and parent """ return self.find_xelement( ".//input[@name='{}']".format(name), parent ) def load_url(self, url): """ Navigate to Web page and wait for loading of all dependencies. """ self.driver.get(url) runner = self WebDriverWait(self.driver, 10).until( lambda d: runner.files_loaded() ) def relogin_with_new_password(self): """ Log out and then log in using a new password. It is need to check a new password """ if (self.logged_in()): self.logout() else: self.load_url(self.get_base_url()) self.login(loginscreen.PKEY, loginscreen.PASSWD_ITEST_USER_NEW) # User is not logged in assert self.logged_in() def reset_password(self, username=None, current_password=None, new_password=None, link_text=None, link_url=None): """ Reset password with the given one """ login_screen = self.get_login_screen() if username is not None: username_field = self.get_input_field('username', login_screen) cur_pass_field = self.get_input_field('current_password', login_screen) new_pass_field = self.get_input_field('new_password', login_screen) verify_pass_field = self.get_input_field('verify_password', login_screen) if username is not None: username_field.send_keys(username) cur_pass_field.send_keys(current_password) new_pass_field.send_keys(new_password) verify_pass_field.send_keys(new_password) verify_pass_field.send_keys(Keys.RETURN) self.wait() self.assert_notification(assert_text='Password change complete', link_text=link_text, link_url=link_url) def get_data_from_form_row(self, form_row): """ Parse data from the form record to a comparable structure """ result = [] label = self.find_xelement(".//label", form_row) assert label.is_displayed() result.append(label.get_attribute('name')) result.append(label.text) req = self.find_xelement("./../..", label) result.append(self.has_class(req, 'required')) field = self.find_xelement(".//input", form_row) # not editable field editable = field.is_displayed() if not editable: field = self.find_xelement(".//p", form_row) result.append(editable) assert field.is_displayed() result.append(field.get_attribute('type')) result.append(field.get_attribute('name')) if not editable: result.append(field.text) else: result.append(field.get_attribute('value')) result.append(field.get_attribute('placeholder')) return tuple(result) def get_data_from_button(self, button): """ Parse data from the button to a comparable structure """ result = [] result.append(button.get_attribute('name')) result.append(button.get_attribute('title')) return tuple(result) def assert_form_equals(self, actual_form, expected_form): """ Compare two forms """ assert len(actual_form) == len(expected_form) for act_row, exp_row in zip(actual_form, expected_form): # structure of rows # label_name, label_text, # required, editable, # input_type, input_name, # input_text, placeholder assert self.get_data_from_form_row(act_row) == exp_row def assert_buttons_equal(self, actual_buttons, expected_buttons): """ Compare button sets """ assert len(actual_buttons) == len(expected_buttons) for act_button, exp_button in zip(actual_buttons, expected_buttons): assert self.get_data_from_button(act_button) == exp_button def assert_validations_equal(self, actual_alerts, expected_alerts): """ Compare validation sets """ assert len(actual_alerts) == len(expected_alerts) for act_alert, exp_alert in zip(actual_alerts, expected_alerts): assert (act_alert.text,) == exp_alert def has_validation(self, parent): return self.find_xelement(".//div[@name='validation']", parent, strict=False) def check_elements_of_form(self, form_data): login_screen = self.get_login_screen() form = self.find_xelement(".//div[@class='form-horizontal']", login_screen) # rows form_rows = self.find_xelements( ".//div[contains(@class, 'form-group')]", form ) form_rows = [el for el in form_rows if el.is_displayed() and not self.has_validation(el)] self.assert_form_equals(form_rows, form_data['rows']) # buttons buttons = self.find_xelements(".//button", login_screen) buttons = [el for el in buttons if el.is_displayed()] self.assert_buttons_equal(buttons, form_data['buttons']) def check_alerts(self, form_data): login_screen = self.get_login_screen() # Push the the most rigth button to see the Required fields # it should be either 'Reset' or 'Reset and Login' or 'Login' button self.button_click_on_login_screen(form_data['buttons'][-1][0]) alerts = self.find_xelements( ".//*[@data-name][contains(@class, 'alert-danger')]", login_screen ) required_msgs = form_data['required_msg'] self.assert_validations_equal(alerts, required_msgs) def load_reset_and_login_view(self): self.load() assert self.login_screen_visible() username = loginscreen.PKEY current_password = loginscreen.PASSWD_ITEST_USER login_screen = self.get_login_screen() username_field = self.get_input_field('username', login_screen) cur_pass_field = self.get_input_field('password', login_screen) username_field.send_keys(username) cur_pass_field.send_keys(current_password) cur_pass_field.send_keys(Keys.RETURN) self.wait() self.assert_notification( type='info', assert_text=( 'Your password has expired. Please enter a new password.' ) ) def check_cancel(self): """ Check 'login' view after a cancel of password reset """ self.button_click_on_login_screen('cancel') self.check_elements_of_form(loginscreen.FILLED_LOGIN_FORM) @screenshot def test_reset_password_view(self): self.load_url('/'.join((self.get_base_url(), 'reset_password.html'))) assert self.login_screen_visible() self.check_elements_of_form(loginscreen.RESET_PASSWORD_FORM) self.check_alerts(loginscreen.RESET_PASSWORD_FORM) username = loginscreen.PKEY current_password = loginscreen.PASSWD_ITEST_USER new_password = loginscreen.PASSWD_ITEST_USER_NEW self.reset_password(username, current_password, new_password) self.relogin_with_new_password() @screenshot def test_reset_password_view_with_redirect(self): redir_url = self.get_base_url().lower() encoded_redir_url = urllib.parse.urlencode({'url': redir_url}) target_url = '/'.join((self.get_base_url(), 'reset_password.html?{}')) self.load_url(target_url.format(encoded_redir_url)) assert self.login_screen_visible() self.check_elements_of_form(loginscreen.RESET_PASSWORD_FORM) self.check_alerts(loginscreen.RESET_PASSWORD_FORM) username = loginscreen.PKEY current_password = loginscreen.PASSWD_ITEST_USER new_password = loginscreen.PASSWD_ITEST_USER_NEW self.reset_password(username, current_password, new_password, link_text='Continue to next page', link_url=redir_url, ) self.relogin_with_new_password() @screenshot def test_reset_password_view_with_delayed_redirect(self): redir_url = self.get_base_url().lower() + '/' encoded_redir_url = urllib.parse.urlencode( {'url': redir_url, 'delay': 5} ) target_url = '/'.join((self.get_base_url(), 'reset_password.html?{}')) self.load_url(target_url.format(encoded_redir_url)) assert self.login_screen_visible() self.check_elements_of_form(loginscreen.RESET_PASSWORD_FORM) self.check_alerts(loginscreen.RESET_PASSWORD_FORM) username = loginscreen.PKEY current_password = loginscreen.PASSWD_ITEST_USER new_password = loginscreen.PASSWD_ITEST_USER_NEW self.reset_password(username, current_password, new_password, link_text='Continue to next page', link_url=redir_url, ) self.assert_notification(type='info', assert_text='You will be redirected in ') self.wait(3) # check url after start delay timer, but before end assert self.driver.current_url != redir_url self.wait(5) assert self.driver.current_url == redir_url self.relogin_with_new_password() @screenshot def test_reset_password_and_login_view(self): self.load_reset_and_login_view() self.check_elements_of_form(loginscreen.RESET_AND_LOGIN_FORM) self.check_alerts(loginscreen.RESET_AND_LOGIN_FORM) # check click on 'Cancel' button self.check_cancel() self.button_click_on_login_screen('login') # check if user is not logged assert not self.logged_in() current_password = loginscreen.PASSWD_ITEST_USER new_password = loginscreen.PASSWD_ITEST_USER_NEW # username is already here, do not fill up self.reset_password(current_password=current_password, new_password=new_password) # waiting for auto login process self.wait(5) assert self.logged_in() # check again if new password is valid self.relogin_with_new_password() @screenshot def test_login_view(self): self.load() # check empty 'login' view self.check_elements_of_form(loginscreen.EMPTY_LOGIN_FORM) self.check_alerts(loginscreen.EMPTY_LOGIN_FORM) # check non empty 'login' view login_screen = self.get_login_screen() username_field = self.get_input_field('username', login_screen) username_field.send_keys(loginscreen.PKEY) self.wait(0.5) self.check_elements_of_form(loginscreen.LOGIN_FORM) self.check_alerts(loginscreen.LOGIN_FORM)