Testing UI5 Web Components

Note: All examples in this tutorial are taken from the demo UI5 Web Component (ui5-demo), generated by the package initialization script. For more information on creating a new package with a demo Web Component inside, click here.

1. Prerequisites

The test framework of choice for UI5 Web Components is WebdriverIO or WDIO for short. It has a straightforward API - https://webdriver.io/docs/api.html, and has excellent support for Web Components.

The browser of choice for test execution is Google Chrome, respectively the used WebDriver is ChromeDriver.

Managing ChromeDriver

ChromeDriver is a peer dependency of @ui5/webcomponents-tools. Therefore, you are expected to install and upgrade it manually.

You can install it with npm:

  • npm i --save-dev chromedriver

or with yarn:

  • yarn add -D chromedriver

Google Chrome and ChromeDriver need to be the same version to work together. Whenever you update Google Chrome on your system (or it updates automatically, if allowed), you are expected to also update ChromeDriver to the respective version.

2. Running the tests

2.1 Test configuration

The configuration for WDIO can be found in the config/ directory under wdio.conf.js.

As explained here in the section about the config/ directory, you can customize, or even completely replace the default configuration.

However, before doing so, please note the following two benefits of working with the default configuration, provided by UI5 Web Components:

  • Hooks, synchronizing the execution of all relevant WDIO commands (e.g. click, url, $, $$) with the rendering of the framework to ensure consistency when reading or changing the state of the components.
  • Additional API methods: setProperty, setAttribute, removeAttribute, hasClass.

So our recommendation would be to modify it, if necessary, but not completely replace it.

2.2 Running all tests

npm run test

or

yarn test

This will launch a static server and execute all tests found in the test/specs/ directory of your package.

Note: Before running the tests for the first time, make sure you’ve built the project, or at least the dev server is running (build or start commands).

2.3 Running a single test spec

npm run test test/specs/Demo.spec.js

or

yarn test test/specs/Demo.spec.js

Note: Executing a single test spec only makes sense for debugging purposes, therefore no test server is launched for it. Make sure you’re running the start command while running single test specs, as it provides a server and the ability to change files, and test the changes on the fly.

3. Writing tests

The simplest test would look something like this:

describe("ui5-demo rendering", async () => {
	await browser.url("test/pages/index.html");

	it("tests if web component is correctly rendered", async () => {
		const innerContent = await browser.$("#myFirstComponent").shadow$("div");
		assert.ok(innerContent, "content rendered");
	});
});

Key points:

  • Load the test page with the browser.url command. You can do this once for each test suite or for each individual test.
  • You can access the Web Components with $ or $$.
  • You can access the shadow roots with shadow$ or shadow$$.
  • Simulate mouse interaction with the Web Components by calling the click command on the Web Component itself or certain parts of its shadow root.
  • Simulate keyboard with the keys command.
  • You can read the DOM with commands such as getHTML, getProperty, getAttribute, etc.
  • You can modify the DOM with commands such as setProperty, setAttribute etc.

For WDIO capabilities, see:

  • Official API: https://webdriver.io/docs/api.html.
  • Additional commands provided in our standard WDIO configuration: setProperty, setAttribute, removeAttribute, hasClass.

Note: The standard WDIO configuration we provide automatically synchronizes all test commands’ execution with the framework rendering cycle. Therefore, in the example above, the shadow$ command will internally wait for all rendering to be over before being executed. The same holds true for commands that modify the DOM such as setAttribute or click.

4. Debugging tests

Debugging with WDIO is really simple. Just follow these 3 steps:

  1. Change the WDIO configuration file config/wdio.conf.js to disable headless mode for Google Chrome as follows:

    From:

     module.exports = require("@ui5/webcomponents-tools/components-package/wdio.js");
    

    to:

     const result = require("@ui5/webcomponents-tools/components-package/wdio.js");
     result.config.capabilities[0]["goog:chromeOptions"].args = ['--disable-gpu']; // From: ['--disable-gpu', '--headless']
     module.exports = result;
    

    If you happen to debug often, it’s recommended to keep the file in this format and just comment out the middle line when you’re done debugging.

  2. Set a breakpoint with browser.debug somewhere in your test:

     it("tests if web component is correctly rendered", async () => {
         const innerContent = await browser.$("#myFirstComponent").shadow$("div");
         await browser.debug();
         assert.ok(innerContent, "content rendered");
     });
    

    For more on debug, see https://webdriver.io/docs/api/browser/debug.html.

  3. Run the single test spec and wait for the browser to open and pause on your breakpoint:

  • Run the dev server, if you haven’t already:

    yarn start

    or

    npm run start.

  • Run the single test spec:

    yarn test test/specs/Demo.spec.js

    or

    npm run test test/specs/Demo.spec.js.

Google Chrome will then open in a new window, controlled by WDIO via the ChromeDriver, and your test will pause on your breakpoint of choice. Proceed to debug normally.

5. Best practices for writing tests

Do not overuse assert.ok

When an assert.ok fails, the error you get is “Expected something to be true, but it was false”. This is fine when you’re passing a Boolean, but not ok when there is an expression inside assert.ok and you’d like to know which part of the expression is not as expected.

For example, when assert.ok(a === b, "They match") fails, the error just says that an expression that was expected to be true was false. However, if you use assert.strictEqual(a, b, "They match"), and it fails, the error will say that a was expected to be a certain value, but it was another value, which makes it much easier to debug.

Prefer one of the following, when applicable:

  • assert.notOk(a) instead of assert.ok(!a)
  • assert.strictEqual(a, b) instead of assert.ok(a === b)
  • assert.isBelow(a, b) instead of assert.ok(a < b)
  • assert.isAbove(a, b) instead of assert.ok(a > b)
  • assert.exists / assert.notExists when checking for null or undefined

Do not overuse assert.strictEqual

Use:

  • assert.ok instead of assert.strictEqual(a, true)
  • assert.notOk instead of assert.strictEqual(a, false)

Use isExisting to check DOM

Preferred:

assert.ok(await browser.$(<SELECTOR>).isExisting())

instead of:

assert.ok((await browser.$$(<SELECTOR>)).length)

Do not use browser.executeAsync for properties

We have custom commands such as getProperty and setProperty to fill in gaps in the WDIO standard command set. Use them instead of manually setting properties with executeAsync.

Use assert.include instead of string functions

Use:

assert.include(text, EXPECTED_TEXT, "Text found")
assert.notInclude(text, NOT_EXPECTED_TEXT, "Text not found")

instead of:

assert.ok(text.indexOf(EXPECTED_TEXT) > -1, "Text found")
assert.ok(text.includes(EXPECTED_TEXT), "Text found")
assert.notOk(text.includes(NOT_EXPECTED_TEXT), "Text not found")

Extract variables before asserting

Avoid complex expressions inside asserts by extracting parts of them to variables and only asserting the variables.

6. Using the synchronous syntax for writing texts

WebdriverIO still supports (although now deprecated) the synchronous syntax for writing tests. Click here for more information on “sync vs async”.

UI5 Web Components versions up to, including, 1.0.0-rc.15, used to recommend the synchronous syntax, as it is easier to use.

If you have already written tests for your custom UI5 Web Components using the synchronous syntax, and you update to a later version than 1.0.0-rc.15, your tests will no longer run. You have 2 options:

  • Rewrite all tests to use the asynchronous syntax. Click the link above to see some examples. This is the recommended approach, because the synchronous syntax will no longer work with future nodejs versions.
  • For the time being, adapt your WebdriverIO configuration to continue supporting the synchronous syntax.

5.1 Supporting the synchronous syntax for writing tests

  • Change your config/wdio.conf.js file’s content from:
 module.exports = require("@ui5/webcomponents-tools/components-package/wdio.js");

to:

 module.exports = require("@ui5/webcomponents-tools/components-package/wdio.sync.js");

This will give you the exact same WebdriverIO configuration, but with synchronous custom commands (such as getProperty, setProperty, hasClass, etc.).

  • Manually install @wdio/sync

You can install it with npm:

npm i --save-dev @wdio/sync

or with yarn:

yarn add -D @wdio/sync

Just installing the package (with no extra configuration) is enough to let WebdriverIO run the synchronous tests.