Skip to the content.

Karma, code coverage, and azure dev ops

There is quite a bit of info out there on wiring all this up for Angular and webPack projects, but not so much for the vanillaJs ES2022 customElement low tooling project.

The goal is to get a code coverage report to show in AzureDevOps pipelines.

Workingh backward:

I’m sure I’m missing some parts or details. But this is all I can share right now. Good luck, hit me up with an issue on my blog github site if you have an issue.

Tooling used/needed

Generic package.json

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "dist/src/index.js",
  "module": "dist/src/index.js",
  "browser": "dist/browser/index.js",
  "types": "dist/src/index.d.ts",
  "type": "module",
  "author": "Eric",
  "contributors": [],
  "scripts": {
    "build": "tsc --project tsconfig.json & rollup --config ./rollup.browser.cfg & rollup --config ./rollup.karma.cfg",
    "serve": "node static-server.cjs",
    "test:dev": "karma start karma.conf.cjs",
    "test:report": "karma start karma.azure.cfg.cjs",
    "karma": "karma start karma.conf.cjs",
    "rollup": "rollup --config ./rollup.browser.cfg",
    "rollup-watch": "rollup --config ./rollup.browser.cfg --watch",
    "rollup:test": "rollup --config ./rollup.karma.cfg",
    "rollup:test-watch": "rollup --config ./rollup.karma.cfg --watch",
    "artifacts-auth": "vsts-npm-auth -config .npmrc"
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "license": "ISC",
  "files": [
    "./src",
    "dist"
  ],
  "devDependencies": {
    "@custom-elements-manifest/analyzer": "^0.6.3",
    "@rollup/plugin-node-resolve": "^15.0.1",
    "@rollup/plugin-typescript": "^8.3.4",
    "@types/jasmine": "^4.0.0",
    "@types/node": "^17.0.29",
    "@typescript-eslint/eslint-plugin": "^5.33.0",
    "@typescript-eslint/parser": "^5.33.0",
    "del-cli": "^5.0.0",
    "eslint": "^8.21.0",
    "eslint-plugin-custom-elements": "^0.0.6",
    "eslint-plugin-jasmine": "^4.1.3",
    "eslint-plugin-no-unsanitized": "^4.0.1",
    "jasmine": "^3.10.0",
    "jasmine-core": "^3.10.1",
    "karma": "^6.4.1",
    "karma-chrome-launcher": "^3.1.0",
    "karma-coverage": "^2.2.0",
    "karma-jasmine": "^5.1.0",
    "karma-jasmine-html-reporter": "^1.7.0",
    "karma-requirejs": "^1.1.0",
    "karma-sinon": "^1.0.5",
    "karma-sourcemap-loader": "^0.3.8",
    "karma-spec-reporter": "^0.0.32",
    "rollup": "^2.60.2",
    "rollup-plugin-ts": "^3.2.0",
    "sinon": "^11.1.2",
    "static-server": "^2.2.1",
    "ts-node": "^10.9.1",
    "ts-sinon": "^2.0.2",
    "typescript": "^4.5.4"
  }
}

karma.azure.conf.cjs

Might not need all of this, but after trying so many different things. It works and I’m moving on.

// Karma configuration
// How to configure in AzDo.
// the Cobertura report is in the coverage/cobertura.xml file
// https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/publish-code-coverage-results-v1?view=azure-pipelines

module.exports = function (config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '', //process.cwd(), //'', //require('path').join(__dirname, ''),

    // frameworks to use
    // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
    frameworks: ['jasmine', 'sinon'],
    // client: {
    //   clearContext: false // leave Jasmine Spec Runner output visible in browser
    // },

    // list of files / patterns to load in the browser
    files: [
      'dist/browser/test/index.js'
    ],

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

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
    preprocessors: {
      '**/*.js': ['sourcemap'],
      './dist/browser/test/index.js':['coverage']  // IMPORTANT FOR generating the report.
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
    //reporters: ['progress'],
    reporters: ['kjhtml', 'spec', 'progress', 'coverage'],
    coverageReporter: {
      reporters:[
        { type: 'html', subdir: 'report-html' },
        { type: 'lcov', subdir: 'report-lcov' },
        { type: 'cobertura', subdir: '.', file: 'cobertura.xml' },
        { type: 'text', subdir: '.', file: 'text.txt' },
        { type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
      ],
      // lltype: 'text', //'html'
      dir: 'coverage/', // require('path').join(__dirname, '../coverage'),
      includeAllSources: true
    },

    // web server port
    port: 9876,


    // 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_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,


    // start these browsers
    // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
    browsers: ['ChromeHeadless'],
    customLaunchers:{
      HeadlessChrome:{
        base: 'ChromeHeadless',
        flags: [
          '--no-sandbox',
          '--disable-web-security', '--disable-site-isolation-trials'
        ]
      }
    },

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

    // Concurrency level
    // how many browser instances should be started simultaneously
    concurrency: Infinity,

    mime: {
      'text/x-typescript': ['ts']
    },

  });
};

rollup.karma.cfg

//import typescript from '@rollup/plugin-typescript';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import ts from 'rollup-plugin-ts';
export default [
  {
    input: './test/index.spec.ts',
    output: {
      file: './dist/browser/test/index.js',
      format: 'es',
      sourcemap: true
    },
    plugins: [
       nodeResolve(),
      ts({
        tsconfig: './tsconfig.karma.json'
      })
    ]
  }
];

tsconfig.karma.json

{
  "include": ["."],
  "exclude": [
    "./dist",
    "node_modules"
  ],
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "outDir": "./dist/browser/test",
    "noEmit": false
  },
  "paths": {
    "sinon": [
      "node_modules/sinon/pkg/sinon-esm.js"
    ],
    "ts-sinon": [
      "node_modules/ts-sinon/dist/index.ts"
    ],
    "@sinon": [
      "node_modules/@sinonjs/commons/types/index.d.ts"
    ],
    "jasmine-core": [
      "node_modules/jasmine-core/lib/jasmine-core.js"
    ]
  },
  "types": [
    "jasmine",
    "sinon"
  ]
}

azure-pipelines.yml


trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: NodeTool@0
  inputs:
    versionSpec: '18.x'

- script: |
    npm install
    npm run build
  displayName: 'npm install and build'

# NOTE: if tests are not built as part of build, set this up
# - task: Npm@1
#   displayName: 'build unit tests'
#   inputs:
#     command: 'custom'
#     customCommand: 'run rollup:test'

# Make sure karma is not in watch mode and this is the config that generates the report.
- task: Npm@1
  displayName: 'run unit tests'
  inputs:
    command: 'custom'
    customCommand: 'run test:report'

- task: PublishCodeCoverageResults@1
  displayName: 'Publish code coverage'
  inputs:
    codeCoverageTool: Cobertura
    summaryFileLocation: 'coverage/cobertura.xml'