Skip to Content
Technical Articles
Author's profile photo Volker Buzek

Testing the community code challenge app with wdi5 – FLP navigation, upload, download

Watching qmacro walk through the latest SAP devs community challenge, it was evident that the “profilepic” UI5 app was missing some tests. Why not put in some wdi5 end-to-end testing flavor? What I thought would be a low hanging fruit to pick turned out to hold quite some challenges:

  1. use in an ES module environment
  2. navigate an FLP tile
  3. conducting a file upload
  4. asserting a file download

But: got all of the above covered with wdi5 💪

Here‘s how.
(note: all of the following will also go in the new “recipes” section of the soon-to-launch new wdi5 documentation site)
(note 2: grab the source over at https://github.com/vobu/sap-community-code-challenge and code-view along)
(note 3: do npm i && npm start in one terminal, then npm test in another to have the test run)

use in an ES module environment

As Thomas Jung had set the project up to live in ESM land, wdi5 had to adhere to that at config- and runtime. This was easy to solve: just a rename of the config- and test-file to a .cjs extension to let the Node.js runtime know we’re a CJS module. No coding changes required.

The project launches as a mocked Fiori Launchpad (FLP). Given the FLP is also a UI5 app, wdi5‘s standard mechanism of retrieving controls could be used in the tests.
Via the text property matcher, the tile‘s label is located (a sap.m.Text).

const tile = await browser
.asControl({
    selector: {
        properties: {
            text: "SAP Community Profile Picture Editor"
        },
        controlType: "sap.m.Text"
    }
})

Then we navigate up the control tree with the help of wdi5‘s fluent async api until we reach the tile itself via .getParent().getParent().
A click on it brings us to the profilepic app itself.

// get wdio element reference
const $tile = await tile.getWebElement()
// use wdio api
await $tile.click()

Why not locating that tile itself directly? Because it doesn‘t use a stable ID, and thus its‘ ID might change in the next UI5 rendering cycle – using a locator id such as __tile0 might break the test eventually then.

conducting a file upload

This part shows wdi5’s excellent foundation, Webdriver.IO (wdio), playing in unison with wdi5.
First, we utilize wdi5 to retrieve the file uploader control.

const uploader = await browser.asControl({
    forceSelect: true,
    selector: {
        id: "fileToUpload",
        viewName: "profilePic.view.App"
    }
})

Then we use wdio to get the file input element. As per the WebDriver spec, this is the one DOM element capable of receiving a programmatic file upload.

const $uploader = await uploader.getWebElement()
const $fileInput = await $uploader.$("input[type=file]")
await $fileInput.setValue(file)

After doing this, wdi5‘s api is used again to assert that the file was uploaded and to press the enhance/upload button.

// assert the upload
const uploadedImage = await uploader.getValue()
expect(uploadedImage).toEqual(fileName)
// trigger pic enhancement
await browser
    .asControl({
    selector: {
        id: "button",
        viewName: "profilePic.view.App"
    }
})
    .firePress() // this will go away with wdi5 0.9.0 and replaced by .press()

Volià!

asserting a file download

Now time to unpack the bag of tricks for doing and validating a file download.
Chromedriver offers some custom settings that help preparing things.

The config is set so that

  • no download dialog appears
  • a static download dir exists
// in wdio.conf.cjs
"goog:chromeOptions": {
    prefs: {
        directory_upgrade: true,
        prompt_for_download: false,
        "download.default_directory": join(__dirname, "test", "__assets__")
    }
}

Then the download is triggered via pressing the respective UI5 button.

// wdi5
await browser
    .asControl({
    selector: {
        id: "downloadButton",
        viewName: "profilePic.view.App"
    }
})
    .firePress() // this will go away with wdi5 0.9.0 and replaced by .press()

After the download completes, standard Node.js mechanisms are used to validate the downloaded file in the static download directory specified.

const downloadedFile = join(__dirname, "__assets__", "image.png") // by the books, getting the image name dynamically would be a thing
// stat is from 
// const { stat } = require("node:fs/promises")
expect(await (await stat(downloadedFile)).size).toBeGreaterThan(1)

bonus: chrome debug tools

Wouldn’t it be cool to have Chrome’s debug tool available at test-time to eventually look under the hood while tests are executing? Rhetoric question, of course it would.

This can be achieve with wdi5/wdio by providing a respective flag to the browser config:

// in wdio.conf.cjs
"goog:chromeOptions": {
    args: process.env.DEBUG ? ["--auto-open-devtools-for-tabs"] : []
}

If you now execute the test with the DEBUG env var set, e.g. via DEBUG=true npm test, then the debug pane opens.

All in all, quite a concert: wdi5, Webdriver.IO, Chrome and Node.js playing together nicely to test advanced functionality in a UI5 app.
What‘s not to like?!

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Nicolai Geburek
      Nicolai Geburek

      Thanks a lot for putting this together, Volker!