cryostat-web

Web Testing

Refer to this document for information on how to unit test Cryostat Web.

LIBRARIES

CONFIGURATION

UNIT TESTING

Overview

Use Jest’s describe function to group related unit tests into a single block. The tests themselves are denoted using the test or its alias it. Jest also provides an extensive list of “matchers” for making assertions. These Jest utilities do not need to be imported.

In order to render the component under test into its HTML DOM representation and perform queries on this representation, use RTL’s render function in conjunction with screen, both of which can be imported from @testing-library/react. After the render call, the screen object can be queried for DOM nodes/elements, which in turn can be asserted on using the aforementioned Jest matchers. There is typically one render call per unit test.

Tips

MOCKING

Overview

Refer to the Jest documentation for various mocking techniques, including mock functions and more advanced strategies such as manual mocks.

The decision to mock out a component during testing should adhere to RTL’s guiding principle that “the more your tests resemble the way your software is used, the more confidence they can give you”. Therefore, when unit testing a component make an effort to only mock out API calls, child components that belong to Cryostat Web (since they’ll have their own unit tests), and the shared services that are propagated throughout the app using the ServiceContext. Any third-party child components, such as those belonging to Patternfly, should be left unmocked if possible.

Tips

SNAPSHOT TESTING

Overview

Snapshot testing helps ensure that we stay on top of any changes to our UI. It’s a complement to regular unit testing, in which we render React components, take a serialized snapshot of the result, and compare it to a reference snapshot file to see if anything has changed. Snapshot files are committed to version control alongside their corresponding tests and are included in the review process.

When the Jest test suite runs, a new snapshot will be created for every component under test and compared to the reference snapshot in version control. If there is any discrepancy between the two snapshots a diff will be output to the command line. From here, it is up to you to determine whether the difference is due to a bug or an intentional implementation change. This may warrant updating or adding more unit tests. When you are satisfied with the reasons behind the changed snapshot, you can update it to be the new reference snapshot by running the following command:

yarn test -- -u -t=”SPEC_NAME” 

Where the -u flag tells Jest to update the snapshot and the -t flag specifies which test to update it for. SPEC_NAME is matched against the string passed into the describe call of the test file in question. For example, in Recordings.test.tsx the unit tests are housed inside of the describe(‘<Recordings />’, ….) block so in order to update the snapshot for the Recordings component, you would pass -t=”<Recordings />” to the above command.

Tips

INTEGRATION TESTING

Overview

We can also use Jest as a test runner for Selenium tests. This allows us to write integration tests that simulate user actions and interactions with the Cryostat Web UI.

To run the integration tests, you will need to have the preview server running. You can start the preview server by running yarn start:dev:preview.

You will also need a WebDriver implementation for the specific browser you want to automate. WebDriver allows Selenium to control the browser and perform actions on web elements. Here is list of currently supported drivers:

Note: Currently only Geckodriver is supported for cryostat-web itests. In the future, there should be support for ChromeDriver (for Chrome) and Edge WebDriver (for Microsoft Edge).

Then, finally we can run yarn itest to run the integration tests, which will open up a fresh browser window and run the tests.

Alternatively, you can start the integration tests immediately without the need for an already up and running dev server, by running

$ yarn itest:preview

This will automatically start a Mirage dev server, run the integration tests on that server, and tear down the server on completion.

Tips

Add more methods to each PO, to test more actions. The point is, we want to abstract each browser action, so that even if something changes within our code, (e.g. a class tag is renamed, or a button is placed in a different component), all we need is to change the underlying implementation of each function to keep tests consistent.

  async skipTour() {
    const skipButton = await this.driver
      .wait(until.elementLocated(By.css('button[data-action="skip"]')))
      .catch(() => null);
    if (skipButton) await skipButton.click();
  }

In the code, we first tell the driver to wait, until an element is located by the css selector (‘button[data-action=”skip”]’), and assign it to a variable. If not found, we assign null. Then if the variable is non-null, we click it. To find a good query to use, it is recommended to use the Selenium IDE extension on your browser. The extension allows you to easily see queries that can be used to select an element you want.