Search code examples
angularkarma-jasminekarma-runnerpuppeteer

Karma tests using a Docker image of Chrome


For tests on CI server I want to use an image of Chrome instead of PhantomJS. I can do this with and without puppeteer but both require me to install chrome-stable package on the server. Thus I want a more lightweight method hence the use of the docker image.

For Karma, according to the docs, I must specify a script for the browser if i want to use a custom browser. Also Karma will pass 1 argument to this script, the url. For this requirement I also pulled the browserless docker image browserless quickstart.

I have created the script and can tun the Karma test but it looks like the karma enters failed state before the script is finished executing. Any ideas on where I am going wrong here. The container runs as expected and curl command to localhost:3000 returns what is expected.

Custom script

#!/bin/bash

set -euxo pipefail

URL="$1"
# http://localhost:9876/?id=30931514 this is the argument passed in by Karma
# Change the port to the port the docker container exposes 3000
DOCKER_URL=$( echo "$URL" | sed s/9876/3000/g )

killDockerContainer(){
 echo 'Killing Docker Container'
 docker rm -f browserless
}

trap "killDockerContainer; exit 0" EXIT


echo "Launching browserless: $DOCKER_URL"

docker run   -d   -p 3000:3000   --shm-size 2gb   --name browserless   --restart always   -e "DEBUG=browserless/chrome"   -e "MAX_CONCURRENT_SESSIONS=10" browserless/chrome 

Output from test run

3 01 2019 11:53:08.471:INFO [karma-server]: Karma v3.1.3 server started at 
http://0.0.0.0:9876/
03 01 2019 11:53:08.471:INFO [launcher]: Launching browsers /software/applications/app/browserle 14% building modules 38/38 modules 0 active03 01 2019 11:53:08.491:INFO [launcher]: Starting browser /software/applications/app/browserless.sh
03 01 2019 11:53:08.491:DEBUG [temp-dir]: Creating temp dir at /tmp/karma-34604420
03 01 2019 11:53:08.543:DEBUG [launcher]: /software/applications/app/browserless.sh http://local 17% building modules 60/60 modules 0 active03 01 2019 11:53:10.906:DEBUG [launcher]: Process /software/applications/app/browserless.sh exited with code 0
03 01 2019 11:53:10.907:ERROR [launcher]: Cannot start /software/applications/app/browserless.sh
        + URL='http://localhost:9876/?id=34604420'
++ sed s/9876/3000/g
++ echo 'http://localhost:9876/?id=34604420'
+ DOCKER_URL='http://localhost:3000/?id=34604420'
+ trap 'killDockerContainer; exit 0' EXIT
+ echo 'Launching browserless: http://localhost:3000/?id=34604420'
+ docker run -d -p 3000:3000 --shm-size 2gb --name browserless --restart always -e DEBUG=browserless/chrome -e MAX_CONCURRENT_SESSIONS=10 domain:9082/browserless/chrome 'http://localhost:3000/?id=34604420'
+ killDockerContainer
+ echo 'Killing Docker Container'
+ docker rm -f browserless
+ exit 0

03 01 2019 11:53:10.907:ERROR [launcher]: /software/applications/app/browserless.sh stdout: Launching browserless: http://localhost:3000/?id=34604420
b7144002bc2c9abc786dbdd015a8426c9afcbd0713f408cf3103e980e2278649
Killing Docker Container
browserless

Solution

  • I managed to get this going via the karma-selenium-webdriver-launcher (instead of a custom browser) plugin and using selenium/standalone-chrome (instead of browserless) as the image. I'm sure this method would have worked with browserless just as well but the selenium gives me more options for future such as different browsers and using selenium grids etc.

    Two important caveats are that the hostname that Karma uses must be changed in the config to the hostname of the vm karma is running on. The other name you will be conserned about will be where docker is running. This happens to be on localhost also so that will be used when specifying where karma expects the browser to be. Now knowing that much here was my karma config file to get this going.

    1 other important thing to consider will be the case where you want to run these tests on jenkins slaves thus hardcoding the hostname will not work. So you will have to find a way to get that address in your Karma config.

    const webdriver = require('selenium-webdriver');
    module.exports = function (config) {
    
      config.set({
        basePath: '',
        frameworks: ['jasmine', '@angular-devkit/build-angular'],
        plugins: [
          require('karma-jasmine'),
          require('karma-jasmine-html-reporter'),
          require('karma-coverage-istanbul-reporter'),
          require('karma-selenium-webdriver-launcher'),
          require('@angular-devkit/build-angular/plugins/karma')
        ],
        hostname: 'server_hostname', // Here we need to change from default localhost to the hostname of the server
        client:{
          clearContext: false // leave Jasmine Spec Runner output visible in browser
        },
        coverageIstanbulReporter: {
          dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
          fixWebpackSourcePaths: true,
          reporters : ['coverage'],
          preprocessors : {'src/app/*.ts' : 'coverage'}
        },
        angularCli: {
          environment: 'dev'
        },
        reporters: ['progress', 'kjhtml'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_DEBUG,
        autoWatch: false,
        browsers: ['Chrome-wd'],
         customLaunchers: {
            'Chrome-wd': {
                    base: 'SeleniumWebdriver',
                    browserName: 'Chrome',
                    getDriver: function() {
                    return new webdriver.Builder()
                            .forBrowser('chrome')
                            .usingServer('http://hostname:4444/wd/hub') // Docker is run using docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-chrome
                            .build()
                    }
            //      flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-software-rasterizer', '--disable-dev-shm-usage', '--remote-debugging-port=9222']
            }
        },
        singleRun: true
      });
    };