Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
laszlo_kajan2
Active Participant

Executive summary


OPA5 integration testing of SAP Fiori launchpad apps, including code coverage, isn't supported by sap.ui.test.Opa5. This blog post provides a solution.

Automation of OPA5 integration testing of SAP Fiori launchpad apps with code coverage is also presented.

Author and motivation


Laszlo Kajan is a full stack Fiori/SAPUI5 expert, present on the SAPUI5 field since 2015.

The motivation behind this blog post is to provide an automated integration testing solution for Fiori launchpad apps - a feature so far missing from sap.ui.test.Opa5.

Headless OPA5 testing of Fiori launchpad apps with code coverage and Karma


Goal



  • Implement automated OPA5 integration tests for SAPUI5 launchpad apps

  • Provide code coverage results

  • Employ current OPA5 best practices


App folder structure



  • webapp

    • test

      • integration

        • arrangement

          • component

            • Arrangement.js





        • pages

          • Common.js



        • AllJourneys.js

        • opaTestsWithComponent.qunit.html



      • karma

        • context.html



      • launchers

        • ushellLauncher.js





    • fakeLRep.json

    • test.html



  • karma.conf.js

  • package.json


Issue 1: Missing 'iStartMyUIComponentInUshell'


The SAP WebIDE runs Fiori launchpad apps with mock data by running the app's Component.js in a sap.ushell.Container. OPA5 testing allows apps to be started in an iframe - iStartMyAppInAFrame, or by their Component.js - iStartMyUIComponent. As of 20180809, test code coverage analysis only works in case the app is not started in an iframe. When iStartMyUIComponent is used to start the app, unlike starting it with mock data, Component.js is not placed into a sap.ushell.Container. Because of this, application code that otherwise works well - e.g. access to launchpad services - fails, leading to test errors. This is the first issue to solve.

How to run OPA5 integration tests with code coverage results, in a way similar to how the app is run with mock data? There is no OPA5 method 'iStartMyUIComponentInUshell' that would start a Component.js in a sap.ushell.Container. Let us define one:

  1. webapp/test/integration/arrangement/component/Arrangement.js:
    // 20180808 openui5/src/sap.m/test/sap/m/demokit/cart
    // https://github.com/SAP/openui5/blob/master/src/sap.m/test/sap/m/demokit/cart/webapp/test/integration...
    sap.ui.define([
    "sap/ui/test/Opa5",
    "sap/ui/core/routing/HashChanger",
    "com/acme/top/wdsp/mon/test/launchers/ushellLauncher"
    ], function(Opa5, HashChanger, launcher) {
    "use strict";

    // Copy from /sap/ui/test/Opa5-dbg.js
    function createWaitForObjectWithoutDefaults() {
    return {
    // make sure no controls are searched by the defaults
    viewName: null,
    controlType: null,
    id: null,
    searchOpenDialogs: false,
    autoWait: false
    };
    }

    var Arrangement = Opa5.extend("com.acme.top.wdsp.mon.test.integration.arrangement.component.Arrangement", {

    iStartMyUIComponentInUshell: function(oOptions) {
    var bComponentLoaded = false;
    oOptions = oOptions || {};

    var oFirstWaitForOptions = createWaitForObjectWithoutDefaults();
    oFirstWaitForOptions.success = function() {
    // include stylesheet
    var sComponentStyleLocation = jQuery.sap.getModulePath("sap.ui.test.OpaCss", ".css");
    $.sap.includeStyleSheet(sComponentStyleLocation);

    if (oOptions.hash) {
    HashChanger.getInstance().setHash(oOptions.hash);
    }

    launcher.start().then(function() {
    bComponentLoaded = true;
    });
    };
    // wait for starting of component launcher
    this.waitFor(oFirstWaitForOptions);

    var oPropertiesForWaitFor = createWaitForObjectWithoutDefaults();
    oPropertiesForWaitFor.errorMessage = "Unable to start launcher with hash: " + oOptions.hash;
    oPropertiesForWaitFor.check = function() {
    return bComponentLoaded;
    };

    // add timeout to object for waitFor when timeout is specified
    if (oOptions.timeout) {
    oPropertiesForWaitFor.timeout = oOptions.timeout;
    }

    return this.waitFor(oPropertiesForWaitFor);
    },

    iStartTheApp: function(oOptions) {
    oOptions = oOptions || {};
    return this.iStartMyUIComponentInUshell({
    hash: oOptions.hash
    });
    }
    });
    return Arrangement;
    });​


  2. webapp/test/launchers/ushellLauncher.js:
    // Based on https://sapui5.hana.ondemand.com/1.44.38/resources/sap/ui/test/launchers/componentLauncher-dbg.js
    /*!
    * UI development toolkit for HTML5 (OpenUI5)
    * (c) Copyright 2009-2016 SAP SE or an SAP affiliate company.
    * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
    */
    sap.ui.define([
    'jquery.sap.global'
    ], function(jQuery) {
    "use strict";
    var $ = jQuery,
    _loadingStarted = false,
    _oComponentContainer = null,
    _$Component = null;

    /**
    * By using start launcher will instantiate and place the sap.ushell.Container into html.
    * By using teardown launcher will destroy the sap.ushell.Container and remove the div from html.
    * Calling start twice without teardown is not allowed
    * @private
    * @class
    * @author SAP SE
    * @alias sap.ui.test.launchers.ushellLauncher
    */
    return {

    start: function() {
    if (_loadingStarted) {
    throw "sap.ui.test.launchers.componentLauncher: Start was called twice without teardown";
    }

    var oPromise = Promise.resolve();

    _loadingStarted = true;

    return oPromise.then(function() {
    var sId = jQuery.sap.uid();

    // create and add div to html
    _$Component = $('<div id="' + sId + '" class="sapUiOpaComponent"></div>');
    /* eslint-disable sap-no-dom-insertion */
    $("body").append(_$Component).addClass("sapUiOpaBodyComponent");
    /* eslint-disable sap-no-dom-insertion */

    // create and place the component into html
    _oComponentContainer = sap.ushell.Container.createRenderer();

    _oComponentContainer.placeAt(sId);
    });

    },

    hasLaunched: function() {
    return _loadingStarted;
    },

    teardown: function() {
    // Opa prevent the case if teardown was called after the start but before the promise was fulfilled
    if (!_loadingStarted) {
    throw "sap.ui.test.launchers.componentLauncher: Teardown has been called but there was no start";
    }
    _oComponentContainer.destroy();
    _$Component.remove();
    _loadingStarted = false;
    $("body").removeClass("sapUiOpaBodyComponent");
    }
    };

    }, /* export= */ true);​



Run the test like this:

  1. webapp/test.html:
    <!DOCTYPE html>
    <html>
    <head>
    <title>Testing Overview</title>
    <!-- try to load the basic UI5 styles -->
    <link rel="stylesheet" type="text/css" href="resources/sap/ui/core/themes/sap_bluecrystal/library.css">
    </head>
    <body class="sapUiBody sapUiMediumMargin sapUiForceWidthAuto">
    <h1>Testing Overview</h1>
    <p>This is an overview page of various ways to test the generated app during development.<br/>Choose one of the access points below to launch the app as a standalone application, e.g. on a Tomcat server.</p>

    <ul>
    <li><a href="test/integration/opaTestsWithComponent.qunit.html?coverage">test/integration/opaTestsWithComponent.qunit.html</a> - run all integration tests with Component</li>
    </ul>
    </body>
    </html>​


  2. webapp/test/integration/opaTestsWithComponent.qunit.html:
    <!DOCTYPE html>
    <html style="overflow:auto">
    <head>
    <title>Integration tests for com.acme.top.wdsp.mon with Component</title>
    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
    <meta charset="utf-8">

    <script type="text/javascript">
    window["sap-ushell-config"] = {
    defaultRenderer : "fiori2",
    renderers: {
    fiori2: {
    componentData: {
    config: {
    rootIntent: "ZF67_WDAYSP-monitor",
    search: "hidden"
    }
    }
    }
    },
    applications: {
    "ZF67_WDAYSP-monitor": {
    "additionalInformation": "SAPUI5.Component=com.acme.top.wdsp.mon",
    "applicationType": "URL",
    "url": "../../"
    }
    }
    };
    </script>
    <script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js" id="sap-ushell-bootstrap"></script>

    <!--
    src="https://sapui5.hana.ondemand.com/1.44.38/resources/sap-ui-core.js"
    src="../../resources/sap-ui-core.js"
    -->

    <!-- The namespace sap.ui.demo.cart.test.arrangement.Arrangement is used to run the IFrame or the component without changes to the test code -->
    <!--script>
    // Note 20180809: loading sap-ui-core this way causes all sorts of problems with sap.ushell.Container loading,
    // regardless of the "preload" setting. It doesn't work, must use the data-sap-ui... solution below.
    window["sap-ui-config"] = {
    "animation":"false",
    "compatVersion":"edge",
    "debug":"false",
    "frameOptions":"deny",
    "language":"en",
    "theme":"sap_belize",
    "libs":"sap.m, sap.ushell",
    "resourceRoots":{
    "com.acme.top.wdsp.mon":"../../",
    "Arrangement":"./arrangement/component/Arrangement"},
    "preload":"async",
    "xx-debugModuleLoading":"true",
    "xx-showLoadErrors":"true",
    "xx-supportedLanguages":["en"]};
    </script>
    <script id="sap-ui-bootstrap"
    src="../../resources/sap-ui-core.js">
    </script-->
    <script id="sap-ui-bootstrap"
    src="../../resources/sap-ui-core.js"
    data-sap-ui-animation="false"
    data-sap-ui-compatVersion="edge"
    data-sap-ui-frameOptions='deny'
    data-sap-ui-language="en"
    data-sap-ui-libs='sap.m, sap.ushell'
    data-sap-ui-preload='async'
    data-sap-ui-resourceroots='{
    "com.acme.top.wdsp.mon" : "../../",
    "Arrangement": "./arrangement/component/Arrangement"
    }'
    data-sap-ui-theme="sap_belize"
    data-sap-ui-xx-supportedLanguages="">
    </script>

    <script src="../../resources/sap/ui/qunit/qunit-css.js"></script>
    <script src="../../resources/sap/ui/thirdparty/qunit.js"></script>
    <script src="../../resources/sap/ui/qunit/qunit-junit.js"></script>
    <script src="../../resources/sap/ui/qunit/qunit-coverage.js"
    data-sap-ui-cover-only="com/acme/top/wdsp/mon/"
    data-sap-ui-cover-never="[com/acme/top/wdsp/mon/localService/, com/acme/top/wdsp/mon/test/]">
    </script>

    <script>
    // https://github.com/SAP/openui5/blob/master/src/sap.m/test/sap/m/demokit/cart/webapp/test/integration...
    // we want to be able to load our tests asynchronously - pause QUnit until we loaded everything
    QUnit.config.autostart = false;

    sap.ui.getCore().attachInit(function () {
    "use strict";
    sap.ui.require([
    "sap/ui/fl/FakeLrepConnector",
    "com/acme/top/wdsp/mon/localService/mockserver",
    "com/acme/top/wdsp/mon/test/integration/AllJourneys"
    ], function (FakeLrepConnector, server) {
    //Fake LREP
    FakeLrepConnector.enableFakeConnector("../../fakeLRep.json");

    // set up test service for local testing
    server.init({autoRespondAfter: 50});

    // configuration has been applied and the tests in the journeys have been loaded - start QUnit
    QUnit.start();
    });
    });
    </script>
    </head>
    <body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
    </body>
    </html>​

     

  3. webapp/fakeLRep.json:
    {
    "changes": [],
    "settings": {
    "isKeyUser": true,
    "isAtoAvailable": false,
    "isProductiveSystem": false
    }
    }​


  4. webapp/test/integration/AllJourneys.js:
    // https://github.com/SAP/openui5/blob/master/src/sap.m/test/sap/m/demokit/cart/webapp/test/integration...
    sap.ui.define([
    "sap/ui/test/Opa5",
    "Arrangement",
    "./ListJourney",
    //"./BusyJourney"
    ], function(Opa5, Arrangement) {
    "use strict";

    Opa5.extendConfig({
    arrangements: new Arrangement(),
    viewNamespace: "com.acme.top.wdsp.mon.view.",
    autoWait: true
    });
    });​


  5. webapp/test/integration/ListJourney.js:
    /* global QUnit */
    sap.ui.define([
    "sap/ui/test/opaQunit",
    "./pages/List"
    ], function (opaTest) {
    "use strict";

    QUnit.module("List Page Journey");

    opaTest("Should see the list with all entries", function (Given, When, Then) {
    // Arrangements
    Given.iStartTheApp();

    //Actions
    When.onTheListPage.iLookAtTheScreen();

    // Assertions
    Then.onTheListPage.iShouldSeeTheList().
    and.theListShouldHaveAllEntries().
    and.theHeaderShouldDisplayAllEntries();

    Then.onTheListPage.iTeardownMyApp();
    });
    }
    );​


  6. webapp/test/integration/pages/List.js:
    sap.ui.define([
    "sap/ui/test/Opa5",
    "sap/ui/test/actions/Press",
    "sap/ui/test/actions/EnterText",
    "sap/ui/test/matchers/AggregationLengthEquals",
    "sap/ui/test/matchers/AggregationFilled",
    "sap/ui/test/matchers/PropertyStrictEquals",
    "com/acme/top/wdsp/mon/test/integration/pages/Common"
    ], function(Opa5, Press, EnterText, AggregationLengthEquals, AggregationFilled, PropertyStrictEquals, Common) {
    "use strict";

    var sViewName = "List",
    sSomethingThatCannotBeFound = "*#-Q@@||",
    iGroupingBoundary = 100;

    Opa5.createPageObjects({
    onTheListPage : {
    baseClass : Common,

    actions : {
    },

    assertions : {

    iShouldSeeTheList : function () {
    return this.waitFor({
    id : "innerUi5Table",
    viewName : sViewName,
    success : function (oList) {
    Opa5.assert.ok(oList, "Found the list");
    },
    errorMessage : "Can't see the list."
    });
    },

    theListShouldHaveAllEntries : function () {
    var aAllEntities,
    iExpectedNumberOfItems;
    // retrieve all TransferSet to be able to check for the total amount
    return this.waitFor(this.createAWaitForAnEntitySet({
    entitySet : "ZF67_C_HRMON",
    success : function (aEntityData) {
    aAllEntities = aEntityData;
    return this.waitFor({
    id : "innerUi5Table",
    viewName : sViewName,
    matchers : function (oList) {
    // If there are less items in the table than the growingThreshold, only check for this number.
    iExpectedNumberOfItems = Math.min(oList.getGrowingThreshold(), aAllEntities.length);
    return new AggregationLengthEquals({name : "items", length : iExpectedNumberOfItems}).isMatching(oList);
    },
    success : function (oList) {
    Opa5.assert.strictEqual(oList.getItems().length, iExpectedNumberOfItems, "The table displays all items");
    },
    errorMessage : "Table does not display all entries."
    });
    }
    }));
    },

    theHeaderShouldDisplayAllEntries : function () {
    return this.waitFor({
    id : "innerUi5Table",
    viewName : sViewName,
    success : function (oList) {
    var iExpectedLength = oList.getBinding("items").getLength();
    this.waitFor({
    id : "smartTable",
    viewName : sViewName,
    success : function (oSmartTable) {
    var sTableHeader = oSmartTable.getDomRef().querySelector("div > div > div > span").textContent;
    Opa5.assert.strictEqual(sTableHeader, "Transfers (11)", "The header should show 'Transfers (11)'");
    },
    errorMessage : "The table 'smartTable' was not found"
    });
    },
    errorMessage : "Table title does not display the number of items in the list"
    });
    },
    }
    }
    });
    }
    );​


  7. webapp/test/integration/pages/Common.js:
    sap.ui.define([
    "sap/ui/test/Opa5",
    "com/acme/top/wdsp/mon/test/launchers/ushellLauncher"
    ], function(Opa5, launcher) {
    "use strict";

    // Copy from /sap/ui/test/Opa5-dbg.js
    function createWaitForObjectWithoutDefaults() {
    return {
    // make sure no controls are searched by the defaults
    viewName: null,
    controlType: null,
    id: null,
    searchOpenDialogs: false,
    autoWait: false
    };
    }

    return Opa5.extend("com.acme.top.wdsp.mon.test.integration.pages.Common", {

    iLookAtTheScreen: function() {
    return this;
    },

    createAWaitForAnEntitySet: function(oOptions) {
    return {
    success: function() {
    var bMockServerAvailable = false,
    aEntitySet;

    this.getMockServer().then(function(oMockServer) {
    aEntitySet = oMockServer.getEntitySetData(oOptions.entitySet);
    bMockServerAvailable = true;
    });

    return this.waitFor({
    check: function() {
    return bMockServerAvailable;
    },
    success: function() {
    oOptions.success.call(this, aEntitySet);
    }
    });
    }
    };
    },

    getMockServer: function() {
    return new Promise(function(success) {
    (Opa5.getWindow() || window).sap.ui.require(["com/acme/top/wdsp/mon/localService/mockserver"], function(mockserver) {
    success(mockserver.getMockServer());
    });
    });
    },

    iTeardownMyApp: function() {
    var oOptions = createWaitForObjectWithoutDefaults();
    oOptions.success = function() {
    if (launcher.hasLaunched()) {
    this.iTeardownMyUIComponentInUshell();
    } else {
    Opa5.prototype.iTeardownMyApp.apply(this, arguments);
    }
    }.bind(this);

    return this.waitFor(oOptions);
    },

    iTeardownMyUIComponentInUshell: function() {

    var oOptions = createWaitForObjectWithoutDefaults();
    oOptions.success = function() {
    launcher.teardown();
    };
    return this.waitFor(oOptions);
    }
    });
    });​


  8. The test runs and code coverage results are now shown, the app runs in the Unified Shell (ushell):


Issue 2: Karma context for OPA5 integration tests


Karma can be used to automate OPA5 tests. As long as tests are not run in an iframe, code coverage reporting works as expected. But karma-openui5 <= 0.2.3 can unfortunately not be used to set up the test context of a launchpad app, because of a problem with loading the 'sap.ushell' library when the dependency is given as window["sap-ui-config"].libs = "sap.m, sap.ushell" (instead of data-sap-ui-libs='sap.m, sap.ushell' - see comments in code for 'opaTestsWithComponent.qunit.html' above).

How to the up the Karma test context for OPA5 integration tests for launchpad apps?

  1. package.json:
    {
    "name": "com.acme.top.wdsp.mon",
    "version": "1.0.0",
    "description": "Monitor Tool",
    "main": "webapp/Component.js",
    "license": "UNLICENSED",
    "scripts": {
    "karma": "karma start karma.conf.js",
    "test": "npm run karma:ci",
    "karma:ci": "karma start karma.conf.js --singleRun"
    },
    "author": "Laszlo Kajan",
    "devDependencies": {
    "karma": "^2.0.5",
    "karma-chrome-launcher": "^2.2.0",
    "karma-cli": "^1.0.1",
    "karma-coverage": "^1.1.2",
    "karma-junit-reporter": "^1.2.0",
    "karma-phantomjs-launcher": "^1.0.4",
    "karma-qunit": "^2.1.0",
    "qunit": "^2.6.1"
    }
    }​


  2. karma.conf.js:
    // Example: Headless OPA5 testing with Karma and PhantomJS
    // https://blogs.sap.com/2016/11/21/headless-opa5-testing-with-karma-and-phantomjs/
    module.exports = function(config) {
    'use strict';

    const CI_MODE = !!config.singleRun;

    config.set({
    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: [
    'qunit'
    ],

    // list of files / patterns to load in the browser
    files: [
    {pattern: 'node_modules/mobx/lib/mobx.umd.min.js', included: false, served: true, watched: false},
    {pattern: 'com.acme.top.eihb/webapp/**/*', included: false, served: true, watched: true},
    {pattern: 'sap.ui.mobx/src/**/*', included: false, served: true, watched: true},
    {pattern: 'webapp/**/*', included: false, served: true, watched: true},
    ],

    // list of files / patterns to exclude
    exclude: [
    ],

    customContextFile: "webapp/test/karma/context.html",

    client: {
    captureConsole: true,
    // If false, Karma does not clear the context window upon the completion of running the tests
    clearContext: false,
    useIframe: false,
    qunit: {
    // showUI: true needs the clearContext: false option to display correctly in non-debug mode
    //showUI: true,
    testTimeout: 15000,
    autostart: false,
    autoload: false
    }
    },

    proxies: {
    '/sap/bc/bsp/sap/zx6g_libmobx/4.1.1/': '/base/node_modules/mobx/lib/',
    '/sap/bc/ui5_ui5/sap/zf67_eihb_lib/': '/base/com.acme.top.eihb/webapp/',
    '/sap/bc/ui5_ui5/sap/zx6g_libmobxui5/': '/base/sap.ui.mobx/src/'
    },

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: CI_MODE ? {
    'webapp/*.js': ['coverage'],
    'webapp/!(localService|test)/**/*.js': ['coverage']
    } : {},

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'junit'].concat(CI_MODE ? 'coverage' : []),

    coverageReporter: {
    dir: 'reports/coverage',
    // Include all sources files, as indicated by the coverage preprocessor
    includeAllSources: true,
    subdir: browser => browser.split(' ')[0],
    reporters: [
    // {type: 'cobertura', subdir: 'cobertura'},
    {type: 'lcov', subdir: 'lcov'},
    // {type: 'lcovonly', subdir: 'lcovonly'},
    {type: 'text-summary'}
    ]
    },

    junitReporter: {
    outputDir: 'reports', // results will be saved as $outputDir/$browserName.xml
    outputFile: 'junit/webapp.xml',
    // if included, results will be saved as $outputDir/$browserName/$outputFile
    suite: 'sapui5', // suite will become the package name attribute in xml testsuite element
    useBrowserName: true // add browser name to report and classes names
    },

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_ERROR,

    //loggers: [
    // {type: 'console'},
    // {
    // type: 'file',
    // filename: 'karma.log',
    // maxLogSize: 65536,
    // backups: 3
    // }
    //],

    browserNoActivityTimeout: 30000,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: [
    // 'Chrome'
    'Chrome_without_security'
    // 'PhantomJS_custom'
    ],

    customLaunchers: {
    Chrome_without_security: {
    base: 'Chrome',
    flags: ['--disable-web-security']
    },

    PhantomJS_custom: {
    base: 'PhantomJS',
    options: {
    viewportSize: {
    width: 1920,
    height: 1080
    },
    customHeaders: {
    DNT: "1"
    },
    windowName: 'my-window',
    settings: {
    webSecurityEnabled: false,
    userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36"
    }
    },
    flags: ['--load-images=true', '--debug=false', '--disk-cache=false'],
    debug: false
    }
    },

    // Have phantomjs exit if a ResourceError is encountered (useful if karma exits without killing phantom)
    phantomjsLauncher: {
    exitOnResourceError: false
    },

    // enable / disable watching file and executing tests whenever any file changes
    // Use `karma run' to run tests if set to false
    autoWatch: true,

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
    })
    }
    // vim:et:ts=2:​


  3. webapp/test/karma/context.html:
    <!DOCTYPE html>
    <!--
    Copy of node_modules/karma/static/context.html
    This is the execution context.
    Reloaded before every execution run.
    -->
    <html>
    <head>
    <title>Integration tests for com.acme.top.wdsp.mon with Component</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
    <meta charset="utf-8">
    </head>
    <body>
    <!-- The scripts need to be in the body DOM element, as some test running frameworks need the body
    to have already been created so they can insert their magic into it. For example, if loaded
    before body, Angular Scenario test framework fails to find the body and crashes and burns in
    an epic manner. -->
    <script src="context.js"></script>
    <script type="text/javascript">
    // Configure our Karma and set up bindings
    %CLIENT_CONFIG%
    window.__karma__.setupContext(window);

    // All served files with the latest timestamps
    %MAPPINGS%
    </script>
    <!-- copy from opaTestsWithComponent.qunit.html { -->
    <script type="text/javascript">
    window["sap-ushell-config"] = {
    defaultRenderer : "fiori2",
    renderers: {
    fiori2: {
    componentData: {
    config: {
    rootIntent: "ZF67_WDAYSP-monitor",
    search: "hidden"
    }
    }
    }
    },
    applications: {
    "ZF67_WDAYSP-monitor": {
    "additionalInformation": "SAPUI5.Component=com.acme.top.wdsp.mon",
    "applicationType": "URL",
    "url": "/base/webapp/"
    }
    }
    };
    </script>
    <script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js" id="sap-ushell-bootstrap"></script>

    <script id="sap-ui-bootstrap"
    src="https://sapui5.hana.ondemand.com/1.44.38/resources/sap-ui-core.js"
    data-sap-ui-animation="false"
    data-sap-ui-compatVersion="edge"
    data-sap-ui-frameOptions='deny'
    data-sap-ui-language="en"
    data-sap-ui-libs='sap.m, sap.ushell'
    data-sap-ui-preload='async'
    data-sap-ui-resourceroots='{
    "com.acme.top.wdsp.mon" : "/base/webapp/",
    "Arrangement": "/base/webapp/test/integration/arrangement/component/Arrangement"
    }'
    data-sap-ui-theme="sap_belize"
    data-sap-ui-xx-supportedLanguages="">
    </script>

    <!-- copy from opaTestsWithComponent.qunit.html } -->
    <!-- Dynamically replaced with <script> tags -->
    %SCRIPTS%
    <!-- copy from opaTestsWithComponent.qunit.html { -->
    <script src="https://sapui5.hana.ondemand.com/1.44.38/resources/sap/ui/qunit/qunit-css.js"></script>
    <script src="https://sapui5.hana.ondemand.com/1.44.38/resources/sap/ui/thirdparty/qunit.js"></script>
    <script src="https://sapui5.hana.ondemand.com/1.44.38/resources/sap/ui/qunit/qunit-junit.js"></script>
    <script src="https://sapui5.hana.ondemand.com/1.44.38/resources/sap/ui/qunit/qunit-coverage.js"
    data-sap-ui-cover-only="com/acme/top/wdsp/mon/"
    data-sap-ui-cover-never="[com/acme/top/wdsp/mon/localService/, com/acme/top/wdsp/mon/test/]">
    // TODO: data-sap-ui-cover-only="com/acme/top/wdsp/mon/" may be too narrow: include libraries as well?
    </script>

    <script>
    // https://github.com/SAP/openui5/blob/master/src/sap.m/test/sap/m/demokit/cart/webapp/test/integration...
    // we want to be able to load our tests asynchronously - pause QUnit until we loaded everything
    QUnit.config.autostart = false;

    sap.ui.getCore().attachInit(function () {
    "use strict";
    sap.ui.require([
    "sap/ui/fl/FakeLrepConnector",
    "com/acme/top/wdsp/mon/localService/mockserver",
    "com/acme/top/wdsp/mon/test/integration/AllJourneys"
    ], function (FakeLrepConnector, server) {
    //Fake LREP
    FakeLrepConnector.enableFakeConnector("/base/webapp/fakeLRep.json");

    // set up test service for local testing
    server.init({autoRespondAfter: 50});

    // configuration has been applied and the tests in the journeys have been loaded - start QUnit
    QUnit.start();
    });
    });
    </script>
    <!-- copy from opaTestsWithComponent.qunit.html } -->
    <script type="text/javascript">
    window.__karma__.loaded();
    </script>
    </body>
    </html>​


  4. Run the tests like this:
    #!/bin/sh -e
    export PATH=./node_modules/.bin:$PATH;
    export CHROME_BIN=chromium;
    npm install;
    Xvfb :99 & export XVFB_PID=$!;
    export DISPLAY=:99.0;
    npm test;​


  5. Read the reports:

    1. junit: reports/*/junit/webapp.xml

    2. coverage: reports/coverage/lcov/lcov-report/index.html




Summary


This blog post showed you how to run OPA5 integration tests for your Fiori launchpad app.

You are now also able to automate the running of the tests using Karma.

Further reading



Afterword


Thank you for reading through this blog post. I hope you found it useful. I hope it raised questions as well. Do follow the above, and other links to satisfy your curiosity - a sure way to deepen your understanding.

Please check out my other blog posts for interesting topics, like "Adding Reactive State Management with Validation to Existing UI5 Application – a Tutorial".
4 Comments
Labels in this area