#!/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-later import time import testlib @testlib.skipBeiboot("no local overrides/config in beiboot mode") @testlib.nondestructive class TestMenu(testlib.MachineCase): def testDarkThemeSwitcher(self): b = self.browser def 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 Chromium if b.browser == "chromium": # Light theme overrides browser defaults b._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.browser m = self.machine # Add a link with a hash in it to test that this works m.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 menu b.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 it b.click_system_menu("/system/#/memory") # Ensure that our tests pick up unhandled JS exceptions b.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 crash b.switch_to_top() b.wait_visible("#navbar-oops") # normally called at the end of the test, should fail due to the oops with self.assertRaisesRegex(AssertionError, "Cockpit shows an Oops"): self.check_browser_errors() # don't actually fail this test b.allow_oops = True def testSessionTimeout(self): b = self.browser m = self.machine m.execute("printf '[Session]\nIdleTimeout = 1\n' >> /etc/cockpit/cockpit.conf") # does not time out immediately self.login_and_go() time.sleep(20) self.assertFalse(b.is_present("#session-timeout-modal")) b.wait_visible("#hosts-sel") # a mouse event resets the timer b.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 up time.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 minute time.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 out b.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.browser self.login_and_go() # initially shows host switcher b.wait_visible("#hosts-sel") self.assertEqual(b.eval_js("document.activeElement.tagName"), "BODY") # press Tab once → "Skip to content" skiplink b.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") # reset b.reload() b.wait_visible("#hosts-sel") b.wait_text("#super-user-indicator", "Administrative access") # press Tab twice → "Skip main navigation" skiplink b.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'") # reset b.reload() b.wait_visible("#hosts-sel") # without skip links, tabbing goes through page menu b.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.browser m = self.machine # # Make the logs page the default page m.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()