diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..ab40cef
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: node_js
+sudo: false
+before_install:
+  - "npm -g install npm"
+node_js:
+  - "4"
+  - "6"
+  - "0.10"
+  - "5"
+  - "0.12"
diff --git a/debian/changelog b/debian/changelog
index f97fa76..ae1574f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+node-slide (1.1.6+git20160623.1.77a5b62-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 07 Mar 2022 07:10:00 -0000
+
 node-slide (1.1.6-3) unstable; urgency=medium
 
   * Team upload
diff --git a/package.json b/package.json
index 7799184..f1aa8b3 100644
--- a/package.json
+++ b/package.json
@@ -8,13 +8,18 @@
   "description": "A flow control lib small enough to fit on in a slide presentation. Derived live at Oak.JS",
   "main": "./lib/slide.js",
   "dependencies": {},
-  "devDependencies": {},
+  "devDependencies": {
+    "tap": "^5.7.2"
+  },
   "engines": {
     "node": "*"
   },
+  "scripts": {
+    "test": "tap test/*.js"
+  },
   "repository": {
     "type": "git",
-    "url": "git://github.com/isaacs/slide-flow-control.git"
+    "url": "https://github.com/npm/slide-flow-control.git"
   },
   "license": "ISC"
 }
diff --git a/test/chain.js b/test/chain.js
new file mode 100644
index 0000000..2d17e20
--- /dev/null
+++ b/test/chain.js
@@ -0,0 +1,81 @@
+var test = require('tap').test
+var chain = require('../').chain
+
+test('works in simple case', function (t) {
+  var state = undefined
+  var subject = {
+    one: function (arg, cb) {
+      t.equal(arg, 'oneArg')
+      state = 'oneCall'
+      setTimeout(function () {
+        cb(null, { state: state, result: 'oneResult' })
+      }, 100)
+    },
+    two: function (arg, cb) {
+      t.equal(arg, 'twoArg')
+      state = 'twoCall'
+      setTimeout(function () {
+        cb(null, { state: state, result: 'twoResult' })
+      }, 100)
+    }
+  }
+
+  chain([
+    [subject, subject.one, 'oneArg'],
+    [subject, subject.two, 'twoArg']
+  ], function (err, results) {
+    t.same(results, [
+      { state: 'oneCall', result: 'oneResult' },
+      { state: 'twoCall', result: 'twoResult' }
+    ])
+    t.end()
+  })
+})
+
+test('skips rest of functions if error occur', function (t) {
+  var state = undefined
+  var subject = {
+    one: function (arg, cb) {
+      t.equal(arg, 'oneArg')
+      state = 'oneCall'
+      setTimeout(function () {
+        cb({ state: state, error: 'oneError' })
+      }, 100)
+    },
+    two: function (arg, cb) {
+      throw new Error('should be never called')
+    }
+  }
+
+  chain([
+    [subject, subject.one, 'oneArg'],
+    [subject, subject.two, 'twoArg']
+  ], function (err, results) {
+    t.same(err, { state: 'oneCall', error: 'oneError' })
+    t.end()
+  })
+})
+
+test('skips rest of functions if error occur', function (t) {
+  var state = undefined
+  var subject = {
+    one: function (arg, cb) {
+      t.equal(arg, 'oneArg')
+      state = 'oneCall'
+      setTimeout(function () {
+        cb({ state: state, error: 'oneError' })
+      }, 100)
+    },
+    two: function (arg, cb) {
+      throw new Error('should be never called')
+    }
+  }
+
+  chain([
+    [subject, subject.one, 'oneArg'],
+    [subject, subject.two, 'twoArg']
+  ], function (err, results) {
+    t.same(err, { state: 'oneCall', error: 'oneError' })
+    t.end()
+  })
+})