Karma Configuration for Authentication During SAPUI5 OPA Testing

Scenario:

I want to execute SAPUI5 OPA tests base on backend OData services directly instead of pre-prepared mock-up data or generated mock-up by mock server. The challenge here is authentication against backend server when the tests are executed via Jenkins. Unlike manual execution by which user and password can be provided manually, automatic Jenkins execution has to overcome the BASIC authentication without any manual interventions. 

Investigation:

A Nodejs web server is started by Karma when launch the tests. The first thing I need to verify is whether the web server has the ability of proxying OData requests to designated backend servers or not. I searched keywords 'Karma proxy' and found below Karma official page about its configurations. 
proxies: {
  '/static': 'http://gstatic.com',
  '/web': 'http://localhost:9000',
  '/img/': '/base/test/images/',
  '/proxyfied': {
    'target': 'http://myserver.localhost',
    'changeOrigin': true
  }
}
I started to test the proxy configurations but kept failing because of port issue. The embedded proxy always adds default port 80 for HTTP or 443 for HTTPS if there is no port found from the proxy configuration. There are two kinds of URL I can use to access my backend ABAP server. 
1: https://host:44300?sap-client=500
2: https://wdhost
The host in the first option is the ABAP server can be determined from transaction code SMICM. Client parameter is needed to access required client. But the parameter is not necessary for the second opinion since client info is included in the wdhost which is configured and routed by SAP Web Dispatch. And no port should be presented. Now the situation is, if I use the option one, I need the client URL query parameter, but Karma proxy configuration is not supporting this. If I use the option two, it always adds 443 as the port. Requests fail if port is added. Since it is impossible to change the port logic, I have to adopt the first option. But the pre-condition is I can find a way to give additional sap-client parameter.
To overcome it, my first try was Karma custom middle were. As described in the official docs, Karma supports custom middleware by simple configuration. I had configured as below to test it.
    middleware: ['custom'],
    plugins: [
      'karma-*',
      {
        'middleware:custom': ['factory', function(config) {
          return function(request, response, next) {
            console.log(request.url);
            next();
          };
        }]
      }
    ]
Unfortunately, it didn't work. My intention was, for those OData requests, I would adjust the 'req' object accordingly before it was forwarded to backend ABAP sever by proxy. The reality is those requests were handled by Karma proxy middleware without entering the configured custom middleware. Obviously, it is not the solution.

What I want is somewhere before entering proxy handling. After checking the proxy code, I found 'proxyReq' and 'proxyRes' events were emitted before and after requests were sent.
 
I realized there may be some hooks provided by Karma. And finally, I found it from the official docs.
proxyReq: function(proxyReq, req, res, options) {
  proxyReq.setHeader('Referer', 'https://www.example.com/'); 
}
proxyRes: function(proxyRes, req, res) {
  if (proxyRes.headers['set-cookie']) {
    proxyRes.headers['set-cookie'] = proxyRes.headers['set-cookie'].map(function (cookie) {
      return cookie.replace(/\s*secure;?/i, '');
    })
  }
}

Implementation:

proxyReq: 
  1. Adding HTTP header 'Authorization'. The value of this header is `Basic btoa("USER:PS")`.
  2. Adding additional URL parameter 'sap-client'.
  3. Adding HTTP header 'x-csrf-token'. For the first metadata request, add this header with value 'fetch' to request a token. 
  4. Adding HTTP header 'x-csrf-token' with a real token received from metadata request (check proxyRes.1).
proxyRes:
  1. Keeping returned 'x-csrf-token' from the first metadata request in a variable (check proxyReq.4). 
With hooks 'proxyReq' and 'proxyRes', credentials information is able to be sent to backend servers successfully without any manual interventions. The last point is how to get the user and password when it is executed in Jenkins. The OPA test will be started by node command, such as 'npm run karma' which is configured in package.json. Before executing this command, pre-configured Jenkins credentials can be retrieved by code (groovy); and pass it to node command by 'npm run karma --set-user USER --set-ps PS'. Then it is easy to get it by process.argv and set it to the request HTTP header.

Error: UNABLE_TO_VERIFY_LEAF_SIGNATURE

This error is raised when the SAPUI5 SDK resides in an internal server which is only accessible via HTTPS. To solve this, required certificate needed before starting karma. 
  1. Prepare required certificate file, for example abc.crt. Put it into the root folder of the SAPUI5 project.
  2. Add module cross-env. npm install cross-env
  3. Update script for starting karma. The original is 'karma start karma.conf.js'. And change it to 'cross-env NODE_EXTRA_CA_CERTS=./abc.crt karma start karma.conf.js'
With explicit passing certificate, this error can be fixed.

Comments