diff --git a/lib/webui.pm b/lib/webui.pm new file mode 100644 index 00000000..2f843e19 --- /dev/null +++ b/lib/webui.pm @@ -0,0 +1,456 @@ +package webui; + +use strict; + +use base 'Exporter'; +use Exporter; + +use testapi; +use utils; +use bugzilla; + +our @EXPORT = qw/select_disks webui_add_partition custom_blivet_format_partition custom_blivet_resize_partition custom_change_type custom_change_fs custom_change_device custom_delete_part get_full_repo get_mirrorlist_url crash_anaconda_text report_bug_text/; + +sub select_disks { + # Handles disk selection for the new Cockpit based WebUI. + # It has one optional argument - the number of disks to select. + # Should be run when main WebUI hub is displayed. Enters + # disk selection spoke and then ensures that required number + # of disks are selected. + # Example usage: + # after calling `select_disks(2);` from WebUI main hub, + # installation destination spoke will be displayed and two + # attached disks will be selected for installation. + my %args = ( + disks => 1, + iscsi => {}, + @_ + ); + my %iscsi = %{$args{iscsi}}; + # WebUI hub + assert_screen "webui_cockpit_installation_method", 300; + + # this is awkward, but on the install_repository_hd_variation test, + # we have two disks but on F39+ anaconda knows we're using one of + # them as an install source and 'protects' it (doesn't show it on + # INSTALLATION DESTINATION), so we need to go down the single disk + # branch in that case. Once F38 is EOL we could potentially tweak + # this to use a dedicated var or something + my $relnum = get_release_number; + if (get_var('NUMDISKS') > 1 && !(get_var('TEST') eq 'install_repository_hd_variation' && $relnum > 38)) { + # Multi-disk case. Select however many disks the test needs. If + # $disks is 0, this will do nothing, and 0 disks will be selected. + for my $n (1 .. $args{disks}) { + assert_and_click "webui_cockpit_select_disk_dropdown"; + assert_and_click "webui_cockpit_select_disk_$n"; + } + } + else { + # Single disk case. + # The Cockpit WebUI cannot deselect disks. + if ($args{disks} == 0) { + # Clicking will *de*-select. + # assert_and_click "anaconda_install_destination_select_disk_1"; + diag("The test has attempted to deselect the installation disk, however this is not supported."); + } + elsif ($args{disks} > 1) { + die "Only one disk is connected! Cannot select $args{disks} disks."; + } + # For exactly 1 disk, we don't need to do anything. + } + + # Handle network disks. + if (%iscsi) { + assert_and_click "webui_cockpit_modify_storage"; + foreach my $target (keys %iscsi) { + my $ip = $iscsi{$target}->[0]; + my $user = $iscsi{$target}->[1]; + my $password = $iscsi{$target}->[2]; + assert_and_click "webui_cockpit_blue_menu_button"; + assert_and_click "webui_cockpit_add_iscsi_portal"; + wait_still_screen 2; + type_safely $ip; + send_key "tab"; + type_safely $user; + send_key "tab"; + type_safely "password"; + assert_and_click "webui_cockpit_next_button_blue" + # type_safely $target; + # # start discovery - three tabs, enter + # type_safely "\t\t\t\n"; + # if ($user && $password) { + # assert_and_click "anaconda_install_destination_target_auth_type"; + # assert_and_click "anaconda_install_destination_target_auth_type_chap"; + # send_key "tab"; + # type_safely $user; + # send_key "tab"; + # type_safely $password; + # } + # assert_and_click "anaconda_install_destination_target_login"; + # assert_and_click "anaconda_install_destination_select_target"; + } + #assert_and_click "anaconda_spoke_done"; + } + +} + +sub modify_storage { + # This subroutine opens the Configure storage page. + # It should run, when we are in the main webui menu, + # on the Installation method tab. + assert_and_click("webui_cockpit_modify_storage"); + assert_screen("webui_cockpit_modify_storage_active"); +} + +sub create_partition_table { + # This will create a partition table, if no partition + # table has been created yet. This should run as a + # first function before partitions are added. + # It takes two arguments, $type (gpt or mbr) to create + # a corresponding partition style, and $zero (0 or 1). + # If $zero is 1, the created partition will be overwritten + # with zeros. + my ($type, $zero) = @_; + # Normal waittime is 30. + my $waittime = 30; + # Open the disk submenu. + assert_and_click("webui_cockpit_disk_submenu"); + # Click on Create partition table + assert_and_click("webui_cockpit_create_parttable"); + # Check that the dialog has opened. + assert_screen("webui_cockpit_initialize_disk_shown"); + # Click on the dropdown menu. + assert_and_click("webui_cockpit_dropdown_arrow"); + # Select partition type + assert_and_click("webui_cockpit_parttable_$type"); + # Zerowrite, if user wants it. + if ($zero) { + assert_and_click("webui_cockpit_overwrite_zeros"); + # Increase waittime to give space. + $waittime = 300; + } + # Confirm + assert_and_click("webui_cockpit_button_initialize"); + # Wait until come back to the Configure storage screen + assert_screen("webui_cockpit_modify_storage_active", timeout => $waittime); +} + +sub add_partition { + # Used to add partition on WebUI partitioning screen. + # Should be called when the Configure storage is displayed and disk(s) are selected. + # You can pass device type for partition (needle tagged anaconda_blivet_devicetype_$devicetype should exist), + # whether partitions should be of RAID1 (devicetype is then automatically handled) - you then + # need to have two disks added, size of that partition in MiBs, desired filesystem of that partition + # (anaconda_blivet_part_fs_$filesystem should exist) and mountpoint of that partition (e. g. string "/boot"). + my %args = ( + devicetype => "", + raid1 => 0, + size => 0, + filesystem => "", + mountpoint => "", + units => "g", + name => "", + deep => 0, + encrypt => 0, + @_ + ); + my $waittime = 30; + $args{devicetype} = "raid" if $args{raid1}; + + # Currently, no partitions can be created when I select two disks + # and create a raid device, therefore only add partitions + # when we are in a non-raid mode. + + # Let's check if there is Free space on the disk, if not + # we will die. + unless (check_screen("webui_cockpit_disk_free_space")) { + die("There is no free space on the disk, I cannot create any partitions."); + } + unless ($args{devicetype} eq "raid") { + # Open the submenu + assert_and_click("webui_cockpit_partitioning_freespace"); + # If no previous partitions have been created, we need to + # create the partition table on the empty disk. + assert_and_click("webui_cockpit_create_partition"); + + # We start the dialogue with the cursor being placed + # in the name field, so if $name, we'll type it. If nothing + # is specified, the defaults are empty strings, so we can type + # it anyway. Then press tab to move away. + type_very_safely($args{name}); + send_key("tab"); + # If mountpoint is set, we'll set it here + type_very_safely($args{mountpoint}); + send_key("tab"); + # Select device type + if ($args{devicetype}) { + # The focus should be placed on that field already, + # so we can open the dropdown using the space key. + send_key("space"); + assert_and_click "webui_cockpit_devicetype_$args{devicetype}"; + } + # To come to the size field, we will need to press "tab" twice. + send_key("tab"); + sleep(1); + send_key("tab"); + type_very_safely($args{size}); + # Type in units + unless ($args{units} ne "g") { + send_key("tab"); + send_key($args{units}); + } + # If user wants to zerowrite the partition + if ($deep) { + assert_and_click("webui_cockpit_overwrite_zeros"); + $waittime = 300; + } + # If user wants encryption + if ($encryption) { + assert_and_click("webui_cockpit_no_encryption"); + assert_and_click("webui_cockpit_encrypt_$encryption"); + } + # Create the partition + assert_and_click("webui_cockpit_button_create"); + # Check that we have come back to the Storage page. + assert_screen("webui_cockpit_modify_storage_active"); + } +} + +sub custom_blivet_format_partition { + # This subroutine formats a selected partition. To use it, you must select the + # partition by other means before you format it using this routine. + # You have to create a needle for any non-existing filesystem that is + # passed via the $type, such as anaconda_blivet_part_fs_ext4. + my %args = @_; + # Start editing the partition and select the Format option + assert_and_click "anaconda_blivet_part_edit"; + assert_and_click "anaconda_blivet_part_format"; + # Select the appropriate filesystem type. + assert_and_click "anaconda_blivet_part_drop_select"; + assert_and_click "anaconda_blivet_part_fs_$args{type}"; + wait_still_screen 2; + # Fill in the label if needed. + send_key "tab"; + if ($args{label}) { + type_very_safely $args{label}; + } + # Fill in the mountpoint. + send_key "tab"; + type_very_safely $args{mountpoint}; + assert_and_click "anaconda_blivet_part_format_button"; +} + +sub custom_blivet_resize_partition { + # This subroutine resizes the selected (active) partition to a given value. Note, that + # if the selected value is bigger than the available space, it will only be + # resized to fill up the available space no matter the number. + # This routine cannot will not be able to select a particular partition!!! + my %args = @_; + # Start editing the partition and select the Resize option + assert_and_click "anaconda_blivet_part_edit"; + assert_and_click "anaconda_blivet_part_resize"; + # Select the appropriate units. Note, that there must a be needle existing + # for each possible unit that you might want to use, such as + # "anaconda_blivet_size_unit_gib". + assert_and_click "anaconda_blivet_part_drop_select"; + assert_and_click "anaconda_blivet_size_unit_$args{units}"; + # Move back to the value field. + send_key "shift-tab"; + # Type in the new size. + type_very_safely $args{size}; + assert_and_click "anaconda_blivet_part_resize_button"; +} + + +sub custom_change_type { + # Used to set different device types for specified partition (e.g. + # RAID). Should be called when custom partitioning spoke is + # displayed. Pass it type of partition and name of partition. + # Needles `anaconda_part_select_$part` and + # `anaconda_part_device_type_$type` should exist. Example usage: + # `custom_change_type("raid", "root");` uses + # `anaconda_part_select_root` and `anaconda_part_device_type_raid` + # needles to set RAID for root partition. + my ($type, $part) = @_; + $part ||= "root"; + assert_and_click "anaconda_part_select_$part"; + assert_and_click "anaconda_part_device_type"; + # Move the mouse away from the menu + mouse_set(10, 10); + assert_and_click "anaconda_part_device_type_$type"; + assert_and_click "anaconda_part_update_settings"; + wait_still_screen 5; +} + +sub custom_change_fs { + # Used to set different file systems for specified partition. + # Should be called when custom partitioning spoke is displayed. + # Pass filesystem name and name of partition. Needles + # `anaconda_part_select_$part` and `anaconda_part_fs_$fs` should + # exist. Example usage: + # `custom_change_fs("ext4", "root");` uses + # `anaconda_part_select_root` and `anaconda_part_fs_ext4` needles + # to set ext4 file system for root partition. + my ($fs, $part) = @_; + $part ||= "root"; + assert_and_click "anaconda_part_select_$part"; + wait_still_screen 5; + # if fs is already set correctly, do nothing + return if (check_screen "anaconda_part_fs_${fs}_selected", 5); + assert_and_click "anaconda_part_fs"; + # Move the mouse away from the menu + mouse_set(10, 10); + assert_and_click "anaconda_part_fs_$fs"; + assert_and_click "anaconda_part_update_settings"; + wait_still_screen 5; +} + +sub custom_change_device { + my ($part, $devices) = @_; + assert_and_click "anaconda_part_select_$part"; + assert_and_click "anaconda_part_device_modify"; + foreach my $device (split(/ /, $devices)) { + assert_and_click "anaconda_part_device_${device}"; + } + assert_and_click "anaconda_part_device_select"; + assert_and_click "anaconda_part_update_settings"; + wait_still_screen 5; +} + +sub custom_delete_part { + # Used for deletion of previously added partitions in custom + # partitioning spoke. Should be called when custom partitioning + # spoke is displayed. Pass the partition name. Needle + # `anaconda_part_select_$part` should exist. Example usage: + # `custom_delete_part('swap');` uses needle + # `anaconda_part_select_swap` to delete previously added swap + # partition. + my ($part) = @_; + return if not $part; + assert_and_click "anaconda_part_select_$part"; + assert_and_click "anaconda_part_delete"; +} + +sub get_full_repo { + my ($repourl) = @_; + # trivial thing we kept repeating: fill out an HTTP or HTTPS + # repo URL with flavor and arch, leave hd & NFS ones alone + # (as for those tests we just use a mounted ISO and URL is complete) + if ($repourl !~ m/^(nfs|hd:)/) { + my $arch = get_var("ARCH"); + $repourl .= "/Everything/$arch/os"; + } + return $repourl; +} + +sub get_mirrorlist_url { + return "mirrors.fedoraproject.org/mirrorlist?repo=fedora-" . lc(get_var("VERSION")) . "&arch=" . get_var('ARCH'); +} + +sub crash_anaconda_text { + # This routine uses the WebUI crash trigger to break the ongoing WebUI installation to simulate + # an WebUI crash and runs a series of steps that results in creating a bug in Bugzilla. + # It is used in the `install_text.pm` test and can be switched on by using the CRASH_REPORT + # variable set to 1. + # + # tty3 has a shell on all f31+ installer and live images + select_console "tty3-console"; + assert_screen("anaconda_text_install_shell"); + # We use the trigger command to do the simulated crash. + type_string "kill -USR1 `cat /var/run/anaconda.pid`\n"; + # And navigate back to the main panel of WebUI. This should require + select_console "tty1-console"; + assert_screen("anaconda_text_install_main"); + # We wait until the crash menu appears. This usually takes some time, + # so let's try for 300 seconds, this should be long enough. + my $trials = 1; + until (check_screen("anaconda_text_crash_menu_ready") || $trials > 30) { + sleep 10; + ++$trials; + } + # If the crash menu never appears, let's assert it to fail. + if ($trials > 30) { + assert_screen("anaconda_text_crash_menu_ready"); + } + +} + +sub report_bug_text { + # This routine handles the Bugzilla reporting after a simulated crash on + # a textual console. + # We will not create a needle for every menu item, and we will fail, + # if there will be no positive Bugzilla confirmation shown at the end + # of the process and then we will fail. + # + # Let us record the time of this test run. Later, we will use it to + # limit the Bugzilla search. + my $timestamp = time(); + # + # First, collect the credentials. + my $login = get_var("BUGZILLA_LOGIN"); + my $password = get_var("_SECRET_BUGZILLA_PASSWORD"); + my $apikey = get_var("_SECRET_BUGZILLA_APIKEY"); + # Choose item 1 - Report the bug. + type_string "1\n"; + sleep 2; + # Choose item 1 - Report to Bugzilla + type_string "1\n"; + sleep 5; + # Do login. + type_string $login; + type_string "\n"; + sleep 5; + # Enter the name of the Zilla. + type_password $password; + type_string "\n"; + sleep 10; + # Save the report without changing it. + # It would need some more tweaking to actually type into the report, but since + # it is reported even if unchanged, we leave it as such. + type_string ":wq\n"; + # Wait until the Crash menu appears again. + # The same screen shows the result of the Bugzilla operation, + # so if the needle matches, the bug has been created in Bugzilla. + # Bugzilla connection is slow so we need to wait out some time, + # therefore let's use a cycle that will check each 10 seconds and + # ends if there is no correct answer from Bugzilla in 120 seconds. + my $counter = 0; + until (check_screen("anaconda_text_bug_reported") || $counter > 12) { + sleep 10; + ++$counter; + } + # Sometimes, Bugzilla throws out a communication error although the bug has been + # created successfully. If this happens, we will softfail and leave the creation + # check to a later step. + if ($counter > 12) { + record_soft_failure "Warning: Bugzilla has reported an error which could mean that the bug has not been created correctly, but it probably is not a real problem, if the test has not failed completely. "; + } + + # Now, let us check with Bugzilla directly, if the bug has been created. + # First, we shall get a Bugzilla format timestamp to use it in the query. + # The timestamp will limit the list of bugs to those that have been created since + # the then -> resulting with high probability in the one that this test run + # has just created. + $timestamp = convert_to_bz_timestamp($timestamp); + # Then we fetch the latest bug from Bugzilla. + my $lastbug = get_newest_bug($timestamp, $login); + unless ($lastbug) { + die "Bugzilla returned no newly created bug. It seems that the bug has not been created."; + } + else { + print("BUGZILLA: The last bug was found: $lastbug\n"); + } + # We have found that the bug indeed is in the bugzilla (otherwise + # we would have died already) so now we close it to clean up after this test run. + my $result = close_notabug($lastbug, $apikey); + unless ($result) { + record_soft_failure "The bug has not been closed for some reason. Check manually."; + } + else { + print("BUGZILLA: The last bug $lastbug changed status to CLOSED.\n"); + } + + # Quit anaconda + type_string "4\n"; + +} diff --git a/tests/disk_cockpit_btrfs.pm b/tests/disk_cockpit_btrfs.pm new file mode 100644 index 00000000..37b234e8 --- /dev/null +++ b/tests/disk_cockpit_btrfs.pm @@ -0,0 +1,32 @@ +use base "anacondatest"; +use strict; +use testapi; +use utils; +use webui; + +sub run { + my $self = shift; + # Go to INSTALLATION DESTINATION and ensure the disk is selected. + # Because PARTITIONING starts with 'custom_', this will select custom. + select_disks(); + assert_and_click "anaconda_spoke_done"; + + # Manual partitioning spoke should be displayed. Select BTRFS + # partitioning scheme + custom_scheme_select("btrfs"); + assert_and_click "anaconda_part_automatic"; + assert_and_click "anaconda_spoke_done"; + assert_and_click "anaconda_part_accept_changes"; + + # Anaconda hub + assert_screen "anaconda_main_hub", 300; + +} + +sub test_flags { + return {fatal => 1}; +} + +1; + +# vim: set sw=4 et: