diff --git a/packages/person-select/.gitlab-ci.yml b/packages/person-select/.gitlab-ci.yml
index e7b6c12343a6bfa6a3d45a2ea19511979c29f5c2..9cadb871dc906edde0c952330b3a14f0b5767a58 100644
--- a/packages/person-select/.gitlab-ci.yml
+++ b/packages/person-select/.gitlab-ci.yml
@@ -14,6 +14,7 @@ test:
   stage: test
   script:
     - apt update
-    - apt install -y npm
+    - apt install -y npm chromium
     - npm install
     - npm run build
+    - npm test
diff --git a/packages/person-select/README.md b/packages/person-select/README.md
index 59d83e9cdcc36242bfd15467b53f935af13b5837..65ca8fd81e0afaa5b2ca49e50cad39dcd7918bad 100644
--- a/packages/person-select/README.md
+++ b/packages/person-select/README.md
@@ -15,6 +15,9 @@ npm install
 
 # constantly build dist/bundle.js and run a local web-server on port 8002 
 npm run watch-local
+
+# run tests
+npm test
 ```
 
 Jump to <http://localhost:8002> and you should get a Single Sign On login page.
diff --git a/packages/person-select/karma.conf.js b/packages/person-select/karma.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..901e07806c448e53fd84367917e67267a1836771
--- /dev/null
+++ b/packages/person-select/karma.conf.js
@@ -0,0 +1,23 @@
+// Trick to use the auto-downloaded puppeteer chrome binary
+process.env.CHROME_BIN = require('puppeteer').executablePath();
+
+module.exports = function(config) {
+  config.set({
+    basePath: 'dist',
+    frameworks: ['mocha', 'chai'],
+    files: [
+      './bundle.js',
+      {pattern: './**/*', included: false, watched: true, served: true},
+    ],
+    autoWatch: true,
+    browsers: ['ChromeHeadlessNoSandbox'],
+    customLaunchers: {
+      ChromeHeadlessNoSandbox: {
+        base: 'ChromeHeadless',
+        flags: ['--no-sandbox']
+      }
+    },
+    singleRun: false,
+    logLevel: config.LOG_ERROR
+  });
+}
diff --git a/packages/person-select/package.json b/packages/person-select/package.json
index 8f9d0574402ab7f657bad6c931be6f6cd1f1c5fe..7a5bcceafeb01d89bc641482515ebc3e8569efd1 100644
--- a/packages/person-select/package.json
+++ b/packages/person-select/package.json
@@ -3,7 +3,14 @@
   "version": "1.0.0",
   "main": "src/index.js",
   "devDependencies": {
+    "karma": "^4.2.0",
+    "karma-chai": "^0.1.0",
+    "karma-chrome-launcher": "^3.0.0",
+    "karma-mocha": "^1.3.0",
     "node-sass": "^4.12.0",
+    "puppeteer": "^1.15.0",
+    "mocha": "^6.2.0",
+    "chai": "^4.2.0",
     "rollup": "^1.11.3",
     "rollup-plugin-commonjs": "^9.3.4",
     "rollup-plugin-copy": "^2.0.1",
@@ -13,6 +20,7 @@
     "rollup-plugin-terser": "^4.0.4",
     "rollup-plugin-json": "^4.0.0",
     "rollup-plugin-replace": "^2.2.0",
+    "rollup-plugin-multi-entry": "^2.1.0",
     "i18next-scanner": "^2.10.2",
     "vpu-auth": "file:./vendor/auth",
     "vpu-common": "file:./vendor/common"
@@ -31,9 +39,11 @@
     "build-dev": "rollup -c --environment BUILD:development",
     "build-prod": "rollup -c --environment BUILD:production",
     "build-demo": "rollup -c --environment BUILD:demo",
+    "build-test": "rollup -c --environment BUILD:test",
     "i18next": "i18next-scanner",
     "watch": "npm run watch-local",
     "watch-local": "rollup -c --watch",
-    "watch-dev": "rollup -c --watch --environment BUILD:development"
+    "watch-dev": "rollup -c --watch --environment BUILD:development",
+    "test": "npm run build-test && karma start --singleRun"
   }
 }
diff --git a/packages/person-select/rollup.config.js b/packages/person-select/rollup.config.js
index cd666342d9feed16008f501af6efdb3a2374cb11..593fbce3f715f64e43de1fcc9f8a983138e610f5 100644
--- a/packages/person-select/rollup.config.js
+++ b/packages/person-select/rollup.config.js
@@ -6,17 +6,19 @@ import {terser} from "rollup-plugin-terser";
 import json from 'rollup-plugin-json';
 import replace from "rollup-plugin-replace";
 import serve from 'rollup-plugin-serve';
+import multiEntry from 'rollup-plugin-multi-entry';
 
 const build = (typeof process.env.BUILD !== 'undefined') ? process.env.BUILD : 'local';
 console.log("build: " + build);
 
 export default {
-    input: 'src/demo.js',
+    input: (build != 'test') ? 'src/demo.js' : 'test/**/*.js',
     output: {
         file: 'dist/bundle.js',
         format: 'esm'
     },
     plugins: [
+        multiEntry(),
         resolve(),
         commonjs(),
         json(),
@@ -28,7 +30,7 @@ export default {
             minimize: false,
             plugins: []
         }),
-        (build !== 'local') ? terser() : false,
+        (build !== 'local' && build !== 'test') ? terser() : false,
         copy({
             targets: [
                 'assets/index.html',
diff --git a/packages/person-select/test/unit.js b/packages/person-select/test/unit.js
new file mode 100644
index 0000000000000000000000000000000000000000..14f15338ca76b99c0e87def20f5a1a3e189d7845
--- /dev/null
+++ b/packages/person-select/test/unit.js
@@ -0,0 +1,19 @@
+import '../src/person-select';
+
+describe('vpu-library-person-select basics', () => {
+  let node;
+
+  beforeEach(async () => {
+    node = document.createElement('vpu-library-person-select');
+    document.body.appendChild(node);
+    await node.updateComplete;
+  });
+
+  afterEach(() => {
+    node.remove();
+  });
+
+  it('should render', () => {
+      expect(node).to.have.property('shadowRoot');
+  });
+});