diff --git a/packages/data-table-view/.gitlab-ci.yml b/packages/data-table-view/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..41502e66dfaa6d94a1b1944dc3fb19baa5ed8e39
--- /dev/null
+++ b/packages/data-table-view/.gitlab-ci.yml
@@ -0,0 +1,16 @@
+image: registry.gitlab.tugraz.at/vpu/webcomponents/common/main:v1
+
+before_script:
+  - "sed -i 's|git@gitlab.tugraz.at:VPU|../..|g' .gitmodules"
+  - git submodule sync
+  - git submodule update --init
+
+stages:
+  - test
+
+test:
+  stage: test
+  script:
+    - npm install
+    - npm run build
+    - npm test
diff --git a/packages/data-table-view/karma.conf.js b/packages/data-table-view/karma.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..b6731e5705b9b7419fa3831747c0a3feb0bfd074
--- /dev/null
+++ b/packages/data-table-view/karma.conf.js
@@ -0,0 +1,28 @@
+// 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'],
+    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'],
+    customLaunchers: {
+      ChromeHeadlessNoSandbox: {
+        base: 'ChromeHeadless',
+        flags: ['--no-sandbox']
+      }
+    },
+    singleRun: false,
+    logLevel: config.LOG_ERROR
+  });
+}
diff --git a/packages/data-table-view/package.json b/packages/data-table-view/package.json
index 067ec8830bc7a5fb838cc8ac737aaa2a51e1b6a4..7f3bd00ca83ab60a53b92806fb7fcf72249133a8 100644
--- a/packages/data-table-view/package.json
+++ b/packages/data-table-view/package.json
@@ -3,27 +3,26 @@
   "version": "1.0.0",
   "main": "src/vpu-data-table-view.js",
   "devDependencies": {
+    "chai": "^4.2.0",
+    "glob": "^7.1.4",
+    "i18next-scanner": "^2.10.2",
     "karma": "^4.2.0",
-    "karma-chai": "^0.1.0",
     "karma-chrome-launcher": "^3.0.0",
     "karma-mocha": "^1.3.0",
+    "mocha": "^6.2.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": "^10.0.2",
     "rollup-plugin-consts": "^1.0.1",
     "rollup-plugin-copy": "^3.1.0",
     "rollup-plugin-delete": "^1.1.0",
     "rollup-plugin-json": "^4.0.0",
-    "rollup-plugin-multi-entry": "^2.1.0",
     "rollup-plugin-node-resolve": "^5.2.0",
     "rollup-plugin-postcss": "^2.0.3",
     "rollup-plugin-serve": "^1.0.1",
     "rollup-plugin-terser": "^5.1.1",
     "rollup-plugin-url": "^2.2.2",
-    "i18next-scanner": "^2.10.2",
     "vpu-auth": "file:./vendor/auth",
     "vpu-common": "file:./vendor/common"
   },
diff --git a/packages/data-table-view/rollup.config.js b/packages/data-table-view/rollup.config.js
index 0a22d9bc72117ab757d0ee0400c2c15b6ce369f4..c0fbd8a36252e520930f5aa64b313ed2a72208b2 100644
--- a/packages/data-table-view/rollup.config.js
+++ b/packages/data-table-view/rollup.config.js
@@ -1,21 +1,22 @@
 import path from 'path';
+import glob from 'glob';
 import resolve from 'rollup-plugin-node-resolve';
 import commonjs from 'rollup-plugin-commonjs';
 import copy from 'rollup-plugin-copy';
 import {terser} from "rollup-plugin-terser";
 import json from 'rollup-plugin-json';
 import serve from 'rollup-plugin-serve';
-import multiEntry from 'rollup-plugin-multi-entry';
-import url from "rollup-plugin-url";
+import urlPlugin from "rollup-plugin-url";
 import consts from 'rollup-plugin-consts';
-import del from 'rollup-plugin-delete'
+import del from 'rollup-plugin-delete';
+import chai from 'chai';
 
 const pkg = require('./package.json');
 const build = (typeof process.env.BUILD !== 'undefined') ? process.env.BUILD : 'local';
 console.log("build: " + build);
 
 export default {
-    input: (build != 'test') ? ['src/vpu-data-table-view.js', 'src/vpu-data-table-view-demo.js'] : 'test/**/*.js',
+    input: (build != 'test') ? ['src/vpu-data-table-view.js', 'src/vpu-data-table-view-demo.js'] : glob.sync('test/**/*.js'),
     output: {
         dir: 'dist',
         entryFileNames: '[name].js',
@@ -28,6 +29,10 @@ export default {
         if (warning.code === 'MODULE_LEVEL_DIRECTIVE') {
             return;
         }
+        // ignore chai warnings
+        if (warning.code === 'CIRCULAR_DEPENDENCY') {
+            return;
+          }
         throw new Error(warning);
     },
     watch: {
@@ -37,7 +42,6 @@ export default {
         del({
             targets: 'dist/*'
         }),
-        (build == 'test') ? multiEntry() : false,
         consts({
             environment: build,
         }),
@@ -48,9 +52,21 @@ export default {
             }
         }),
         commonjs({
-            include: 'node_modules/**'
+            include: 'node_modules/**',
+            namedExports: {
+                'chai': Object.keys(chai),
+              }
         }),
         json(),
+        urlPlugin({
+            limit: 0,
+            include: [
+              "node_modules/bulma/**/*.css",
+              "node_modules/bulma/**/*.sass",
+            ],
+            emitFiles: true,
+            fileName: 'shared/[name].[hash][extname]'
+          }),
         (build !== 'local' && build !== 'test') ? terser() : false,
         copy({
             targets: [
diff --git a/packages/data-table-view/test/unit.js b/packages/data-table-view/test/unit.js
new file mode 100644
index 0000000000000000000000000000000000000000..1ff2e129ca08078861a916b102bd1c9dcc021296
--- /dev/null
+++ b/packages/data-table-view/test/unit.js
@@ -0,0 +1,40 @@
+import {assert} from 'chai';
+
+import '../src/vpu-data-table-view';
+import '../src/vpu-data-table-view-demo';
+
+suite('vpu-data-table-view basics', () => {
+  let node;
+
+  suiteSetup(async () => {
+    node = document.createElement('vpu-data-table-view');
+    document.body.appendChild(node);
+    await node.updateComplete;
+  });
+
+  suiteTeardown(() => {
+    node.remove();
+  });
+
+  test('should render', () => {
+    assert(node.shadowRoot !== undefined);
+  });
+});
+
+suite('vpu-data-table-view-demo basics', () => {
+  let node;
+
+  suiteSetup(async () => {
+    node = document.createElement('vpu-data-table-view-demo');
+    document.body.appendChild(node);
+    await node.updateComplete;
+  });
+
+  suiteTeardown(() => {
+    node.remove();
+  });
+
+  test('should render', () => {
+    assert(node.shadowRoot !== undefined);
+  });
+});