#!/usr/bin/python3 # checkpoint: # 1. export blueprint package list (copy) # 2. blueprint description edit/update # 3. hostname setting # 4. user setting import crypt import re import composerlib import testlib @testlib.nondestructive class TestCustom(composerlib.ComposerCase): def testExport(self): b = self.browser m = self.machine self.login_and_go("/composer", superuser=True) b.wait_present("#main") # go to blueprint openssh-server b.click("#openssh-server-name") # export package list actions_drop_down_sel = ".cmpsr-header__actions #dropdownKebab" with b.wait_timeout(300): b.click(actions_drop_down_sel) b.wait_attr(actions_drop_down_sel, "aria-expanded", "true") b.click("a:contains('Export')") b.wait_present("#cmpsr-modal-export") with b.wait_timeout(300): b.wait_in_text("#textInput2-modal-markup", "openssh-server") packages_list = b.text("#textInput2-modal-markup") b.click("#cmpsr-modal-export button[data-btn=copy-export]") b.click("#cmpsr-modal-export button[data-btn=close-export]") b.wait_not_present("#cmpsr-modal-export") list_from_backend = m.execute(""" composer-cli blueprints depsolve openssh-server | tail -n +2 """).split() # remove tailing ".x86_64" or ".noarch"(6 chars) or ".aarch64"(7 chars) # and "*:" in libpcap-14:1.9.1-2.fc31.x86_64 regex = re.compile("-[0-9]*:") def remove(x): if x.endswith("aarch64"): return regex.sub("-", x[:-8]) else: return regex.sub("-", x[:-7]) format_list = map(remove, list_from_backend) self.assertEqual(packages_list, "\n".join(format_list)) # collect code coverage result self.check_coverage() def testDescription(self): b = self.browser m = self.machine self.login_and_go("/composer", superuser=True) b.wait_present("#main") # go to blueprint openssh-server b.click("#openssh-server-name") # update description (cancel first) updated_description = "edit ssh server image description" with b.wait_timeout(300): b.click("a:contains('ssh server image')") b.wait_present("#cmpsr-modal-edit-description") b.set_input_text("#textInput-modal-markup", updated_description) b.click("#cmpsr-modal-edit-description button:contains('Cancel')") b.wait_not_present("#cmpsr-modal-edit-description") b.wait_present("a:contains('ssh server image')") # update description actions_drop_down_sel = ".cmpsr-header__actions #dropdownKebab" b.click(actions_drop_down_sel) b.wait_attr(actions_drop_down_sel, "aria-expanded", "true") b.click("a:contains('Edit description')") b.wait_present("#cmpsr-modal-edit-description") b.set_input_text("#textInput-modal-markup", updated_description) b.click("#cmpsr-modal-edit-description button:contains('Save')") b.wait_not_present("#cmpsr-modal-edit-description") b.wait_present("a:contains('{}')".format(updated_description)) # backend got updated as well desc = m.execute(""" composer-cli blueprints show openssh-server | grep description | \ awk -F '"' '{print $2}' """).rstrip() self.assertEqual(desc, updated_description) # collect code coverage result self.check_coverage() def testHostname(self): b = self.browser m = self.machine self.login_and_go("/composer", superuser=True) b.wait_present("#main") b.click("#openssh-server-name") with b.wait_timeout(300): b.click("#blueprint-tabs-tab-customizations") # set hostname to "openssh-server" b.click("button[aria-describedby=Hostname-help2]") b.set_input_text("input[aria-label=Hostname]", "hostname-openssh-server") b.click(".form-control-pf-save") b.wait_present("span:contains(hostname-openssh-server)") hostname = m.execute(""" composer-cli blueprints show openssh-server | grep hostname | awk -F '"' '{print $2}' """).rstrip() b.wait_present("span:contains({})".format(hostname)) # update hostname b.click(".form-control-pf-value") b.set_input_text("input[aria-label=Hostname]", "update-openssh-server") b.key_press("\r") b.wait_present("span:contains(update-openssh-server)") hostname = m.execute(""" composer-cli blueprints show openssh-server | grep hostname | awk -F '"' '{print $2}' """).rstrip() b.wait_present("span:contains({})".format(hostname)) # clicking x button will not update b.click(".form-control-pf-value") b.set_input_text("input[aria-label=Hostname]", "no-update-openssh-server") b.click(".form-control-pf-cancel") b.wait_present("span:contains(update-openssh-server)") hostname = m.execute(""" composer-cli blueprints show openssh-server | grep hostname | awk -F '"' '{print $2}' """).rstrip() b.wait_present("span:contains({})".format(hostname)) # invalid character, save button is disabled b.click(".form-control-pf-value") b.set_input_text("input[aria-label=Hostname]", "?") b.wait_attr(".form-control-pf-save", "disabled", "") # collect code coverage result self.check_coverage() def testUserAccount(self): b = self.browser m = self.machine # HACK: cockpit-composer may loose keystrokes, see issue #944 def set_input_value(sel, val): w = "" for k in val: w += k while b.val(sel) != w: b.focus(sel) b.key_press(k) self.login_and_go("/composer", superuser=True) b.wait_present("#main") b.click("#openssh-server-name") with b.wait_timeout(300): b.click("#blueprint-tabs-tab-customizations") # add a new user b.click("button:contains('Create User Account')") b.wait_present("#cmpsr-modal-user-account") set_input_value("#textInput2-modal-user", "x y") b.wait_val("#textInput1-modal-user", "xy") b.click("input[type=checkbox]") set_input_value("#textInput1-modal-password", "fooBAR!@#123") set_input_value("#textInput2-modal-password", "fooBAR!@#123") ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUOtNJdBEXyKxBB898rdT54ULjMGuO6v4" \ "jLXmRsdRhR5Id/lKNc9hsdioPWUePgYlqML2iSV72vKQoVhkyYkpcsjr3zvBny9+5xej3+T" \ "BLoEMAm2hmllKPmxYJDU8jQJ7wJuRrOVOnk0iSNF+FcY/yaQ0owSF02Nphx47j2KWc0IjGG" \ "lt4fl0fmHJuZBA2afN/4IYIIsEWZziDewVtaEjWV3InMRLllfdqGMllhFR+ed2hQz9PN2Qc" \ "apmEvUR4UCy/mJXrke5htyFyHi8ECfyMMyYeHwbWLFQIve4CWix9qtksvKjcetnxT+WWrut" \ "dr3c9cfIj/c0v/Zg/c4zETxtp cockpit-test" b.set_input_text("#textInput5-modal-user", ssh_key) b.click("#cmpsr-modal-user-account button:contains('Create')") b.wait_not_present("#cmpsr-modal-user-account") b.wait_text("tr td[data-label='Full name']", "x y") b.wait_text("tr td[data-label='User name']", "xy") b.wait_present("tr td[data-label='Server administrator'] .fa-check") b.wait_present("tr td[data-label=Password] .fa-check") b.wait_in_text("tr td[data-label='SSH key']", "cockpit-test") # check backend to have correct password configured passwd_str = m.execute(""" composer-cli blueprints show openssh-server | grep password | awk -F '"' '{print $2}' """).rstrip() salt = passwd_str[:passwd_str.rfind("$")] passwd_crypt = crypt.crypt("fooBAR!@#123", salt) self.assertEqual(passwd_str, passwd_crypt) # check backend to have correct ssh key configured ssh_key_backend = m.execute(""" composer-cli blueprints show openssh-server | grep key | awk -F '"' '{print $2}' """).rstrip() self.assertEqual(ssh_key_backend, ssh_key) # update password new_password = "c456rty$%^RTY" b.click("button[aria-label='Edit User Account xy']") b.wait_present("#cmpsr-modal-user-account") b.click("button:contains('Set New Password')") set_input_value("#textInput1-modal-password", new_password) set_input_value("#textInput2-modal-password", new_password) b.click("#cmpsr-modal-user-account button:contains('Update')") b.wait_not_present("#cmpsr-modal-user-account") b.wait_present("tr td[data-label=Password] .fa-check") passwd_str = m.execute(""" composer-cli blueprints show openssh-server | grep password | awk '{print $3}' """).strip('"\n') salt = passwd_str[:passwd_str.rfind("$")] passwd_crypt = crypt.crypt(new_password, salt) self.assertEqual(passwd_str, passwd_crypt) # remove password b.click("button[aria-label='Edit User Account xy']") b.wait_present("#cmpsr-modal-user-account") b.click("button:contains('Remove Password')") b.click("#cmpsr-modal-user-account button:contains('Update')") b.wait_not_present("#cmpsr-modal-user-account") b.wait_present("tr td[data-label=Password]") b.wait_not_present("tr td[data-label=Password] .fa-check") # duplicated user b.click("button:contains('Create User Account')") b.wait_present("#cmpsr-modal-user-account") set_input_value("#textInput2-modal-user", "x y") b.wait_val("#textInput1-modal-user", "xy") b.wait_in_text("#textInput1-modal-user-help1", "This user name already exists.") b.click("#cmpsr-modal-user-account button:contains('Cancel')") b.wait_not_present("#cmpsr-modal-user-account") # password checking error b.click("button:contains('Create User Account')") b.wait_present("#cmpsr-modal-user-account") set_input_value("#textInput2-modal-user", "admin user") b.wait_val("#textInput1-modal-user", "auser") b.click("input[type=checkbox]") set_input_value("#textInput1-modal-password", "aaa") set_input_value("#textInput2-modal-password", "bbb") b.wait_in_text("#textInput2-modal-password-help", "The values entered for password do not match.") b.click("#cmpsr-modal-user-account .close") b.wait_not_present("#cmpsr-modal-user-account") # delete user delete_drop_down_sel = "button[aria-label='User Account Actions xy']" b.click(delete_drop_down_sel) b.wait_attr(delete_drop_down_sel, "aria-expanded", "true") b.click("a:contains('Delete User Account')") b.wait_not_present("table") # collect code coverage result self.check_coverage() if __name__ == '__main__': testlib.test_main()