diff --git a/karma.conf.js b/karma.conf.js
new file mode 100644
index 0000000..b0559c7
--- /dev/null
+++ b/karma.conf.js
@@ -0,0 +1,23 @@
+module.exports = function(karma) {
+    karma.set({
+        plugins: ['karma-browserify', 'karma-chai', 'karma-mocha', 'karma-phantomjs-launcher'],
+
+        frameworks: ['browserify', 'chai', 'mocha'],
+
+        files: [
+            'src/**/*.js',
+            'test/**/*.js'
+        ],
+        preprocessors: {
+            'src/**/*.js' : ['browserify'],
+            'test/**/*.js': ['browserify']
+        },
+
+        browserify: {
+            debug: true,
+            transform: ['babelify']
+        },
+
+        browsers: ['PhantomJS']
+    });
+}
diff --git a/package.json b/package.json
index e6da28c..92006d2 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,11 @@
     "babelify": "^6.3.0",
     "browserify": "^11.1.0",
     "chai": "^3.2.0",
+    "karma": "^0.13.10",
+    "karma-browserify": "^4.3.0",
+    "karma-chai": "^0.1.0",
+    "karma-mocha": "^0.2.0",
+    "karma-phantomjs-launcher": "^0.2.1",
     "mocha": "^2.3.2",
     "uglify": "^0.1.5"
   },
diff --git a/test/clipboard-action.js b/test/clipboard-action.js
new file mode 100644
index 0000000..9d48d15
--- /dev/null
+++ b/test/clipboard-action.js
@@ -0,0 +1,42 @@
+import ClipboardAction from '../src/clipboard-action';
+
+describe('ClipboardAction', () => {
+    before(() => {
+        global.target = document.createElement('input');
+        target.setAttribute('id', 'foo');
+        document.body.appendChild(global.target);
+
+        global.trigger = document.createElement('button');
+        trigger.setAttribute('class', 'btn');
+        document.body.appendChild(global.trigger);
+    });
+
+    describe('#constructor', () => {
+        it('should throw an error since "data-action" is invalid', (done) => {
+            try {
+                new Clipboard({
+                    action: 'paste'
+                });
+            }
+            catch(e) {
+                done();
+            }
+        });
+
+        it('should throw an error since "data-target" do not match any element', (done) => {
+            try {
+                new ClipboardAction({
+                    target: 'zzz',
+                    trigger: global.trigger
+                });
+            }
+            catch(e) {
+                done();
+            }
+        });
+    });
+
+    after(() => {
+        document.body.innerHTML = '';
+    });
+});
diff --git a/test/clipboard.js b/test/clipboard.js
index 489b203..6f668ed 100644
--- a/test/clipboard.js
+++ b/test/clipboard.js
@@ -1,6 +1,12 @@
-var assert = chai.assert;
+import Clipboard from '../src/clipboard';
 
 describe('Clipboard', () => {
+    before(() => {
+        let button = document.createElement('button');
+        button.setAttribute('class', 'btn');
+        document.body.appendChild(button);
+    });
+
     describe('#constructor', () => {
         it('should throw an error since there was no arguments passed', done => {
             try {
@@ -20,51 +26,13 @@ describe('Clipboard', () => {
             }
         });
 
-        it('should create a NodeList and store it in a property', () => {
+        it('should create a NodeList from selector and store it in a "triggers" property', () => {
             let clipboard = new Clipboard('.btn');
             return assert.instanceOf(clipboard.triggers, NodeList);
         });
     });
 
-    describe('#validate', () => {
-        var elem = document.createElement('input');
-        let clipboard = new Clipboard('.btn');
-
-        it('should throw an error since there was no "data-target" or "data-text"', (done) => {
-            try {
-                clipboard.validate({
-                    currentTarget: elem
-                });
-            }
-            catch(e) {
-                done();
-            }
-        });
-
-        it('should throw an error since "data-action" is invalid', (done) => {
-            try {
-                elem.setAttribute('data-action', 'paste');
-
-                clipboard.validate({
-                    currentTarget: elem
-                });
-            }
-            catch(e) {
-                done();
-            }
-        });
-
-        it('should throw an error since "data-target" do not match any element', (done) => {
-            try {
-                elem.setAttribute('data-target', 'xyz');
-
-                clipboard.validate({
-                    currentTarget: elem
-                });
-            }
-            catch(e) {
-                done();
-            }
-        });
+    after(() => {
+        document.body.innerHTML = '';
     });
 });
diff --git a/test/index.html b/test/index.html
deleted file mode 100644
index a43c232..0000000
--- a/test/index.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Mocha Tests</title>
-  <link rel="stylesheet" href="../node_modules/mocha/mocha.css">
-</head>
-<body>
-  <div id="mocha"></div>
-
-  <div>
-    <p>Copy text from attribute</p>
-    <button class="btn" data-action="copy" data-text="hello">Copy</button>
-  </div>
-
-  <div>
-    <p>Copy text from non-input element</p>
-    <p id="paragraph">hello</p>
-    <button class="btn" data-action="copy" data-target="paragraph">Copy</button>
-  </div>
-
-  <div>
-    <p>Copy text from input element</p>
-    <input id="foo" type="text" value="hello">
-    <button class="btn" data-action="copy" data-target="foo">Copy</button>
-  </div>
-
-  <div>
-    <p>Cut text from textarea element</p>
-    <textarea id="bar">hello</textarea>
-    <button class="btn" data-action="cut" data-target="bar">Cut</button>
-  </div>
-
-  <script src="../dist/clipboard.min.js"></script>
-  <script src="../node_modules/mocha/mocha.js"></script>
-  <script src="../node_modules/chai/chai.js"></script>
-
-  <script>mocha.setup('bdd')</script>
-  <script src="clipboard.js"></script>
-  <script>
-    mocha.checkLeaks();
-    mocha.globals(['Clipboard']);
-    mocha.run();
-  </script>
-</body>
-</html>