1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/../common/pywrap", sys.argv)
# This file is part of Cockpit.
#
# Copyright (C) 2013 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <https://www.gnu.org/licenses/>.
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.")
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()
|