diff --git a/demo/package.json b/demo/package.json
index 1817867a8b5e98e1bb813a056eeb47b14425266e..346eba99cd7de6de56dd8b34c952ddd8ef22549f 100644
--- a/demo/package.json
+++ b/demo/package.json
@@ -28,7 +28,6 @@
     "glob": "^7.1.6",
     "i18next-scanner": "^2.10.3",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.1.0",
     "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
diff --git a/demo/yarn.lock b/demo/yarn.lock
index e7fee0b4ac4074a4de0389c906b2a4c01c2d9c66..c19d6ec42ce7d777e25d8d21fdf67b61d1c8dedb 100644
--- a/demo/yarn.lock
+++ b/demo/yarn.lock
@@ -4147,11 +4147,6 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
-jsqr@^1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.3.1.tgz#515a766e58b00c80142f3a2dc4b8751100ceedcf"
-  integrity sha512-zCTP6Qd/WwjrpuHFkJuXc5opRdKprUr7eI7+JCCtcetThJt45qptu82MWQ+eET+FtDrMo7+BYjo3iD0XIq1L9Q==
-
 jszip@^3.2.2, jszip@^3.5.0:
   version "3.5.0"
   resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
@@ -4162,11 +4157,6 @@ jszip@^3.2.2, jszip@^3.5.0:
     readable-stream "~2.3.6"
     set-immediate-shim "~1.0.1"
 
-karma-chai@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/karma-chai/-/karma-chai-0.1.0.tgz#bee5ad40400517811ae34bb945f762909108b79a"
-  integrity sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=
-
 karma-chrome-launcher@^3.0.0, karma-chrome-launcher@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738"
diff --git a/package.json b/package.json
index b1ca1d8d693bf581dbe17143e18ed28e139daeb0..eb8f3f9307b3111ea0d067348bbd90777b15e9de 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "packages/*"
   ],
   "scripts": {
-    "test": "lerna run test"
+    "test": "lerna run --parallel test"
   },
   "author": "",
   "license": "LGPL-2.1-or-later",
diff --git a/packages/app-shell/rollup.config.js b/packages/app-shell/rollup.config.js
index acea48bc1bbb9f83ff858ed07758c7468d5e6966..9203f9a2e62af54e1f0262365781f2090c64a9bf 100644
--- a/packages/app-shell/rollup.config.js
+++ b/packages/app-shell/rollup.config.js
@@ -24,7 +24,7 @@ export default (async () => {
         onwarn: function (warning, warn) {
             // ignore chai warnings
             if (warning.code === 'CIRCULAR_DEPENDENCY') {
-              return;
+                return;
             }
             warn(warning);
         },
diff --git a/packages/auth/karma.conf.js b/packages/auth/karma.conf.js
index b6731e5705b9b7419fa3831747c0a3feb0bfd074..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/auth/karma.conf.js
+++ b/packages/auth/karma.conf.js
@@ -15,7 +15,7 @@ module.exports = function(config) {
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/auth/package.json b/packages/auth/package.json
index 2f6167836d2e0a497a007fce0d9d7e903f9e2cab..dd985976981719c9393ccdd726d2034b91ce33be 100644
--- a/packages/auth/package.json
+++ b/packages/auth/package.json
@@ -17,8 +17,8 @@
     "glob": "^7.1.6",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/auth/test/unit.js b/packages/auth/test/unit.js
index fee5e7c55ac981e3372134e587a2cd3a183fb19e..9995d6ef5e8a7f53ae586ad0701f7f9fc3811640 100644
--- a/packages/auth/test/unit.js
+++ b/packages/auth/test/unit.js
@@ -6,7 +6,7 @@ import '../src/dbp-auth-demo';
 suite('dbp-auth basics', () => {
   let node;
 
-  suiteSetup(async () => {
+  setup(async () => {
     node = document.createElement('dbp-auth-keycloak');
     node.setAttribute('url', 'someurl');
     node.setAttribute('realm', 'somerealm');
@@ -15,7 +15,7 @@ suite('dbp-auth basics', () => {
     await node.updateComplete;
   });
 
-  suiteTeardown(() => {
+  teardown(() => {
     node.remove();
   });
 
@@ -27,13 +27,13 @@ suite('dbp-auth basics', () => {
 suite('dbp-login-button', () => {
     let node;
   
-    suiteSetup(async () => {
+    setup(async () => {
       node = document.createElement('dbp-login-button');
       document.body.appendChild(node);
       await node.updateComplete;
     });
-  
-    suiteTeardown(() => {
+
+    teardown(() => {
       node.remove();
     });
   
@@ -45,13 +45,13 @@ suite('dbp-login-button', () => {
 suite('dbp-auth-demo basics', () => {
   let node;
 
-  suiteSetup(async () => {
+  setup(async () => {
     node = document.createElement('dbp-auth-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  suiteTeardown(() => {
+  teardown(() => {
     node.remove();
   });
 
diff --git a/packages/check-in-place-select/karma.conf.js b/packages/check-in-place-select/karma.conf.js
index 4b99e08adee238067dc7df307582333977129c4f..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/check-in-place-select/karma.conf.js
+++ b/packages/check-in-place-select/karma.conf.js
@@ -1,17 +1,21 @@
 // Trick to use the auto-downloaded puppeteer chrome binary
 process.env.CHROME_BIN = require('puppeteer').executablePath();
-const pkg = require('./package.json');
 
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/check-in-place-select/package.json b/packages/check-in-place-select/package.json
index ab6f3e30e3e76a5ab33a56ce31e7beae7b0a028f..36965425280ed34d5ad63f7e152e24d25e21bb4f 100644
--- a/packages/check-in-place-select/package.json
+++ b/packages/check-in-place-select/package.json
@@ -13,8 +13,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/check-in-place-select/rollup.config.js b/packages/check-in-place-select/rollup.config.js
index 0ea288b1a98e5332a1ca65624c556b20192a20d1..7c4bb333c801748e9847ac54c91e1d3e2a1ecc46 100644
--- a/packages/check-in-place-select/rollup.config.js
+++ b/packages/check-in-place-select/rollup.config.js
@@ -24,6 +24,10 @@ export default (async () => {
             sourcemap: true
         },
         onwarn: function (warning, warn) {
+            // ignore chai warnings
+            if (warning.code === 'CIRCULAR_DEPENDENCY') {
+                return;
+            }
             warn(warning);
         },
         plugins: [
diff --git a/packages/check-in-place-select/test/unit.js b/packages/check-in-place-select/test/unit.js
index d5498cebd707c198d3ba744bf54aa29d6e67b8da..c8d13045f1834de4ce0834bfc587ada390dbfbe1 100644
--- a/packages/check-in-place-select/test/unit.js
+++ b/packages/check-in-place-select/test/unit.js
@@ -1,38 +1,40 @@
+import {assert} from 'chai';
+
 import '../src/dbp-check-in-place-select.js';
 import '../src/demo.js';
 
-describe('dbp-check-in-place-select basics', () => {
+suite('dbp-check-in-place-select basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-check-in-place-select');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
 
-describe('dbp-check-in-place-select-demo basics', () => {
+suite('dbp-check-in-place-select-demo basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-check-in-place-select-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/data-table-view/test/unit.js b/packages/data-table-view/test/unit.js
index 531a2bfe5519e583c43026a6950439a7d11f88d8..7ce5434a6e2b5af58f82ae02691553bd552c4b90 100644
--- a/packages/data-table-view/test/unit.js
+++ b/packages/data-table-view/test/unit.js
@@ -6,13 +6,13 @@ import '../src/dbp-data-table-view-demo';
 suite('dbp-data-table-view basics', () => {
   let node;
 
-  suiteSetup(async () => {
+  setup(async () => {
     node = document.createElement('dbp-data-table-view');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  suiteTeardown(() => {
+  teardown(() => {
     node.remove();
   });
 
@@ -24,13 +24,13 @@ suite('dbp-data-table-view basics', () => {
 suite('dbp-data-table-view-demo basics', () => {
   let node;
 
-  suiteSetup(async () => {
+  setup(async () => {
     node = document.createElement('dbp-data-table-view-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  suiteTeardown(() => {
+  teardown(() => {
     node.remove();
   });
 
diff --git a/packages/file-handling/karma.conf.js b/packages/file-handling/karma.conf.js
index 6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/file-handling/karma.conf.js
+++ b/packages/file-handling/karma.conf.js
@@ -4,13 +4,18 @@ process.env.CHROME_BIN = require('puppeteer').executablePath();
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/file-handling/package.json b/packages/file-handling/package.json
index fe66a616fb786ea6e482bfb082b9e5ac2edccbe9..2fc9910df322cc27bcc0dd25dc3dba2865fbf47a 100644
--- a/packages/file-handling/package.json
+++ b/packages/file-handling/package.json
@@ -11,8 +11,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.0.1",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.0",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/file-handling/rollup.config.js b/packages/file-handling/rollup.config.js
index 3c597be182733aedd8571ba82b1de1005f963bca..48f897d03d5e462c6dd5c98e7a329f875ff4289c 100644
--- a/packages/file-handling/rollup.config.js
+++ b/packages/file-handling/rollup.config.js
@@ -25,6 +25,13 @@ export default (async () => {
             format: 'esm',
             sourcemap: true
         },
+        onwarn: function (warning, warn) {
+            // ignore chai warnings
+            if (warning.code === 'CIRCULAR_DEPENDENCY') {
+                return;
+            }
+            warn(warning);
+        },
         plugins: [
             del({
                 targets: 'dist/*'
diff --git a/packages/file-handling/test/unit.js b/packages/file-handling/test/unit.js
index fee6e05b8fcfe4f6e212d3590b5de58cdde26a11..ce2943ed70dcdecbd4cf9a71a1635936a09a47f8 100644
--- a/packages/file-handling/test/unit.js
+++ b/packages/file-handling/test/unit.js
@@ -1,38 +1,40 @@
+import {assert} from 'chai';
+
 import '../src/dbp-file-source';
 import '../src/demo';
 
-describe('dbp-file-source basics', () => {
+suite('dbp-file-source basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-file-source');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
 
-describe('dbp-file-source demo', () => {
+suite('dbp-file-source demo', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-file-source-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/knowledge-base-web-page-element-view/karma.conf.js b/packages/knowledge-base-web-page-element-view/karma.conf.js
index 6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/knowledge-base-web-page-element-view/karma.conf.js
+++ b/packages/knowledge-base-web-page-element-view/karma.conf.js
@@ -4,13 +4,18 @@ process.env.CHROME_BIN = require('puppeteer').executablePath();
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/knowledge-base-web-page-element-view/package.json b/packages/knowledge-base-web-page-element-view/package.json
index 7d54803176ce64d3e5d14f6a8a8704de60b6c00a..467bfd4e40d0a1a109bb49fb6f87921aa066e74c 100644
--- a/packages/knowledge-base-web-page-element-view/package.json
+++ b/packages/knowledge-base-web-page-element-view/package.json
@@ -11,8 +11,8 @@
     "@rollup/plugin-url": "^5.0.1",
     "chai": "^4.2.0",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/knowledge-base-web-page-element-view/rollup.config.js b/packages/knowledge-base-web-page-element-view/rollup.config.js
index df8835f3308578ab5d313038d2756fc19122127d..87bac9e11da46929631564887a8fcec251750e31 100644
--- a/packages/knowledge-base-web-page-element-view/rollup.config.js
+++ b/packages/knowledge-base-web-page-element-view/rollup.config.js
@@ -24,6 +24,10 @@ export default (async () => {
             sourcemap: true
         },
         onwarn: function (warning, warn) {
+            // ignore chai warnings
+            if (warning.code === 'CIRCULAR_DEPENDENCY') {
+                return;
+            }
             warn(warning);
         },
         plugins: [
diff --git a/packages/knowledge-base-web-page-element-view/test/unit.js b/packages/knowledge-base-web-page-element-view/test/unit.js
index 48263b321ca124d7f8b9d6833e1934701a17a390..43594f7feab3b17516d5a3a43f99037d0c8e55c5 100644
--- a/packages/knowledge-base-web-page-element-view/test/unit.js
+++ b/packages/knowledge-base-web-page-element-view/test/unit.js
@@ -1,38 +1,40 @@
+import {assert} from 'chai';
+
 import '../src/dbp-knowledge-base-web-page-element-view.js';
 import '../src/dbp-knowledge-base-web-page-element-view-demo.js';
 
-describe('dbp-knowledge-base-web-page-element-view basics', () => {
+suite('dbp-knowledge-base-web-page-element-view basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-knowledge-base-web-page-element-view');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
 
-describe('dbp-knowledge-base-web-page-element-view demo', () => {
+suite('dbp-knowledge-base-web-page-element-view demo', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-knowledge-base-web-page-element-view-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/language-select/karma.conf.js b/packages/language-select/karma.conf.js
index 6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/language-select/karma.conf.js
+++ b/packages/language-select/karma.conf.js
@@ -4,13 +4,18 @@ process.env.CHROME_BIN = require('puppeteer').executablePath();
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/language-select/package.json b/packages/language-select/package.json
index 23bd392a69b7b17f650c2a2371a8ff65f48d179c..6210a0ee8ff1f9d8a6c7f52a8862d31a8ceafd75 100644
--- a/packages/language-select/package.json
+++ b/packages/language-select/package.json
@@ -11,8 +11,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/language-select/rollup.config.js b/packages/language-select/rollup.config.js
index 96f5a585df45aee5d674510a473664dd25c397bc..27a7693759b18b0308063f0db3ae97d1d7e93395 100644
--- a/packages/language-select/rollup.config.js
+++ b/packages/language-select/rollup.config.js
@@ -20,6 +20,13 @@ export default {
         format: 'esm',
         sourcemap: true
     },
+    onwarn: function (warning, warn) {
+        // ignore chai warnings
+        if (warning.code === 'CIRCULAR_DEPENDENCY') {
+            return;
+        }
+        warn(warning);
+    },
     plugins: [
         del({
             targets: 'dist/*'
diff --git a/packages/language-select/test/unit.js b/packages/language-select/test/unit.js
index 54fb5b9d904d01c18bc3566cb4ef4822a623f029..b76d1f4c02b1027e18ea5fbabcf5251832168347 100644
--- a/packages/language-select/test/unit.js
+++ b/packages/language-select/test/unit.js
@@ -1,7 +1,9 @@
+import {assert, expect} from 'chai';
+
 import '../src/dbp-language-select.js';
 import '../src/demo.js';
 
-describe('dbp-language-select basics', () => {
+suite('dbp-language-select basics', () => {
   let node;
   let events = [];
 
@@ -9,7 +11,7 @@ describe('dbp-language-select basics', () => {
     events.push(e);
   }
 
-  beforeEach(async () => {
+  setup(async () => {
     events.length = 0;
     window.addEventListener('dbp-language-changed', handler);
     node = document.createElement('dbp-language-select');
@@ -17,16 +19,16 @@ describe('dbp-language-select basics', () => {
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
     window.removeEventListener('dbp-language-changed', handler);
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 
-  it('change language events', () => {
+  test('change language events', () => {
     node.lang = 'en';
     expect(node.next).to.equal('de');
     expect(events.length).to.equal(1);
@@ -35,7 +37,8 @@ describe('dbp-language-select basics', () => {
     expect(events.length).to.equal(2);
   });
 
-  it('change next', () => {
+  test('change next', () => {
+    expect(events.length).to.equal(0);
     node.lang = 'en';
     expect(node.next).to.equal('de');
     expect(node.lang).to.equal('en');
@@ -46,20 +49,20 @@ describe('dbp-language-select basics', () => {
   });
 });
 
-describe('dbp-language-select demo', () => {
+suite('dbp-language-select demo', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-language-select-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/matomo/karma.conf.js b/packages/matomo/karma.conf.js
index 6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/matomo/karma.conf.js
+++ b/packages/matomo/karma.conf.js
@@ -4,13 +4,18 @@ process.env.CHROME_BIN = require('puppeteer').executablePath();
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/matomo/package.json b/packages/matomo/package.json
index 965f5a53a37f4b5e2974faffd45e7201438de462..19794bcd3f556a1e8a414cf939a3a29b99c9fe26 100644
--- a/packages/matomo/package.json
+++ b/packages/matomo/package.json
@@ -12,8 +12,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/matomo/rollup.config.js b/packages/matomo/rollup.config.js
index 3f08b808fef8cb84344a80a05c7bea4ff85ead16..74206c840fa48d4755f9899402df662e40d2f94f 100644
--- a/packages/matomo/rollup.config.js
+++ b/packages/matomo/rollup.config.js
@@ -52,6 +52,10 @@ export default (async () => {
             sourcemap: true
         },
         onwarn: function (warning, warn) {
+            // ignore chai warnings
+            if (warning.code === 'CIRCULAR_DEPENDENCY') {
+                return;
+            }
             warn(warning);
         },
         plugins: [
diff --git a/packages/matomo/test/unit.js b/packages/matomo/test/unit.js
index bc2f3f5d70c605f2f1bbaf305b7e4fe891bb8140..cbdbc701823e0bb811cb3853514cf430e14974ea 100644
--- a/packages/matomo/test/unit.js
+++ b/packages/matomo/test/unit.js
@@ -1,3 +1,21 @@
-describe('dbp-matomo', () => {
-    it('should render', () => {});
-});
+import {assert} from 'chai';
+import '../src/dbp-matomo';
+
+suite('dbp-matomo', () => {
+    let node;
+  
+    setup(async () => {
+      node = document.createElement('dbp-matomo');
+      document.body.appendChild(node);
+      await node.updateComplete;
+    });
+  
+    teardown(() => {
+      node.remove();
+    });
+  
+    test('should render', () => {
+      assert.isNotNull(node.shadowRoot);
+    });
+  });
+  
\ No newline at end of file
diff --git a/packages/notification/karma.conf.js b/packages/notification/karma.conf.js
index 6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/notification/karma.conf.js
+++ b/packages/notification/karma.conf.js
@@ -4,13 +4,18 @@ process.env.CHROME_BIN = require('puppeteer').executablePath();
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/notification/package.json b/packages/notification/package.json
index dc68e35681bbe007ae5a07ca4f73b633b6da037a..7fae753c8907c10212e7e7de32999b1e92b38800 100644
--- a/packages/notification/package.json
+++ b/packages/notification/package.json
@@ -12,8 +12,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/notification/rollup.config.js b/packages/notification/rollup.config.js
index 639ffbd370ba8cac72afcef034a30870153570ba..ff5162a1ab89833a5125829502a3aa1e1efa1e90 100644
--- a/packages/notification/rollup.config.js
+++ b/packages/notification/rollup.config.js
@@ -21,6 +21,13 @@ export default {
         format: 'esm',
         sourcemap: true
     },
+    onwarn: function (warning, warn) {
+        // ignore chai warnings
+        if (warning.code === 'CIRCULAR_DEPENDENCY') {
+            return;
+        }
+        warn(warning);
+    },
     plugins: [
         del({
             targets: 'dist/*'
diff --git a/packages/notification/test/unit.js b/packages/notification/test/unit.js
index 786cfacbdfbc08bd4be6dcdd617b7d86749e25bc..e476e86b5cbe7b7536281b17f7f040c095a2f6c2 100644
--- a/packages/notification/test/unit.js
+++ b/packages/notification/test/unit.js
@@ -1,19 +1,21 @@
+import {assert} from 'chai';
+
 import '../src/dbp-notification';
 
-describe('dbp-notification basics', () => {
+suite('dbp-notification basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-notification');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/person-profile/karma.conf.js b/packages/person-profile/karma.conf.js
index 6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/person-profile/karma.conf.js
+++ b/packages/person-profile/karma.conf.js
@@ -4,13 +4,18 @@ process.env.CHROME_BIN = require('puppeteer').executablePath();
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/person-profile/package.json b/packages/person-profile/package.json
index 04fbc1bc30f544aa35082897e22b4b83c561920f..4e8599c43c2c8d95799a203ee006997e78d932a6 100644
--- a/packages/person-profile/package.json
+++ b/packages/person-profile/package.json
@@ -12,8 +12,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/person-profile/rollup.config.js b/packages/person-profile/rollup.config.js
index 977c5a50831238a7c91f02b5f5469920a0013cad..45b314c03ed2fd4eaf3a19caba81adb6f23bfeb5 100644
--- a/packages/person-profile/rollup.config.js
+++ b/packages/person-profile/rollup.config.js
@@ -25,6 +25,10 @@ export default (async () => {
             sourcemap: true
         },
         onwarn: function (warning, warn) {
+            // ignore chai warnings
+            if (warning.code === 'CIRCULAR_DEPENDENCY') {
+                return;
+            }
             warn(warning);
         },
         plugins: [
diff --git a/packages/person-profile/test/unit.js b/packages/person-profile/test/unit.js
index 8635f1135d49d8443e5a3e99ed0e0298595e4d36..db0670b4c2a60cfd9e66964d623d2eff8cfa0912 100644
--- a/packages/person-profile/test/unit.js
+++ b/packages/person-profile/test/unit.js
@@ -1,19 +1,21 @@
+import {assert} from 'chai';
+
 import '../src/dbp-person-profile-demo.js';
 
-describe('dbp-person-profile demo', () => {
+suite('dbp-person-profile demo', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-person-profile-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/person-select/karma.conf.js b/packages/person-select/karma.conf.js
index 4b99e08adee238067dc7df307582333977129c4f..f9cda6ad8cd84ec117f224be7da7c534b48b0490 100644
--- a/packages/person-select/karma.conf.js
+++ b/packages/person-select/karma.conf.js
@@ -1,17 +1,21 @@
 // Trick to use the auto-downloaded puppeteer chrome binary
 process.env.CHROME_BIN = require('puppeteer').executablePath();
-const pkg = require('./package.json');
 
 module.exports = function(config) {
   config.set({
     basePath: 'dist',
-    frameworks: ['mocha', 'chai'],
+    frameworks: ['mocha'],
+    client: {
+      mocha: {
+        ui: 'tdd',
+      },
+    },
     files: [
       {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
       {pattern: './**/*', included: false, watched: true, served: true},
     ],
     autoWatch: true,
-    browsers: ['ChromeHeadlessNoSandbox'],
+    browsers: ['ChromeHeadlessNoSandbox', 'FirefoxHeadless'],
     customLaunchers: {
       ChromeHeadlessNoSandbox: {
         base: 'ChromeHeadless',
diff --git a/packages/person-select/package.json b/packages/person-select/package.json
index 4d058df1a5f5da313aeec08a05f5bb5da51f06a9..851b73007bf7dcb4644d7417471213c61e92de41 100644
--- a/packages/person-select/package.json
+++ b/packages/person-select/package.json
@@ -13,8 +13,8 @@
     "chai": "^4.2.0",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/person-select/rollup.config.js b/packages/person-select/rollup.config.js
index 274fa5e75cbcb8336c1cc7bd405459e0f33a2863..35abaaa59d276eff7a88169fa7b9121e243c5e30 100644
--- a/packages/person-select/rollup.config.js
+++ b/packages/person-select/rollup.config.js
@@ -24,6 +24,10 @@ export default (async () => {
             sourcemap: true
         },
         onwarn: function (warning, warn) {
+            // ignore chai warnings
+            if (warning.code === 'CIRCULAR_DEPENDENCY') {
+                return;
+            }
             warn(warning);
         },
         plugins: [
diff --git a/packages/person-select/test/unit.js b/packages/person-select/test/unit.js
index ce02139539b58c3dac88eacdb9458b270d43dee5..fcefa8617b7147e1cdf6c2a4d1da07a871f1297a 100644
--- a/packages/person-select/test/unit.js
+++ b/packages/person-select/test/unit.js
@@ -1,38 +1,40 @@
+import {assert} from 'chai';
+
 import '../src/dbp-person-select.js';
 import '../src/demo.js';
 
-describe('dbp-person-select basics', () => {
+suite('dbp-person-select basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-person-select');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
 
-describe('dbp-person-select-demo basics', () => {
+suite('dbp-person-select-demo basics', () => {
   let node;
 
-  beforeEach(async () => {
+  setup(async () => {
     node = document.createElement('dbp-person-select-demo');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  afterEach(() => {
+  teardown(() => {
     node.remove();
   });
 
-  it('should render', () => {
-      expect(node).to.have.property('shadowRoot');
+  test('should render', () => {
+    assert.isNotNull(node.shadowRoot);
   });
 });
diff --git a/packages/qr-code-scanner/package.json b/packages/qr-code-scanner/package.json
index 98ed6202721e8f7677253bc6234fe0830bf4d1e7..596d6c0aa22f434c7d545b33379c85e148680d18 100644
--- a/packages/qr-code-scanner/package.json
+++ b/packages/qr-code-scanner/package.json
@@ -14,8 +14,8 @@
     "eslint-plugin-jsdoc": "^30.7.3",
     "i18next-scanner": "^2.10.2",
     "karma": "^5.1.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
+    "karma-firefox-launcher": "^1.3.0",
     "karma-mocha": "^2.0.1",
     "mocha": "^8.0.1",
     "puppeteer": "^5.3.1",
diff --git a/packages/qr-code-scanner/test/unit.js b/packages/qr-code-scanner/test/unit.js
index 0537c74d546512a6d81cdcf1c24e82054407ebd2..3e3340c13ce07bc04b66e463a28e0033ae0f1c2d 100644
--- a/packages/qr-code-scanner/test/unit.js
+++ b/packages/qr-code-scanner/test/unit.js
@@ -5,13 +5,13 @@ import '../src/dbp-qr-code-scanner';
 suite('dbp-qr-code-scanner basics', () => {
   let node;
 
-  suiteSetup(async () => {
+  setup(async () => {
     node = document.createElement('dbp-qr-code-scanner');
     document.body.appendChild(node);
     await node.updateComplete;
   });
 
-  suiteTeardown(() => {
+  teardown(() => {
     node.remove();
   });
 
diff --git a/yarn.lock b/yarn.lock
index c94931489ea4312408450aaee21d1b438c2a85a0..4c39816524cae754047f25b37c3ead56a3f41dd6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5072,11 +5072,6 @@ jszip@^3.2.2, jszip@^3.5.0:
     readable-stream "~2.3.6"
     set-immediate-shim "~1.0.1"
 
-karma-chai@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/karma-chai/-/karma-chai-0.1.0.tgz#bee5ad40400517811ae34bb945f762909108b79a"
-  integrity sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=
-
 karma-chrome-launcher@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738"