#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/../common/pywrap", sys.argv)## Copyright (C) 2013 Red Hat, Inc.# SPDX-License-Identifier: LGPL-2.1-or-laterimport timeimport testlib@testlib.skipBeiboot("no local overrides/config in beiboot mode")@testlib.nondestructiveclass TestMenu(testlib.MachineCase):def testDarkThemeSwitcher(self):b = self.browserdef switch_style(style_sel, prevPage="system", pixel_test=False):b.switch_to_top()b.click("#toggle-menu")if pixel_test:b.wait_visible("#toggle-menu-menu")b.assert_pixels_in_current_layout("#toggle-menu-menu", "shell-toggle-menu")b.click(style_sel)b.enter_page(f"/{prevPage}")self.login_and_go("/system")switch_style("#dark", pixel_test=True)b._wait_present("html.pf-v6-theme-dark")switch_style("#light")b.wait_not_present("html.pf-v6-theme-dark")# Test overriding, switching only works on Chromiumif b.browser == "chromium":# Light theme overrides browser defaultsb._set_emulated_media_theme("dark")b.wait_not_present("html.pf-v6-theme-dark")switch_style("#auto")b._wait_present("html.pf-v6-theme-dark")b._set_emulated_media_theme("light")b.wait_not_present("html.pf-v6-theme-dark")switch_style("#dark")b._wait_present("html.pf-v6-theme-dark")def testBasic(self):b = self.browserm = self.machine# Add a link with a hash in it to test that this worksm.execute("mkdir -p /usr/local/share/cockpit/systemd; cp -rp /usr/share/cockpit/systemd/* /usr/local/share/cockpit/systemd")m.execute("""sed -i '/"menu"/a "memory": { "label": "Memory", "path": "#/memory" },' /usr/local/share/cockpit/systemd/manifest.json""")self.addCleanup(m.execute, "rm -r /usr/local/share/cockpit/systemd")self.login_and_go("/system")b.switch_to_top()b.click('#toggle-docs')b.click('button:contains("About Web Console")')b.wait_in_text('#about-cockpit-modal', 'Cockpit is an interactive Linux server admin interface')if not m.ostree_image:pkgname = "cockpit" if m.image == "arch" else "cockpit-bridge"b.wait_visible(f'#about-cockpit-modal:contains("{pkgname}")')b.assert_pixels('.pf-v6-c-modal-box:has(#about-cockpit-modal)', "about-cockpit-modal",mock={".pf-v6-c-content--dd": "v1234"})b.click('.pf-v6-c-about-modal-box__close button')b.wait_not_present('#about-cockpit-modal')# Clicking inside the iframed pages should close the docs menub.click("#toggle-docs")b.wait_visible("#toggle-docs-menu")b.assert_pixels("#toggle-docs-menu", "shell-docs-menu")b.enter_page("/system")b.focus("#overview main")b.switch_to_top()b.wait_not_present("#toggle-docs-menu")# Check that we can use a link with a hash in itb.click_system_menu("/system/#/memory")# Ensure that our tests pick up unhandled JS exceptionsb.switch_to_top()b.go("/playground/exception")# Test that subpages are correctly shown in the navigation (twice - once that only one page is shown as active)b.wait_in_text("#host-apps .pf-m-current", "Development")b.enter_page("/playground/exception")b.click("button")# UI should show the crashb.switch_to_top()b.wait_visible("#navbar-oops")# normally called at the end of the test, should fail due to the oopswith self.assertRaisesRegex(AssertionError, "Cockpit shows an Oops"):self.check_browser_errors()# don't actually fail this testb.allow_oops = Truedef testSessionTimeout(self):b = self.browserm = self.machinem.execute("printf '[Session]\nIdleTimeout = 1\n' >> /etc/cockpit/cockpit.conf")# does not time out immediatelyself.login_and_go()time.sleep(20)self.assertFalse(b.is_present("#session-timeout-modal"))b.wait_visible("#hosts-sel")# a mouse event resets the timerb.enter_page("/system")b.mouse("#system_information_hardware_text", "mousemove", 24, 24)b.switch_to_top()# 30s before the 1 min timeout the dialog pops uptime.sleep(35)with b.wait_timeout(3):b.wait_visible("#session-timeout-modal")self.assertGreater(int(b.text("#session-timeout-modal .pf-v6-c-modal-box__body").split()[-2]), 15)# click on "Continue session"b.click("#session-timeout-modal footer button")b.wait_not_present("#session-timeout-modal")# now wait for timeout dialog again, but don't click; instead, wait for the full minutetime.sleep(30)with b.wait_timeout(8):b.wait_popup("session-timeout-modal")self.assertGreater(int(b.text("#session-timeout-modal .pf-v6-c-modal-box__body").split()[-2]), 20)time.sleep(30)# that logs you outb.wait_visible("#login")b.wait_visible("#login-info-message")b.wait_text("#login-info-message", "You have been logged out due to inactivity.")@testlib.skipImage("TODO: fix for OpenSUSE", "opensuse-tumbleweed")def testKeyboardNavigation(self):b = self.browserself.login_and_go()# initially shows host switcherb.wait_visible("#hosts-sel")self.assertEqual(b.eval_js("document.activeElement.tagName"), "BODY")# press Tab once → "Skip to content" skiplinkb.key("Tab")b.wait_js_cond("document.activeElement.parentElement.classList.contains('pf-v6-c-skip-to-content')")self.assertEqual(b.eval_js("document.activeElement.textContent"), "Skip to content")self.assertEqual(b.eval_js("document.activeElement.getAttribute('href')"), "#content")b.key("Enter")b.wait_js_cond("document.activeElement.getAttribute('id') === 'content'")self.assertEqual(b.eval_js("document.activeElement.tagName"), "DIV")self.assertEqual(b.eval_js("document.activeElement.getAttribute('id')"), "content")# resetb.reload()b.wait_visible("#hosts-sel")b.wait_text("#super-user-indicator", "Administrative access")# press Tab twice → "Skip main navigation" skiplinkb.key("Tab")self.assertEqual(b.eval_js("document.activeElement.textContent"), "Skip to content")b.key("Tab")self.assertEqual(b.eval_js("document.activeElement.textContent"), "Skip main navigation")b.key("Enter")b.wait_js_cond("document.activeElement.tagName === 'NAV'")self.assertEqual(b.eval_js("document.activeElement.getAttribute('id')"), "hosts-sel")# actually skips the page menu and goes on to top nav bar; where exactly depends on whether# the host switcher is enabled.b.key("Tab")if self.multihost_enabled:b.wait_js_cond("document.activeElement.getAttribute('id') === 'host-toggle'")b.key("Tab")b.wait_js_cond("document.activeElement.textContent === 'Administrative access'")# resetb.reload()b.wait_visible("#hosts-sel")# without skip links, tabbing goes through page menub.key("Tab", repeat=5)# different browsers behave a bit different here -- Firefox is at "Overview", Chromium at "Logs"b.wait_js_cond("document.activeElement.classList.contains('pf-v6-c-nav__link')")def testCustomLandingPage(self):b = self.browserm = self.machine# # Make the logs page the default pagem.write("/etc/cockpit/systemd.override.json", '{"menu":{"index":{"order":100},"logs":{"order":1}}}')self.login_and_go()b.wait_js_cond('window.location.pathname == "/system/logs"')if __name__ == '__main__':testlib.test_main()