# Integration Tests of CockpitThis directory contains automated integration tests for Cockpit, and thesupport files for them. The architecture of the automated integration tests isdescribed in [ARCHITECTURE](./ARCHITECTURE.md)To run the tests on Fedora, refer to the [HACKING](../HACKING.md) guide forinstallation of all of the necessary build and test dependencies. There'sno need to trigger a build manually - the test suite preparation step belowwill handle that.If test failures are encountered that look like they may be related to problemswith nested virtualization, refer to[this Fedora guide](https://docs.fedoraproject.org/en-US/quick-docs/using-nested-virtualization-in-kvm/index.html)for more details and recommendations on ensuring it is enabled correctly.## Preparation and general invocation*Warning*: Never run the build, test, or any other command here as root!You first need to build cockpit, and install it into a VM:test/image-prepareThis uses the default OS image. See `$TEST_OS` below how to select a different one.See `test/image-prepare --help` for some special modes, like skipping unittests, building overlays, or preparing an image with the[cockpit/ws container](https://quay.io/repository/cockpit/ws) instead of RPMs.See our scenarios in [test/run](./run) for how they are being used.In most cases you want to run an individual test in a suite, for example:test/verify/check-metrics TestCurrentMetrics.testCPUYou can get a list of tests by inspecting the `def test*` in the source, or byrunning the suite with `-l`/`--list`:test/verify/check-metrics -lSometimes you may also want to run all tests in a test file suite:test/verify/check-sessionTo see more verbose output from the test, use the `-v`/`--verbose` and/or `-t`/`--trace` flags:test/verify/check-session --verbose --traceIf you specify `-s`/`--sit` in addition, then the test will wait on failure andallow you to log into cockpit and/or the test instance and diagnose the issue.The cockpit and SSH addresses of the test instance will be printed:test/verify/check-session -stYou can also run *all* the tests, with automatic parallelism:test/common/run-tests --test-dir test/verifyHowever, this will take *really* long. You can specify a subset of tests (see`--help`); but usually it's better to run individual tests locally, and let theCI machinery run all of them in a draft pull request.The tests will automatically download the VM images they need, so expectthat the initial run may take a few minutes.## Interactive browserNormally each test starts its own chromium headless browser process on aseparate random port. To interactively follow what a test is doing:TEST_SHOW_BROWSER=1 test/verify/check-session --traceYou can also run a test against Firefox instead of Chromium:TEST_BROWSER=firefox test/verify/check-session --traceSee below for details.## Manual testingYou can conduct manual interactive testing against a test image by starting theimage like so:bots/vm-run -s cockpit.socket archOnce the machine is booted and the cockpit socket has been activated, amessage will be printed describing how to access the virtual machine, viassh and web. See the "Helpful tips" section below.To login using the web client you have to use the username and password listed below. If you wish to enable `root` login using the web you can remove `root` from `/etc/cockpit/disallowed-users`.- Username: `admin`- Password: `foobar`By default, it's only possible to contact the virtual machine from the hostmachine on which it's running. If you want to conduct manual testing fromother devices on your network, set `TEST_BIND_GLOBAL=1`, for example:TEST_BIND_GLOBAL=1 bots/vm-run -s cockpit.socket archThis will bind the Cockpit and SSH ports to all interfaces, making it possibleto access a URL like http://yourhost.local:9091/ to test Cockpit from anothermachine on your LAN.## Pixel testsPixel tests in Cockpit ensure that updates of our dependencies or code changesdon't break the UI: for example slight changes of layout, padding, color andeverything which isn't easily spotted by a human. They also give us confidencethat an update of our UI Framework doesn't introduce changes in how Cockpitlooks.Pixel tests make a screenshot of a selector and compare it to a known goodreference image. if there is a difference, the test fails and a pixeldifference is shown.This works as our tests run in the [cockpit/tasks container](https://ghcr.io/cockpit-project/tasks)which pins the browser and font rendering so repeated runs provide the samepixels. To generate new pixels, this tasks container must be used; your ownbrowser and font rendering software might generate different results. For moreinformation read the ["introduction blog post"](https://cockpit-project.org/blog/pixel-testing.html).The test images are stored in a git submodule in the `test/reference` directoryand be fetched with:```sh./test/common/pixel-tests update```As Cockpit tests under multiple distributions and it is not worth the effort torun pixel tests on every supported distribution we only run them for theimage configured in `test/reference-image`.Our tests call `Browser.assert_pixels` at interesting and strategic places.This assertion method requires at least a CSS selector and an image title.Pixel tests are generated in five layouts by default: desktop, medium, mobile,dark and rtl.Take a screenshot of the content in `#detail-content`:```pythonbrowser.assert_pixels("#detail-content", "filesystem")```Take a screenshot of the content in `#detail-content` and ignore all elementswith a class `disk-stats` as they change per test run:```pythonbrowser.assert_pixels("#detail-content", "filesystem", ignore=[".disks-stats"])```Take a screenshot of the content in `#detail-content` and skip it for aspecific layout as it generates unstable pixels:```pythonbrowser.assert_pixels("#detail-content", "filesystem", skip_layouts=["rtl"])```To update pixel tests, locally run the test in the current tasks container, orcreate a draft PR and let the tests run for `test/reference-image` andafterwards fetch the new pixels:```./test/common/pixel-tests fetch "https://cockpit-logs.us-east-1.linodeobjects.com/<snip>/log.html"```Finally, upload the new pixel tests and commit the newly generated submodule commit:```./test/common/pixel-tests push```**Note** that you have to a part of the [Contributors group](https://github.com/orgs/cockpit-project/teams/contributors)to push pixel tests.## Test ConfigurationYou can set these environment variables to configure the test suite:* `TEST_OS`: The OS to run the tests in, like "fedora-coreos" or"debian-testing". See the "cockpit-project/cockpit" section in the[test map](https://github.com/cockpit-project/bots/blob/main/lib/testmap.py)for all supported values. "fedora-44" is the default (`TEST_OS_DEFAULT` inbots' [constants.py](https://github.com/cockpit-project/bots/blob/main/lib/constants.py)).* `TEST_BROWSER`: What browser should be used for testing. Currently supportedvalues are "chromium" and "firefox". "chromium" is the default.* `TEST_SHOW_BROWSER`: Set to run browser interactively. When not specified,browser is run in headless mode. When set to "pixels", the browser will beresized to the exact dimensions that are used for pixel tests.* `TEST_TIMEOUT_FACTOR`: Scale normal timeouts by given float value. Useful forslow/busy testbeds or architectures.See the [bots documentation](https://github.com/cockpit-project/bots/blob/main/README.md)for details about the tools and configuration for these.## Convenient test VM SSH accessIt is recommended to add a snippet like this to your `~/.ssh/config`. Thenyou can log in to test machines without authentication:Match final host 127.0.0.2User rootStrictHostKeyChecking noUserKnownHostsFile /dev/nullCheckHostIp noIdentityFile CHECKOUT_DIR/bots/machine/identityIdentitiesOnly yesYou need to replace `CHECKOUT_DIR` with the actual directory where you cloned`cockpit.git`, or `bots.git` if you have a separate clone for that.Many cockpit developers take it a step further, and add an alias toallow typing `ssh c`:Host cHostname 127.0.0.2Port 2201The `final` keyword in the first rule will cause it to be checked (and matched)after the `Hostname` substitution in the `c` rule.## Fast develop/test iterationEach `image-prepare` invocation will always start from the pristine image andignore the current overlay in `test/images`. It is thorough, but also ratherslow. If you want to iterate on changing only JavaScript/HTML code, as opposedto the bridge or webserver, the whole build and test cycle can be done muchfaster.You always need to do at least one initial `test/image-prepare $TEST_OS` run.Afterwards it depends on the kind of test you want to run.### Nondestructive testsMany test methods or classes are marked as `@nondestructive`, meaning thatthey restore the state of the test VM enough that other tests can runafterwards. This is the fastest and most convenient situation for bothiterating on the code and debugging failing tests.Start the prepared VM with `bots/vm-run $TEST_OS`. Note the SSH and cockpitports. If this is the only running VM, it will have the ports in theexamples below, otherwise the port will be different.Then start building the page you are working on[in watch and rsync mode](../HACKING.md#working-on-cockpits-session-pages), e.g.RSYNC=c ./build.js -w users(Assuming the `c` SSH alias from the previous section and first running VM).Then you can run a corresponding test against the running VM, with additionaldebug output:TEST_OS=... test/verify/check-users -t --machine 127.0.0.2:2201 --browser 127.0.0.2:9091 TestAccounts.testBasic### Destructive testsOther tests need one or more fresh VMs. Instead of a full `test/image-prepare`run (which is slow), you can update the existing VM overlay with updatedbundles. Start the build in watch mode, but without rsyncing, e.g../build.js -w storagedand after each iteration, copy the new bundles into the VM overlay:bots/image-customize -u dist:/usr/share/cockpit/ $TEST_OSThen run the test as you would normally do, e.g.TEST_OS=... test/verify/check-storage-stratis -t TestStorageStratis.testBasicUse `bots/vm-reset` to clean up all prepared overlays in `test/images`.## Debugging testsIf you pass the `-s` ("sit on failure") option to a test program, itwill pause when a failure occurs so that you can log into the testmachine and investigate the problem.A test will print out the commands to access it when it fails in thisway. You can log into a running test-machine using ssh. See the"Helpful tips" section below.You can also put calls to `sit()` into the tests themselves to stop themat strategic places.That way, you can run a test cleanly while still being able to makequick changes, such as adding debugging output to JavaScript.## Guidelines for writing testsIf a test is not decorated with `@nondestructive`, it is OK for a test todestroy the test machine OS installation, or otherwise modify it withoutcleaning up. For example, it is OK to remove all of `/etc` just to see whathappens. The next test will get a pristine test machine.Tests decorated with `@nondestructive` will all run against the same testmachine. The nondestructive test should clean up after itself and restore thestate of the machine, such that the next nondestructive test is not impacted.A fast running test suite is more important than independent,small test cases.Thus, it is OK for tests to be long. Starting the test machine is so slow thatwe should run as many checks within a single session as make sense. Note thatnondestructive tests do not suffer from this, and are much quicker.Still, within a long test, try to have independent sections, whereeach section returns the machine to more or less the state that it wasin before the section. This makes it easier to run these sectionsad-hoc when doing incremental development.## CoverageEvery pull request will trigger a `$DEFAULT_OS/devel` scenario which creates acoverage report of the JavaScript code executed and writes comments aboutuncovered code in the pull request. The overall coverage percentage is recordedin prometheus for a subset of our projects and [visualized in Grafana](https://grafana-cockpit.apps.ocp.cloud.ci.centos.org/d/ci/cockpit-ci?orgId=1).To generate coverage locally for `TestApps`:```export NODE_ENV=devel./build.js./test/image-prepare -q./test/common/run-tests --test-dir test/verify --coverage TestApps```Code which is impossible or very hard to test in our tests can be excluded fromappearing in a pull request as comment by adding a `not-covered` comment with ashort justification:```javascriptreturn cockpit.script(data, { superuser: "try", err: "message" }).catch(console.error); // not-covered: OS error```## Helpful tipsFor web access, if you'd like to avoid Chromium (or Chrome) promptingabout certificate errors while connecting to localhost, you can changethe following setting:chrome://flags/#allow-insecure-localhost