(#5908) - bundle all browser test files
diff --git a/.eslintignore b/.eslintignore
index d48edb2..a6feec8 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -2,4 +2,5 @@
 tests/integration/cordova/**/*.js
 tests/performance-bundle.js
 tests/fuzzy/seedrandom.min.js
-tests/integration/utils-bundle.js
\ No newline at end of file
+tests/integration/utils-bundle.js
+tests/integration/tests-bundle.js
diff --git a/.gitignore b/.gitignore
index 4f436b4..7830585 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,5 @@
 src_browser/
 lerna-debug.log
 tests/integration/utils-bundle.js
+tests/integration/tests-bundle.js
 *.heapsnapshot
diff --git a/bin/dev-server.js b/bin/dev-server.js
index b6cbc07..babd865 100755
--- a/bin/dev-server.js
+++ b/bin/dev-server.js
@@ -9,6 +9,7 @@
 var buildPouchDB = require('./build-pouchdb');
 var browserify = require('browserify');
 var fs = require('fs');
+var concatFiles = require('fileconcat');
 
 var queryParams = {};
 
@@ -45,10 +46,19 @@
   });
 }
 
-function rebuildTestUtils() {
+function rebuildTests() {
   rebuildPromise = rebuildPromise.then(function () {
-    return browserifyPromise('tests/integration/utils.js',
-      'tests/integration/utils-bundle.js');
+    return Promise.all([
+      browserifyPromise('tests/integration/utils.js',
+        'tests/integration/utils-bundle.js'),
+      concatFiles([
+        'tests/integration/aa.*.js',
+        'tests/integration/test.*.js',
+        'tests/integration/browser.*.js'
+      ], 'tests/integration/tests-bundle.js').then(function () {
+        console.log('Rebuilt tests/integration/tests-bundle.js');
+      })
+    ]);
   }).then(function () {
     console.log('Rebuilt tests/integration/utils-bundle.js');
   }).catch(console.error);
@@ -66,8 +76,11 @@
 function watchAll() {
   watch(['packages/node_modules/**/src/**/*.js'],
     debounce(rebuildPouch, 700, {leading: true}));
-  watch(['tests/integration/utils.js'],
-    debounce(rebuildTestUtils, 700, {leading: true}));
+  watch([
+    'tests/integration/aa.*.js',
+    'tests/integration/test.*.js',
+    'tests/integration/browser.*.js'
+  ], debounce(rebuildTests, 700, {leading: true}));
   watch(['tests/performance/**/*.js'],
     debounce(rebuildPerf, 700, {leading: true}));
 }
@@ -80,7 +93,7 @@
   }
   return Promise.all([
     rebuildPouch(),
-    rebuildTestUtils(),
+    rebuildTests(),
     rebuildPerf()
   ]);
 }).then(function () {
diff --git a/bin/test-node.sh b/bin/test-node.sh
index 647f266..708d9e8 100755
--- a/bin/test-node.sh
+++ b/bin/test-node.sh
@@ -12,7 +12,7 @@
 fi
 
 if [ $TYPE = "integration" ]; then
-    TESTS_PATH="tests/integration/test.*.js"
+    TESTS_PATH="tests/integration/{aa,test,node}.*.js"
 fi
 if [ $TYPE = "fuzzy" ]; then
     TESTS_PATH="tests/fuzzy/test.*.js"
@@ -31,7 +31,7 @@
     ./node_modules/.bin/mocha \
         $BAIL_OPT \
         --timeout $TIMEOUT \
-        --require=./tests/integration/node.setup.js \
+        --require=./tests/integration/setup-node.js \
         --reporter=$REPORTER \
         --grep=$GREP \
         $TESTS_PATH
@@ -41,7 +41,7 @@
        ./node_modules/mocha/bin/_mocha -- \
         $BAIL_OPT \
         --timeout $TIMEOUT \
-        --require=./tests/integration/node.setup.js \
+        --require=./tests/integration/setup-node.js \
         --reporter=$REPORTER \
         --grep=$GREP \
         $TESTS_PATH
diff --git a/package.json b/package.json
index 1dfd558..2ede04d 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,8 @@
     "publish-site": "sh bin/publish-site.sh",
     "build-site": "node ./bin/build-site.js",
     "report-coverage": "npm run test-coverage && istanbul-coveralls --no-rm",
-    "build-test": "npm run build-test-utils && npm run build-perf",
+    "build-test": "npm run build-test-utils && npm run build-test-bundle && npm run build-perf",
+    "build-test-bundle": "cat tests/integration/{aa,test,browser}.*.js > tests/integration/tests-bundle.js",
     "build-test-utils": "browserify tests/integration/utils.js > tests/integration/utils-bundle.js",
     "build-perf": "browserify tests/performance/index.js > tests/performance-bundle.js",
     "set-version": "node bin/set-version.js",
@@ -73,6 +74,7 @@
     "eslint": "3.9.0",
     "express": "4.14.0",
     "express-pouchdb": "2.1.0",
+    "fileconcat": "^2.0.1",
     "find-requires": "0.2.2",
     "glob": "7.1.1",
     "http-server": "0.9.0",
diff --git a/tests/integration/test.aa.setup.js b/tests/integration/aa.setup.js
similarity index 100%
rename from tests/integration/test.aa.setup.js
rename to tests/integration/aa.setup.js
diff --git a/tests/integration/index.html b/tests/integration/index.html
index e47eb3c..1880d7e 100644
--- a/tests/integration/index.html
+++ b/tests/integration/index.html
@@ -19,41 +19,7 @@
       var assert = chai.assert;
     </script>
     <script src='utils-bundle.js'></script>
-    <script src='test.setup_global_hooks.js'></script>
-    <script src='test.ajax.js'></script>
-    <script src='test.aa.setup.js'></script>
-    <script src='test.basics.js'></script>
-    <script src='test.constructor.js'></script>
-    <script src='test.changes.js'></script>
-    <script src='test.bulk_docs.js'></script>
-    <script src='test.bulk_get.js'></script>
-    <script src='test.all_docs.js'></script>
-    <script src='test.events.js'></script>
-    <script src='test.conflicts.js'></script>
-    <script src='test.revs_diff.js'></script>
-    <script src='test.replication.js'></script>
-    <script src='test.replication_events.js'></script>
-    <script src='test.sync.js'></script>
-    <script src='test.sync_events.js'></script>
-    <script src='test.retry.js'></script>
-    <script src='test.taskqueue.js'></script>
-    <script src='test.design_docs.js'></script>
-    <script src='browser.info.js'></script>
-    <script src='test.issue221.js'></script>
-    <script src='test.issue2674.js'></script>
-    <script src='test.issue3179.js'></script>
-    <script src='test.issue3646.js'></script>
-    <script src='test.http.js'></script>
-    <script src='test.compaction.js'></script>
-    <script src='test.get.js'></script>
-    <script src='test.local_docs.js'></script>
-    <script src='test.attachments.js'></script>
-    <script src='browser.migration.js'></script>
-    <script src='test.uuids.js'></script>
-    <script src='test.slash_id.js'></script>
-    <script src='test.reserved.js'></script>
-    <script src='test.prefix.js'></script>
-    <script src='browser.worker.js'></script>
+    <script src='tests-bundle.js'></script>
     <script type="text/javascript" src="./webrunner.js"></script>
   </body>
 </html>
diff --git a/tests/integration/test.defaults.js b/tests/integration/node.defaults.js
similarity index 99%
rename from tests/integration/test.defaults.js
rename to tests/integration/node.defaults.js
index ab94433..026d94a 100644
--- a/tests/integration/test.defaults.js
+++ b/tests/integration/node.defaults.js
@@ -8,7 +8,7 @@
   var mkdirp = require('mkdirp');
   var rimraf = require('rimraf');
 
-  describe('defaults', function () {
+  describe('node.defaults.js', function () {
 
     beforeEach(function () {
       return new PouchDB('mydb').destroy().then(function () {
diff --git a/tests/integration/test.failures.js b/tests/integration/node.failures.js
similarity index 96%
rename from tests/integration/test.failures.js
rename to tests/integration/node.failures.js
index 6960f7a..3a5f0d5 100644
--- a/tests/integration/test.failures.js
+++ b/tests/integration/node.failures.js
@@ -6,7 +6,7 @@
   !process.env.ADAPTER) {
   // these tests don't make sense for anything other than default leveldown
 
-  describe('test.failures.js', function () {
+  describe('node.failures.js', function () {
 
     var invalidPath = 'C:\\/path/to/thing/that/doesnt/exist\\with\\backslashes\\too';
 
diff --git a/tests/integration/test.issue1175.js b/tests/integration/node.issue1175.js
similarity index 98%
rename from tests/integration/test.issue1175.js
rename to tests/integration/node.issue1175.js
index 52f06a1..40a8e5d 100644
--- a/tests/integration/test.issue1175.js
+++ b/tests/integration/node.issue1175.js
@@ -52,7 +52,7 @@
   setTimeout(timeOutCallback, maximumTimeToWait);
   return callback;
 }
-describe('replication-http-errors:', function () {
+describe('node.issue1175.js', function () {
   it('Initial replication is ok if source returns HTTP 404', function (done) {
     var source = new MockDatabase(404, null);
     var target = new MockDatabase(200, {});
diff --git a/tests/integration/test.issue915.js b/tests/integration/node.issue915.js
similarity index 96%
rename from tests/integration/test.issue915.js
rename to tests/integration/node.issue915.js
index 29a864d..ff3ea79 100644
--- a/tests/integration/test.issue915.js
+++ b/tests/integration/node.issue915.js
@@ -5,7 +5,7 @@
     !process.env.ADAPTER) {
   // these tests don't make sense for anything other than default leveldown
   var fs = require('fs');
-  describe('test.issue915.js', function () {
+  describe('node.issue915.js', function () {
     afterEach(function (done) {
       fs.unlink('./tmp/_pouch_veryimportantfiles/something', function () {
         fs.rmdir('./tmp/_pouch_veryimportantfiles/', function () {
diff --git a/tests/integration/node.migration.js b/tests/integration/node.migration.js
new file mode 100644
index 0000000..4763e1b
--- /dev/null
+++ b/tests/integration/node.migration.js
@@ -0,0 +1,159 @@
+'use strict';
+if (!process.env.LEVEL_ADAPTER &&
+    !process.env.LEVEL_PREFIX &&
+    !process.env.AUTO_COMPACTION &&
+    !process.env.ADAPTER) {
+  // these tests don't make sense for anything other than default leveldown
+  var fs = require('fs');
+  var ncp = require('ncp').ncp;
+
+  ncp.limit = 16;
+
+  describe('node.migration.js', function () {
+
+    describe('migration one', function () {
+      beforeEach(function (done) {
+        var input =
+          fs.createReadStream('./tests/integration/leveldb/oldStyle.uuid');
+        input.on('end', function () {
+          ncp('./tests/integration/leveldb/oldStyle',
+            './tmp/_pouch_oldStyle', done);
+        });
+        input.pipe(fs.createWriteStream('./tmp/_pouch_oldStyle.uuid'));
+      });
+      it('should work', function () {
+        var db = new PouchDB('oldStyle');
+        return db.get('doc').then(function (doc) {
+          doc.something.should.equal('awesome');
+          return db.destroy();
+        });
+      });
+    });
+    describe('migration two', function () {
+      beforeEach(function (done) {
+        ncp('./tests/integration/leveldb/middleStyle',
+          './tmp/_pouch_middleStyle', done);
+      });
+      it('should work', function () {
+        var db = new PouchDB('middleStyle');
+        return db.id().then(function (id) {
+          id.should.equal('8E049E64-784A-3209-8DD6-97C29D7A5868');
+          return db.get('_local/foo');
+        }).then(function (resp) {
+          resp.something.should.equal('else');
+          return db.allDocs();
+        }).then(function (resp) {
+          resp.total_rows.should.equal(1);
+          resp.rows[0].id.should.equal('_design/foo');
+          return db.destroy();
+        });
+      });
+    });
+
+    // sanity check to ensure we don't actually need to migrate
+    // attachments for #2818
+    describe('#2818 no migration needed for attachments', function () {
+      beforeEach(function (done) {
+        ncp('./tests/integration/leveldb/lateStyle',
+          './tmp/_pouch_lateStyle', done);
+      });
+      it('should work', function () {
+        var db = new PouchDB('lateStyle', { auto_compaction: false });
+        return db.put({
+          _id: 'doc_b',
+          _attachments: {
+            'att.txt': {
+              data: 'Zm9v', // 'foo'
+              content_type: 'text/plain'
+            }
+          }
+        }).then(function () {
+          return db.get('doc_b');
+        }).then(function (doc) {
+          return db.remove(doc);
+        }).then(function () {
+          return db.compact();
+        }).then(function () {
+          return db.get('doc_a', { attachments: true });
+        }).then(function (doc) {
+          doc._attachments['att.txt'].data.should.equal('Zm9vYmFy');
+          doc._attachments['att2.txt'].data.should.equal('Zm9vYmFy');
+          doc._attachments['att3.txt'].data.should.equal('Zm9v');
+          return db.destroy();
+        });
+      });
+    });
+
+    // Sanity check for the fix in 3136 that guards against successive
+    // new_edits to the same rev, ensuring it ignores duplicates.
+    //
+    // In the olden days, <=3.2.0, if you bulk-pushed with new_edits=false
+    // the same rev multiple times, LevelDB would keep incrementing the seq
+    // and keep writing new revs. This was fixed, but when we do _changes,
+    // we have to guard against these duplicate seqs for backwards compat.
+    //
+    // This test is very similar to the test.bulk_docs.js test:
+    // 'Testing successive new_edits to the same doc, different content'
+
+    describe('#3136 no migration needed for overwritten revs', function () {
+      beforeEach(function (done) {
+        ncp('./tests/integration/leveldb/laterStyle',
+          './tmp/_pouch_laterStyle', done);
+      });
+      it('should work', function () {
+        var db = new PouchDB('laterStyle');
+
+        // basically this a db where I did a very pathological thing:
+        //var docsA = [{
+        //  '_id': 'foo',
+        //  '_rev': '1-x',
+        //  'bar' : 'baz',
+        //  '_revisions': {
+        //    'start': 1,
+        //    'ids': ['x']
+        //  }
+        //}, {
+        //  '_id' : 'fee',
+        //  '_rev': '1-x',
+        //  '_revisions': {
+        //    'start': 1,
+        //    'ids': ['x']
+        //  }
+        //}];
+        //var docsB = [{
+        //  '_id': 'foo',
+        //  '_rev': '1-x',
+        //  'bar' : 'zam', // this update should be rejected
+        //  '_revisions': {
+        //    'start': 1,
+        //    'ids': ['x']
+        //  }
+        //}];
+        //
+        //db.bulkDocs(docsA, {new_edits: false});
+        //db.bulkDocs(docsB, {new_edits: false});
+
+        return db.changes({ include_docs: true }).then(function (result) {
+          // the important thing is that 'zam' is ignored. see
+          // the other test in test.bulk_docs.js for details
+          var expected = {
+            "results": [{
+              "id": "fee",
+              "changes": [{ "rev": "1-x" }],
+              "doc": { "_id": "fee", "_rev": "1-x" },
+              "seq": 1
+            }, {
+              "id": "foo",
+              "changes": [{ "rev": "1-x" }],
+              "doc": { "bar": "baz", "_id": "foo", "_rev": "1-x" },
+              "seq": 2
+            }],
+            "last_seq": 2
+          };
+          result.should.deep.equal(expected);
+          return db.destroy();
+        });
+      });
+    });
+  });
+}
diff --git a/tests/integration/test.node-websql.js b/tests/integration/node.node-websql.js
similarity index 84%
rename from tests/integration/test.node-websql.js
rename to tests/integration/node.node-websql.js
index be22c28..17beeb7 100644
--- a/tests/integration/test.node-websql.js
+++ b/tests/integration/node.node-websql.js
@@ -1,7 +1,7 @@
 'use strict';
 
 if (process.env.ADAPTER === 'websql') {
-  describe('test.node-websql.js', function () {
+  describe('node.node-websql.js', function () {
     it('should run websql when we are actually testing websql', function () {
       var db = new PouchDB('testdb');
       db.adapter.should.equal('websql');
diff --git a/tests/integration/node.setup.js b/tests/integration/setup-node.js
similarity index 100%
rename from tests/integration/node.setup.js
rename to tests/integration/setup-node.js
diff --git a/tests/integration/test.migration.js b/tests/integration/test.migration.js
deleted file mode 100644
index 994728c..0000000
--- a/tests/integration/test.migration.js
+++ /dev/null
@@ -1,157 +0,0 @@
-'use strict';
-if (!process.env.LEVEL_ADAPTER &&
-    !process.env.LEVEL_PREFIX &&
-    !process.env.AUTO_COMPACTION &&
-    !process.env.ADAPTER) {
-  // these tests don't make sense for anything other than default leveldown
-  var fs = require('fs');
-  var ncp = require('ncp').ncp;
-
-  ncp.limit = 16;
-
-  describe('migration one', function () {
-    beforeEach(function (done) {
-      var input =
-        fs.createReadStream('./tests/integration/leveldb/oldStyle.uuid');
-      input.on('end', function () {
-        ncp('./tests/integration/leveldb/oldStyle',
-            './tmp/_pouch_oldStyle', done);
-      });
-      input.pipe(fs.createWriteStream('./tmp/_pouch_oldStyle.uuid'));
-    });
-    it('should work', function () {
-      var db = new PouchDB('oldStyle');
-      return db.get('doc').then(function (doc) {
-        doc.something.should.equal('awesome');
-        return db.destroy();
-      });
-    });
-  });
-  describe('migration two', function () {
-    beforeEach(function (done) {
-      ncp('./tests/integration/leveldb/middleStyle',
-          './tmp/_pouch_middleStyle', done);
-    });
-    it('should work', function () {
-      var db = new PouchDB('middleStyle');
-      return db.id().then(function (id) {
-        id.should.equal('8E049E64-784A-3209-8DD6-97C29D7A5868');
-        return db.get('_local/foo');
-      }).then(function (resp) {
-        resp.something.should.equal('else');
-        return db.allDocs();
-      }).then(function (resp) {
-        resp.total_rows.should.equal(1);
-        resp.rows[0].id.should.equal('_design/foo');
-        return db.destroy();
-      });
-    });
-  });
-
-  // sanity check to ensure we don't actually need to migrate
-  // attachments for #2818
-  describe('#2818 no migration needed for attachments', function () {
-    beforeEach(function (done) {
-      ncp('./tests/integration/leveldb/lateStyle',
-          './tmp/_pouch_lateStyle', done);
-    });
-    it('should work', function () {
-      var db = new PouchDB('lateStyle', { auto_compaction: false });
-      return db.put({
-        _id: 'doc_b',
-        _attachments: {
-          'att.txt': {
-            data: 'Zm9v', // 'foo'
-            content_type: 'text/plain'
-            }
-        }
-      }).then(function () {
-        return db.get('doc_b');
-      }).then(function (doc) {
-        return db.remove(doc);
-      }).then(function () {
-        return db.compact();
-      }).then(function () {
-        return db.get('doc_a', {attachments: true});
-      }).then(function (doc) {
-        doc._attachments['att.txt'].data.should.equal('Zm9vYmFy');
-        doc._attachments['att2.txt'].data.should.equal('Zm9vYmFy');
-        doc._attachments['att3.txt'].data.should.equal('Zm9v');
-        return db.destroy();
-      });
-    });
-  });
-
-  // Sanity check for the fix in 3136 that guards against successive
-  // new_edits to the same rev, ensuring it ignores duplicates.
-  //
-  // In the olden days, <=3.2.0, if you bulk-pushed with new_edits=false
-  // the same rev multiple times, LevelDB would keep incrementing the seq
-  // and keep writing new revs. This was fixed, but when we do _changes,
-  // we have to guard against these duplicate seqs for backwards compat.
-  //
-  // This test is very similar to the test.bulk_docs.js test:
-  // 'Testing successive new_edits to the same doc, different content'
-
-  describe('#3136 no migration needed for overwritten revs', function () {
-    beforeEach(function (done) {
-      ncp('./tests/integration/leveldb/laterStyle',
-        './tmp/_pouch_laterStyle', done);
-    });
-    it('should work', function () {
-      var db = new PouchDB('laterStyle');
-
-      // basically this a db where I did a very pathological thing:
-      //var docsA = [{
-      //  '_id': 'foo',
-      //  '_rev': '1-x',
-      //  'bar' : 'baz',
-      //  '_revisions': {
-      //    'start': 1,
-      //    'ids': ['x']
-      //  }
-      //}, {
-      //  '_id' : 'fee',
-      //  '_rev': '1-x',
-      //  '_revisions': {
-      //    'start': 1,
-      //    'ids': ['x']
-      //  }
-      //}];
-      //var docsB = [{
-      //  '_id': 'foo',
-      //  '_rev': '1-x',
-      //  'bar' : 'zam', // this update should be rejected
-      //  '_revisions': {
-      //    'start': 1,
-      //    'ids': ['x']
-      //  }
-      //}];
-      //
-      //db.bulkDocs(docsA, {new_edits: false});
-      //db.bulkDocs(docsB, {new_edits: false});
-
-
-      return db.changes({include_docs: true}).then(function (result) {
-        // the important thing is that 'zam' is ignored. see
-        // the other test in test.bulk_docs.js for details
-        var expected = {
-          "results": [{
-            "id": "fee",
-            "changes": [{"rev": "1-x"}],
-            "doc": {"_id": "fee", "_rev": "1-x"},
-            "seq": 1
-          }, {
-            "id": "foo",
-            "changes": [{"rev": "1-x"}],
-            "doc": {"bar": "baz", "_id": "foo", "_rev": "1-x"},
-            "seq": 2
-          }],
-          "last_seq": 2
-        };
-        result.should.deep.equal(expected);
-        return db.destroy();
-      });
-    });
-  });
-}