| |
| 'use strict'; |
| |
| var adapters = ['http', 'local']; |
| |
| adapters.forEach(function (adapter) { |
| |
| describe('test.changes.js-' + adapter, function () { |
| |
| var dbs = {}; |
| |
| // if it exists, return the single element |
| // which has the specific id. Else retun null. |
| // useful for finding elements within a _changes feed |
| function findById(array, id) { |
| var result = array.filter(function (i) { |
| return i.id === id; |
| }); |
| |
| // |
| if (result.length === 1) { |
| return result[0]; |
| } |
| } |
| |
| beforeEach(function () { |
| dbs.name = testUtils.adapterUrl(adapter, 'testdb'); |
| dbs.remote = testUtils.adapterUrl(adapter, 'test_repl_remote'); |
| }); |
| |
| afterEach(function (done) { |
| testUtils.cleanup([dbs.name, dbs.remote], done); |
| }); |
| |
| it('All changes', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.post({ test: 'somestuff' }, function () { |
| var promise = db.changes({ |
| }).on('change', function (change) { |
| change.should.not.have.property('doc'); |
| change.should.have.property('seq'); |
| done(); |
| }); |
| should.exist(promise); |
| promise.cancel.should.be.a('function'); |
| }); |
| }); |
| |
| it('Changes with complete callback as param', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.post({ test: 'somestuff' }).then(function () { |
| db.changes(function (err, complete) { |
| complete.should.have.property('last_seq'); |
| complete.should.have.property('results'); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('Promise resolved when changes cancelled', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3}, |
| {_id: '4', integer: 4}, |
| {_id: '5', integer: 5}, |
| {_id: '6', integer: 6}, |
| {_id: '7', integer: 7}, |
| {_id: '8', integer: 9}, |
| {_id: '9', integer: 9}, |
| {_id: '10', integer: 10} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs }, function () { |
| var changeCount = 0; |
| var promise = db.changes().on('change', function handler() { |
| changeCount++; |
| if (changeCount === 5) { |
| promise.cancel(); |
| promise.removeListener('change', handler); |
| } |
| }); |
| should.exist(promise); |
| should.exist(promise.then); |
| promise.then.should.be.a('function'); |
| promise.then( |
| function (result) { |
| changeCount.should.equal(5, 'changeCount'); |
| should.exist(result); |
| result.should.deep.equal({status: 'cancelled'}); |
| done(); |
| }, function (err) { |
| changeCount.should.equal(5, 'changeCount'); |
| should.exist(err); |
| done(); |
| }); |
| }); |
| }); |
| |
| // TODO: https://github.com/pouchdb/pouchdb/issues/6618 |
| it.skip('Live changes should clean listener when cancelled', function () { |
| var db = new PouchDB(dbs.name); |
| |
| // TODO: The bug was fixed for the 'idb' adapter in |
| // https://github.com/pouchdb/pouchdb/pull/6504, but still happens with |
| // the 'websql' adapter. It needs to be fixed! |
| if (db.adapter === 'websql') { |
| return; |
| } |
| |
| return new testUtils.Promise(function (resolve, reject) { |
| // Capture logs |
| var logs = []; |
| var oldLog = console.error; |
| console.error = function () { |
| var args = Array.prototype.slice.call(arguments); |
| logs.push(args); |
| oldLog.apply(console, arguments); |
| }; |
| |
| // Try to trigger the problem |
| var changes; |
| var i = 0; |
| function renewChangeListener() { |
| changes = db.changes({live: true}); |
| if (i++ < 20) { |
| setTimeout(function () { |
| changes.cancel(); |
| changes.on('complete', renewChangeListener); |
| }, 0); |
| } else { |
| changes.cancel(); |
| |
| // Check whether error logs have been output or not |
| changes.on('complete', function () { |
| console.error = oldLog; |
| |
| var badLogs = logs.filter(function (args) { |
| return args[0].indexOf( |
| 'possible EventEmitter memory leak detected') !== -1; |
| }); |
| |
| if (badLogs.length > 0) { |
| reject(new Error(badLogs)); |
| } else { |
| resolve(); |
| } |
| }); |
| } |
| } |
| renewChangeListener(); |
| }); |
| }); |
| |
| it('Changes Since', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3}, |
| {_id: '4', integer: 4}, |
| {_id: '5', integer: 5}, |
| {_id: '6', integer: 6}, |
| {_id: '7', integer: 7}, |
| {_id: '8', integer: 9}, |
| {_id: '9', integer: 9}, |
| {_id: '10', integer: 10}, |
| {_id: '11', integer: 11} |
| ]; |
| var db = new PouchDB(dbs.name); |
| |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.info(function (err, info) { |
| var update_seq = info.update_seq; |
| |
| var docs2 = [ |
| {_id: '12', integer: 12}, |
| {_id: '13', integer: 13} |
| ]; |
| |
| db.bulkDocs({ docs: docs2 }, function () { |
| var promise = db.changes({ |
| return_docs: true, |
| since: update_seq |
| }).on('complete', function (results) { |
| results.results.length.should.be.at.least(2); |
| done(); |
| }); |
| should.exist(promise); |
| promise.cancel.should.be.a('function'); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes Since and limit limit 1', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.info(function (err, info) { |
| var update_seq = info.update_seq; |
| |
| var docs2 = [ |
| {_id: '3', integer: 3}, |
| {_id: '4', integer: 4} |
| ]; |
| |
| db.bulkDocs({ docs: docs2 }, function () { |
| db.changes({ |
| return_docs: true, |
| since: update_seq, |
| limit: 1 |
| }).on('complete', function (results) { |
| results.results.length.should.equal(1); |
| done(); |
| }); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes Since and limit limit 0', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.info(function (err, info) { |
| var update_seq = info.update_seq; |
| |
| var docs2 = [ |
| {_id: '3', integer: 3}, |
| {_id: '4', integer: 4} |
| ]; |
| |
| db.bulkDocs({ docs: docs2 }, function () { |
| db.changes({ |
| return_docs: true, |
| since: update_seq, |
| limit: 0 |
| }).on('complete', function (results) { |
| results.results.length.should.equal(1); |
| done(); |
| }); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes limit', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var docs2 = [ |
| {_id: '2', integer: 11}, |
| {_id: '3', integer: 12} |
| ]; |
| var db = new PouchDB(dbs.name); |
| // we use writeDocs since bulkDocs looks to have undefined |
| // order of doing insertions |
| testUtils.writeDocs(db, docs1, function (err, info) { |
| docs2[0]._rev = info[2].rev; |
| docs2[1]._rev = info[3].rev; |
| |
| db.info(function (err, info) { |
| var update_seq = info.update_seq; |
| |
| db.put(docs2[0], function (err, info) { |
| docs2[0]._rev = info.rev; |
| db.put(docs2[1], function (err, info) { |
| docs2[1]._rev = info.rev; |
| db.changes({ |
| return_docs: true, |
| limit: 2, |
| since: update_seq, |
| include_docs: true |
| }).on('complete', function (results) { |
| results = results.results; |
| results.length.should.equal(2); |
| |
| // order is not guaranteed |
| var first = results[0]; |
| var second = results[1]; |
| if (first.id === '3') { |
| second = first; |
| first = results[1]; |
| } |
| first.id.should.equal('2'); |
| first.doc.integer.should.equal(docs2[0].integer); |
| first.doc._rev.should.equal(docs2[0]._rev); |
| second.id.should.equal('3'); |
| second.doc.integer.should.equal(docs2[1].integer); |
| second.doc._rev.should.equal(docs2[1]._rev); |
| done(); |
| }); |
| }); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes with filter not present in ddoc', function (done) { |
| this.timeout(15000); |
| var docs = [ |
| {_id: '1', integer: 1}, |
| { _id: '_design/foo', |
| integer: 4, |
| filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.writeDocs(db, docs, function () { |
| db.changes({ |
| filter: 'foo/odd', |
| limit: 2, |
| include_docs: true |
| }).on('error', function (err) { |
| err.name.should.equal('not_found'); |
| err.status.should.equal(testUtils.errors.MISSING_DOC.status, |
| 'correct error status returned'); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('Changes with `filters` key not present in ddoc', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| { |
| _id: '_design/foo', |
| integer: 4, |
| views: { |
| even: { |
| map: 'function (doc) { if (doc.integer % 2 === 1)' + |
| ' { emit(doc._id, null) }; }' |
| } |
| } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.writeDocs(db, docs, function () { |
| db.changes({ |
| filter: 'foo/even', |
| limit: 2, |
| include_docs: true |
| }).on('error', function (err) { |
| err.status.should.equal(testUtils.errors.MISSING_DOC.status, |
| 'correct error status returned'); |
| err.name.should.equal('not_found'); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('Changes limit and filter', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2} |
| ]; |
| var db = new PouchDB(dbs.name); |
| |
| var docs2 = [ |
| {_id: '3', integer: 3}, |
| {_id: '4', integer: 4}, |
| {_id: '5', integer: 5}, |
| { |
| _id: '_design/foo', |
| integer: 4, |
| filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' } |
| } |
| ]; |
| |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.info(function (err, info) { |
| var update_seq = info.update_seq; |
| |
| testUtils.writeDocs(db, docs2, function () { |
| var promise = db.changes({ |
| return_docs: true, |
| filter: 'foo/even', |
| limit: 2, |
| since: update_seq, |
| include_docs: true |
| }).on('complete', function (results) { |
| results.results.length.should.equal(2); |
| var three = findById(results.results, '3'); |
| three.doc.integer.should.equal(3); |
| var five = findById(results.results, '5'); |
| five.doc.integer.should.equal(5); |
| done(); |
| }).on('error', done); |
| should.exist(promise); |
| promise.cancel.should.be.a('function'); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes with shorthand function name', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| { |
| _id: '_design/even', |
| integer: 3, |
| filters: { even: 'function (doc) { return doc.integer % 2 === 0; }' } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| |
| db.bulkDocs({ docs: docs }, function () { |
| var promise = db.changes({ |
| return_docs: true, |
| filter: 'even', |
| include_docs: true |
| }).on('complete', function (results) { |
| results.results.length.should.equal(2); |
| var zero = findById(results.results, '0'); |
| zero.doc.integer.should.equal(0); |
| var two = findById(results.results, '2'); |
| two.doc.integer.should.equal(2); |
| done(); |
| }).on('error', done); |
| should.exist(promise); |
| promise.cancel.should.be.a('function'); |
| }); |
| }); |
| |
| it('Changes with filter from nonexistent ddoc', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1} |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.writeDocs(db, docs, function () { |
| db.changes({ |
| filter: 'foobar/odd' |
| }).on('error', function (err) { |
| should.exist(err); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('Changes with view not present in ddoc', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| { |
| _id: '_design/foo', |
| integer: 4, |
| views: |
| { even: |
| { map: 'function (doc) { if (doc.integer % 2 === 1) { ' + |
| 'emit(doc._id, null) }; }' } } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.writeDocs(db, docs, function () { |
| db.changes({ |
| filter: '_view', |
| view: 'foo/odd' |
| }).on('error', function (err) { |
| err.status.should.equal(testUtils.errors.MISSING_DOC.status, |
| 'correct error status returned'); |
| err.name.should.equal('not_found'); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('Changes with `views` key not present in ddoc', function (done) { |
| var docs = [ |
| {_id: '1', integer: 1}, |
| { |
| _id: '_design/foo', |
| integer: 4, |
| filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.writeDocs(db, docs, function () { |
| db.changes({ |
| filter: '_view', |
| view: 'foo/even' |
| }).on('error', function (err) { |
| err.status.should.equal(testUtils.errors.MISSING_DOC.status, |
| 'correct error status returned'); |
| err.name.should.equal('not_found'); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('#4451 Changes with invalid view filter', function (done) { |
| var docs = [ |
| {_id: '1', integer: 1}, |
| { |
| _id: '_design/foo', |
| filters: { even: 'function (doc) { return doc.integer % 2 === 1; }' } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs(docs).then(function () { |
| db.changes({filter: 'a/b/c'}).on('error', function () { |
| done('should not be called'); |
| }).on('complete', function () { |
| done(); |
| }); |
| }); |
| }); |
| |
| it('3356 throw inside a filter', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.put({ |
| _id: "_design/test", |
| filters: { |
| test: function () { |
| throw new Error(); // syntaxerrors can't be caught either. |
| }.toString() |
| } |
| }).should.eventually.be.fulfilled.then(function () { |
| return db.changes({filter: 'test/test'}).should.eventually.be.rejected; |
| }).then(function () { |
| done(); |
| }).catch(function (err) { |
| done('We had an error - ' + err); |
| }); |
| }); |
| |
| it('Changes with missing param `view` in request', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| { |
| _id: '_design/foo', |
| integer: 4, |
| views: { even: { map: 'function (doc) { if (doc.integer % 2 === 1) ' + |
| '{ emit(doc._id, null) }; }' } } |
| } |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.writeDocs(db, docs, function () { |
| db.changes({ |
| filter: '_view' |
| }).on('error', function (err) { |
| err.status.should.equal(testUtils.errors.BAD_REQUEST.status, |
| 'correct error status returned'); |
| err.name.should.equal('bad_request'); |
| done(); |
| }); |
| }); |
| }); |
| |
| it('Changes limit and view instead of filter', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.info(function (err, info) { |
| var update_seq = info.update_seq; |
| |
| var docs2 = [ |
| {_id: '3', integer: 3}, |
| {_id: '4', integer: 4}, |
| {_id: '5', integer: 5}, |
| { |
| _id: '_design/foo', |
| integer: 4, |
| views: { even: { map: 'function (doc) ' + |
| '{ if (doc.integer % 2 === 1) ' + |
| '{ emit(doc._id, null) }; }' |
| } |
| } |
| } |
| ]; |
| |
| db.bulkDocs({ docs: docs2 }, function () { |
| |
| db.changes({ |
| return_docs: true, |
| filter: '_view', |
| view: 'foo/even', |
| limit: 2, |
| since: update_seq, |
| include_docs: true |
| }).on('complete', function (results) { |
| var changes = results.results; |
| changes.length.should.equal(2); |
| |
| findById(changes, '3') |
| .doc.integer.should.equal(3); |
| |
| findById(changes, '5') |
| .doc.integer.should.equal(5); |
| |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| }); |
| }); |
| |
| it('#3609 view option implies filter: _view', function () { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '_design/foo', integer: 3, |
| views: { |
| even: { |
| map: 'function (doc) { if (doc.integer % 2 === 1) ' + |
| '{ emit(doc._id, null) }; }' |
| } |
| } |
| } |
| ]; |
| |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return db.changes({return_docs: true, view: 'foo/even'}); |
| }).then(function (changes) { |
| changes.results.length.should.equal(2); |
| }); |
| }); |
| |
| it('Immediately cancel changes', function () { |
| // fixes code coverage by ensuring the changes() listener |
| // emits 'complete' even if the db's task queue isn't |
| // ready yet |
| return new testUtils.Promise(function (resolve, reject) { |
| var db = new PouchDB(dbs.name); |
| var changes = db.changes({live: true}); |
| changes.on('error', reject); |
| changes.on('complete', resolve); |
| changes.cancel(); |
| }); |
| }); |
| |
| it('Changes with invalid ddoc view name', function () { |
| return new testUtils.Promise(function (resolve, reject) { |
| var db = new PouchDB(dbs.name); |
| db.post({}); |
| var changes = db.changes({live: true, filter: '_view', view: ''}); |
| changes.on('error', resolve); |
| changes.on('change', reject); |
| }); |
| }); |
| |
| it('Changes with invalid ddoc view name 2', function () { |
| return new testUtils.Promise(function (resolve, reject) { |
| var db = new PouchDB(dbs.name); |
| db.post({}); |
| var changes = db.changes({live: true, filter: '_view', view: 'a/b/c'}); |
| changes.on('error', resolve); |
| changes.on('change', reject); |
| }); |
| }); |
| |
| it('Changes with style = all_docs', function (done) { |
| var simpleTree = [ |
| [{_id: 'foo', _rev: '1-a', value: 'foo a'}, |
| {_id: 'foo', _rev: '2-b', value: 'foo b'}, |
| {_id: 'foo', _rev: '3-c', value: 'foo c'}], |
| [{_id: 'foo', _rev: '1-a', value: 'foo a'}, |
| {_id: 'foo', _rev: '2-d', value: 'foo d'}, |
| {_id: 'foo', _rev: '3-e', value: 'foo e'}, |
| {_id: 'foo', _rev: '4-f', value: 'foo f'}], |
| [{_id: 'foo', _rev: '1-a', value: 'foo a'}, |
| {_id: 'foo', _rev: '2-g', value: 'foo g', _deleted: true}] |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.putTree(db, simpleTree, function () { |
| db.changes({return_docs: true}).on('complete', function (res) { |
| res.results[0].changes.length.should.equal(1); |
| res.results[0].changes[0].rev.should.equal('4-f'); |
| db.changes({ |
| return_docs: true, |
| style: 'all_docs' |
| }).on('complete', function (res) { |
| res.results[0].changes.length.should.equal(3); |
| var changes = res.results[0].changes; |
| changes.sort(function (a, b) { |
| return a.rev < b.rev; |
| }); |
| changes[0].rev.should.equal('4-f'); |
| changes[1].rev.should.equal('3-c'); |
| changes[2].rev.should.equal('2-g'); |
| done(); |
| }).on('error', done); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('Changes with style = all_docs and a callback for complete', |
| function (done) { |
| var simpleTree = [ |
| [{_id: 'foo', _rev: '1-a', value: 'foo a'}, |
| {_id: 'foo', _rev: '2-b', value: 'foo b'}, |
| {_id: 'foo', _rev: '3-c', value: 'foo c'}], |
| [{_id: 'foo', _rev: '1-a', value: 'foo a'}, |
| {_id: 'foo', _rev: '2-d', value: 'foo d'}, |
| {_id: 'foo', _rev: '3-e', value: 'foo e'}, |
| {_id: 'foo', _rev: '4-f', value: 'foo f'}], |
| [{_id: 'foo', _rev: '1-a', value: 'foo a'}, |
| {_id: 'foo', _rev: '2-g', value: 'foo g', _deleted: true}] |
| ]; |
| var db = new PouchDB(dbs.name); |
| testUtils.putTree(db, simpleTree, function () { |
| db.changes({return_docs: true}, function (err, res) { |
| res.results[0].changes.length.should.equal(1); |
| res.results[0].changes[0].rev.should.equal('4-f'); |
| db.changes({ |
| return_docs: true, |
| style: 'all_docs' |
| }, function (err, res) { |
| should.not.exist(err); |
| res.results[0].changes.length.should.equal(3); |
| var changes = res.results[0].changes; |
| changes.sort(function (a, b) { |
| return a.rev < b.rev; |
| }); |
| changes[0].rev.should.equal('4-f'); |
| changes[1].rev.should.equal('3-c'); |
| changes[2].rev.should.equal('2-g'); |
| done(); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes limit = 0', function (done) { |
| var docs = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs }, function () { |
| db.changes({ |
| return_docs: true, |
| limit: 0 |
| }).on('complete', function (results) { |
| results.results.length.should.equal(1); |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('Changes doc', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.post({ test: 'somestuff' }, function () { |
| db.changes({ |
| include_docs: true |
| }).on('change', function (change) { |
| change.doc._id.should.equal(change.id); |
| change.doc._rev.should |
| .equal(change.changes[change.changes.length - 1].rev); |
| done(); |
| }); |
| }); |
| }); |
| |
| // Note for the following test that CouchDB's implementation of /_changes |
| // with `descending=true` ignores any `since` parameter. |
| it.skip('Descending many changes', function (done) { |
| if (testUtils.isIE()) { |
| return done(); |
| } |
| var db = new PouchDB(dbs.name); |
| var docs = []; |
| var num = 100; |
| for (var i = 0; i < num; i++) { |
| docs.push({ |
| _id: 'doc_' + i, |
| foo: 'bar_' + i |
| }); |
| } |
| var changes = 0; |
| db.bulkDocs({ docs: docs }, function (err) { |
| if (err) { |
| return done(err); |
| } |
| db.changes({ |
| descending: true |
| }).on('change', function () { |
| changes++; |
| }).on('complete', function () { |
| changes.should.equal(num, 'correct number of changes'); |
| done(); |
| }).on('error', function (err) { |
| done(err); |
| }); |
| }); |
| }); |
| |
| it('live-changes', function (done) { |
| var db = new PouchDB(dbs.name); |
| var count = 0; |
| var changes = db.changes({ |
| live: true |
| }).on('complete', function () { |
| count.should.equal(1); |
| done(); |
| }).on('change', function (change) { |
| count += 1; |
| change.should.not.have.property('doc'); |
| count.should.equal(1); |
| changes.cancel(); |
| }); |
| db.post({ test: 'adoc' }); |
| }); |
| |
| it('Multiple watchers', function (done) { |
| var db = new PouchDB(dbs.name); |
| var count = 0; |
| var changes1Complete = false; |
| var changes2Complete = false; |
| function checkCount() { |
| if (changes1Complete && changes2Complete) { |
| count.should.equal(2); |
| done(); |
| } |
| } |
| var changes1 = db.changes({ |
| live: true |
| }).on('complete', function () { |
| changes1Complete = true; |
| checkCount(); |
| }).on('change', function () { |
| count += 1; |
| changes1.cancel(); |
| changes1 = null; |
| }).on('error', done); |
| var changes2 = db.changes({ |
| live: true |
| }).on('complete', function () { |
| changes2Complete = true; |
| checkCount(); |
| }).on('change', function () { |
| count += 1; |
| changes2.cancel(); |
| changes2 = null; |
| }).on('error', done); |
| db.post({test: 'adoc'}); |
| }); |
| |
| it('Continuous changes doc', function (done) { |
| var db = new PouchDB(dbs.name); |
| var changes = db.changes({ |
| live: true, |
| include_docs: true |
| }).on('complete', function (result) { |
| result.status.should.equal('cancelled'); |
| done(); |
| }).on('change', function (change) { |
| change.should.have.property('doc'); |
| change.doc.should.have.property('_rev'); |
| changes.cancel(); |
| }).on('error', done); |
| db.post({ test: 'adoc' }); |
| }); |
| |
| it('Cancel changes', function (done) { |
| var db = new PouchDB(dbs.name); |
| var count = 0; |
| var interval; |
| var docPosted = false; |
| |
| // We want to wait for a period of time after the final |
| // document was posted to ensure we didnt see another |
| // change |
| function waitForDocPosted() { |
| if (!docPosted) { |
| return; |
| } |
| clearInterval(interval); |
| setTimeout(function () { |
| count.should.equal(1); |
| done(); |
| }, 200); |
| } |
| |
| var changes = db.changes({ |
| live: true |
| }).on('complete', function (result) { |
| result.status.should.equal('cancelled'); |
| // This setTimeout ensures that once we cancel a change we dont recieve |
| // subsequent callbacks, so it is needed |
| interval = setInterval(waitForDocPosted, 100); |
| }).on('change', function () { |
| count += 1; |
| if (count === 1) { |
| changes.cancel(); |
| db.post({ test: 'another doc' }, function (err) { |
| if (err) { |
| return done(err); |
| } |
| docPosted = true; |
| }); |
| } |
| }); |
| db.post({ test: 'adoc' }); |
| }); |
| |
| |
| it("#3579 changes firing 1 too many times", function () { |
| var db = new PouchDB(dbs.name); |
| var Promise = testUtils.Promise; |
| return db.bulkDocs([{}, {}, {}]).then(function () { |
| var changes = db.changes({ |
| since: 'now', |
| live: true, |
| include_docs: true |
| }); |
| return Promise.all([ |
| new Promise(function (resolve, reject) { |
| changes.on('error', reject); |
| changes.on('change', function (change) { |
| changes.cancel(); |
| resolve(change); |
| }); |
| }), |
| new Promise(function (resolve) { |
| setTimeout(resolve, 50); |
| }).then(function () { |
| return db.put({_id: 'foobar'}); |
| }) |
| ]); |
| }).then(function (result) { |
| var change = result[0]; |
| change.id.should.equal('foobar'); |
| change.doc._id.should.equal('foobar'); |
| }); |
| }); |
| |
| it('Kill database while listening to live changes', function (done) { |
| var db = new PouchDB(dbs.name); |
| |
| db.changes({live: true}) |
| .on('error', function () { done(); }) |
| .on('complete', function () { done(); }) |
| .on('change', function () { db.destroy().catch(done); }); |
| |
| db.post({ test: 'adoc' }); |
| }); |
| |
| it('#3136 style=all_docs', function () { |
| |
| var db = new PouchDB(dbs.name); |
| |
| var chain = testUtils.Promise.resolve(); |
| |
| var docIds = ['b', 'c', 'a', 'z', 'd', 'e']; |
| |
| docIds.forEach(function (docId) { |
| chain = chain.then(function () { |
| return db.put({_id: docId}); |
| }); |
| }); |
| |
| return chain.then(function () { |
| return db.changes({return_docs: true, style: 'all_docs'}); |
| }).then(function (res) { |
| var ids = res.results.map(function (x) { |
| return x.id; |
| }); |
| ids.should.include.members(docIds); |
| }); |
| }); |
| |
| it('#4191 revs_diff causes endless loop', function () { |
| var db = new PouchDB(dbs.name, {auto_compaction: false}); |
| return db.bulkDocs({ |
| "new_edits": false, |
| "docs": [{"_id": "799","_rev":"1-d22"} |
| ]}).then(function () { |
| return db.bulkDocs({ |
| "new_edits": false, |
| "docs": [{"_id": "3E1", "_rev": "1-ab5"}] |
| }); |
| }).then(function () { |
| return db.bulkDocs( |
| { new_edits: false, |
| docs: |
| [ { _id: 'FB3', _rev: '1-363' }, |
| { _id: '27C', _rev: '1-4c3' }, |
| { _id: 'BD6', _rev: '1-de0' }, |
| { _id: '1E9', _rev: '1-451' } ] } |
| ); |
| }).then(function () { |
| return db.changes({style: 'all_docs', limit: 100}); |
| }).then(function (res) { |
| var lastSeq = res.last_seq; |
| return db.changes({ |
| return_docs: true, |
| since: lastSeq, |
| style: 'all_docs', |
| limit: 100 |
| }); |
| }).then(function (res) { |
| res.results.should.have.length(0); |
| }); |
| }); |
| |
| it('#3136 style=all_docs & include_docs', function () { |
| |
| var db = new PouchDB(dbs.name); |
| |
| var chain = testUtils.Promise.resolve(); |
| |
| var docIds = ['b', 'c', 'a', 'z', 'd', 'e']; |
| |
| docIds.forEach(function (docId) { |
| chain = chain.then(function () { |
| return db.put({_id: docId}); |
| }); |
| }); |
| |
| return chain.then(function () { |
| return db.changes({ |
| return_docs: true, |
| style: 'all_docs', |
| include_docs: true |
| }); |
| }).then(function (res) { |
| var ids = res.results.map(function (x) { |
| return x.id; |
| }); |
| ids.should.include.members(docIds); |
| }); |
| }); |
| |
| it('changes-filter with query params', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var docs2 = [ |
| {_id: '4', integer: 4}, |
| {_id: '5', integer: 5}, |
| {_id: '6', integer: 6}, |
| {_id: '7', integer: 7} |
| ]; |
| var params = { 'abc': true }; |
| var db = new PouchDB(dbs.name); |
| var count = 0; |
| db.bulkDocs({ docs: docs1 }, function () { |
| var changes = db.changes({ |
| filter: function (doc, req) { |
| if (req.query.abc) { |
| return doc.integer % 2 === 0; |
| } |
| }, |
| query_params: params, |
| live: true |
| }).on('complete', function (result) { |
| result.status.should.equal('cancelled'); |
| done(); |
| }).on('change', function () { |
| count += 1; |
| if (count === 4) { |
| changes.cancel(); |
| } |
| }).on('error', done); |
| db.bulkDocs({ docs: docs2 }); |
| }); |
| }); |
| |
| it('Non-live changes filter', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.changes().on('complete', function (allChanges) { |
| db.changes({ |
| return_docs: true, |
| filter: function (doc) { |
| return doc.integer % 2 === 0; |
| } |
| }).on('complete', function (filteredChanges) { |
| // Should get docs 0 and 2 if the filter |
| // has been applied correctly. |
| filteredChanges.results.length.should.equal(2); |
| filteredChanges.last_seq.should.deep.equal(allChanges.last_seq); |
| done(); |
| }).on('error', done); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('Non-live changes filter, descending', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.changes({ |
| descending: true |
| }).on('complete', function (allChanges) { |
| db.changes({ |
| return_docs: true, |
| descending: true, |
| filter: function (doc) { |
| return doc.integer > 2; |
| } |
| }).on('complete', function (filteredChanges) { |
| // Should get docs 2 and 3 if the filter |
| // has been applied correctly. |
| filteredChanges.results.length.should.equal(1); |
| filteredChanges.last_seq.should.deep.equal(allChanges.last_seq); |
| done(); |
| }).on('error', done); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('#2569 Non-live doc_ids filter', function () { |
| var docs = [ |
| {_id: '0'}, |
| {_id: '1'}, |
| {_id: '2'}, |
| {_id: '3'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return db.changes({ |
| return_docs: true, |
| doc_ids: ['1', '3'] |
| }); |
| }).then(function (changes) { |
| var ids = changes.results.map(function (x) { |
| return x.id; |
| }); |
| ids.sort().should.deep.equal(['1', '3']); |
| }); |
| }); |
| |
| it('#2569 Big non-live doc_ids filter', function () { |
| var docs = []; |
| for (var i = 0; i < 5; i++) { |
| var id = ''; |
| for (var j = 0; j < 50; j++) { |
| // make a huge id |
| id += testUtils.btoa(Math.random().toString()); |
| } |
| docs.push({_id: id}); |
| } |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return db.changes({ |
| return_docs: true, |
| doc_ids: [docs[1]._id, docs[3]._id] |
| }); |
| }).then(function (changes) { |
| var ids = changes.results.map(function (x) { |
| return x.id; |
| }); |
| var expectedIds = [docs[1]._id, docs[3]._id]; |
| ids.sort().should.deep.equal(expectedIds.sort()); |
| }); |
| }); |
| |
| it('#2569 Live doc_ids filter', function () { |
| var docs = [ |
| {_id: '0'}, |
| {_id: '1'}, |
| {_id: '2'}, |
| {_id: '3'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return new testUtils.Promise(function (resolve, reject) { |
| var retChanges = []; |
| var changes = db.changes({ |
| doc_ids: ['1', '3'], |
| live: true |
| }).on('change', function (change) { |
| retChanges.push(change); |
| if (retChanges.length === 2) { |
| changes.cancel(); |
| resolve(retChanges); |
| } |
| }).on('error', reject); |
| }); |
| }).then(function (changes) { |
| var ids = changes.map(function (x) { |
| return x.id; |
| }); |
| var expectedIds = ['1', '3']; |
| ids.sort().should.deep.equal(expectedIds); |
| }); |
| }); |
| |
| it('#2569 Big live doc_ids filter', function () { |
| var docs = []; |
| for (var i = 0; i < 5; i++) { |
| var id = ''; |
| for (var j = 0; j < 50; j++) { |
| // make a huge id |
| id += testUtils.btoa(Math.random().toString()); |
| } |
| docs.push({_id: id}); |
| } |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return new testUtils.Promise(function (resolve, reject) { |
| var retChanges = []; |
| var changes = db.changes({ |
| doc_ids: [docs[1]._id, docs[3]._id], |
| live: true |
| }).on('change', function (change) { |
| retChanges.push(change); |
| if (retChanges.length === 2) { |
| changes.cancel(); |
| resolve(retChanges); |
| } |
| }).on('error', reject); |
| }); |
| }).then(function (changes) { |
| var ids = changes.map(function (x) { |
| return x.id; |
| }); |
| var expectedIds = [docs[1]._id, docs[3]._id]; |
| ids.sort().should.deep.equal(expectedIds.sort()); |
| }); |
| }); |
| |
| it('#2569 Non-live doc_ids filter with filter=_doc_ids', function () { |
| var docs = [ |
| {_id: '0'}, |
| {_id: '1'}, |
| {_id: '2'}, |
| {_id: '3'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return db.changes({ |
| return_docs: true, |
| filter: '_doc_ids', |
| doc_ids: ['1', '3'] |
| }); |
| }).then(function (changes) { |
| var ids = changes.results.map(function (x) { |
| return x.id; |
| }); |
| ids.sort().should.deep.equal(['1', '3']); |
| }); |
| }); |
| |
| it('#2569 Live doc_ids filter with filter=_doc_ids', function () { |
| var docs = [ |
| {_id: '0'}, |
| {_id: '1'}, |
| {_id: '2'}, |
| {_id: '3'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| return db.bulkDocs(docs).then(function () { |
| return db.changes({ |
| return_docs: true, |
| filter: '_doc_ids', |
| doc_ids: ['1', '3'] |
| }); |
| }).then(function (changes) { |
| var ids = changes.results.map(function (x) { |
| return x.id; |
| }); |
| ids.sort().should.deep.equal(['1', '3']); |
| }); |
| }); |
| |
| it('Changes to same doc are grouped', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var docs2 = [ |
| {_id: '2', integer: 11}, |
| {_id: '3', integer: 12} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function (err, info) { |
| docs2[0]._rev = info[2].rev; |
| docs2[1]._rev = info[3].rev; |
| db.put(docs2[0], function () { |
| db.put(docs2[1], function () { |
| db.changes({ |
| return_docs: true, |
| include_docs: true |
| }).on('complete', function (changes) { |
| changes.results.length.should.equal(4); |
| |
| var second = findById(changes.results, '2'); |
| second.changes.length.should.equal(1); |
| second.doc.integer.should.equal(11); |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| }); |
| }); |
| |
| it('Changes with conflicts are handled correctly', function (testDone) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var docs2 = [ |
| {_id: '2', integer: 11}, |
| {_id: '3', integer: 12} |
| ]; |
| var localdb = new PouchDB(dbs.name); |
| var remotedb = new PouchDB(dbs.remote); |
| localdb.bulkDocs({ docs: docs1 }).then(function (info) { |
| docs2[0]._rev = info[2].rev; |
| docs2[1]._rev = info[3].rev; |
| return localdb.put(docs2[0]).then(function () { |
| return localdb.put(docs2[1]).then(function (info) { |
| var rev2 = info.rev; |
| return PouchDB.replicate(localdb, remotedb).then(function () { |
| // update remote once, local twice, then replicate from |
| // remote to local so the remote losing conflict is later in |
| // the tree |
| return localdb.put({ |
| _id: '3', |
| _rev: rev2, |
| integer: 20 |
| }).then(function (resp) { |
| var rev3Doc = { |
| _id: '3', |
| _rev: resp.rev, |
| integer: 30 |
| }; |
| return localdb.put(rev3Doc).then(function (resp) { |
| var rev4local = resp.rev; |
| var rev4Doc = { |
| _id: '3', |
| _rev: rev2, |
| integer: 100 |
| }; |
| return remotedb.put(rev4Doc).then(function (resp) { |
| var remoterev = resp.rev; |
| return PouchDB.replicate(remotedb, localdb).then( |
| function () { |
| return localdb.changes({ |
| return_docs: true, |
| include_docs: true, |
| style: 'all_docs', |
| conflicts: true |
| }).on('error', testDone) |
| .then(function (changes) { |
| changes.results.length.should.equal(4); |
| var ch = findById(changes.results, '3'); |
| ch.changes.length.should.equal(2); |
| ch.doc.integer.should.equal(30); |
| ch.doc._rev.should.equal(rev4local); |
| ch.changes.should.deep.equal([ |
| { rev: rev4local }, |
| { rev: remoterev } |
| ]); |
| |
| ch.doc.should.have.property('_conflicts'); |
| ch.doc._conflicts.length.should.equal(1); |
| ch.doc._conflicts[0].should.equal(remoterev); |
| }); |
| }); |
| }); |
| }); |
| }); |
| }); |
| }); |
| }).then(function () { |
| testDone(); |
| }, testDone); |
| }); |
| }); |
| |
| it('Change entry for a deleted doc', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function (err, info) { |
| var rev = info[3].rev; |
| db.remove({ |
| _id: '3', |
| _rev: rev |
| }, function () { |
| db.changes({ |
| return_docs: true, |
| include_docs: true |
| }).on('complete', function (changes) { |
| changes.results.length.should.equal(4); |
| var ch = findById(changes.results, '3'); |
| ch.deleted.should.equal(true); |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| }); |
| |
| it('changes large number of docs', function (done) { |
| var docs = []; |
| var num = 30; |
| for (var i = 0; i < num; i++) { |
| docs.push({ |
| _id: 'doc_' + i, |
| foo: 'bar_' + i |
| }); |
| } |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs }, function () { |
| db.changes({return_docs: true}).on('complete', function (res) { |
| res.results.length.should.equal(num); |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('Calling db.changes({since: \'now\'})', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: [{ foo: 'bar' }] }, function () { |
| db.info(function () { |
| var api = db.changes({ |
| since: 'now' |
| }).on('complete', function () { |
| done(); |
| }).on('error', done); |
| api.should.be.an('object'); |
| api.cancel.should.be.an('function'); |
| }); |
| }); |
| }); |
| |
| //Duplicate to make sure both api options work. |
| it('Calling db.changes({since: \'latest\'})', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: [{ foo: 'bar' }] }, function () { |
| db.info(function () { |
| var api = db.changes({ |
| since: 'latest' |
| }).on('complete', function () { |
| done(); |
| }).on('error', done); |
| api.should.be.an('object'); |
| api.cancel.should.be.an('function'); |
| }); |
| }); |
| }); |
| |
| it('Closing db does not cause a crash if changes cancelled', |
| function (done) { |
| var db = new PouchDB(dbs.name); |
| var called = 0; |
| function checkDone() { |
| called++; |
| if (called === 2) { |
| done(); |
| } |
| } |
| db.bulkDocs({ docs: [{ foo: 'bar' }] }, function () { |
| var changes = db.changes({ |
| live: true |
| }).on('complete', function (result) { |
| result.status.should.equal('cancelled'); |
| checkDone(); |
| }); |
| should.exist(changes); |
| changes.cancel.should.be.a('function'); |
| changes.cancel(); |
| db.close(function (error) { |
| should.not.exist(error); |
| checkDone(); |
| }); |
| }); |
| }); |
| |
| it('fire-complete-on-cancel', function (done) { |
| var db = new PouchDB(dbs.name); |
| var cancelled = false; |
| var changes = db.changes({ |
| live: true |
| }).on('complete', function (result) { |
| cancelled.should.equal(true); |
| should.exist(result); |
| if (result) { |
| result.status.should.equal('cancelled'); |
| } |
| done(); |
| }).on('error', done); |
| should.exist(changes); |
| changes.cancel.should.be.a('function'); |
| setTimeout(function () { |
| cancelled = true; |
| changes.cancel(); |
| }, 100); |
| }); |
| |
| it('changes are not duplicated', function (done) { |
| var db = new PouchDB(dbs.name); |
| var called = 0; |
| var changes = db.changes({ |
| live: true |
| }).on('change', function () { |
| called++; |
| if (called === 1) { |
| setTimeout(function () { |
| changes.cancel(); |
| }, 1000); |
| } |
| }).on('complete', function () { |
| called.should.equal(1); |
| done(); |
| }); |
| db.post({key: 'value'}); |
| }); |
| |
| it('supports return_docs=false', function (done) { |
| if (testUtils.isIE()) { |
| return done(); |
| } |
| var db = new PouchDB(dbs.name); |
| var docs = []; |
| var num = 10; |
| for (var i = 0; i < num; i++) { |
| docs.push({ _id: 'doc_' + i}); |
| } |
| var changes = 0; |
| db.bulkDocs({ docs: docs }, function (err) { |
| if (err) { |
| return done(err); |
| } |
| db.changes({ |
| descending: true, |
| return_docs: false |
| }).on('change', function () { |
| changes++; |
| }).on('complete', function (results) { |
| results.results.should.have.length(0, '0 results returned'); |
| changes.should.equal(num, 'correct number of changes'); |
| done(); |
| }).on('error', function (err) { |
| done(err); |
| }); |
| }); |
| }); |
| |
| it('should respects limit', function (done) { |
| var docs1 = [ |
| {_id: '_local/foo'}, |
| {_id: 'a', integer: 0}, |
| {_id: 'b', integer: 1}, |
| {_id: 'c', integer: 2}, |
| {_id: 'd', integer: 3} |
| ]; |
| var called = 0; |
| var db = new PouchDB(dbs.name); |
| db.bulkDocs({ docs: docs1 }, function () { |
| db.changes({ |
| limit: 1 |
| }).on('change', function () { |
| (called++).should.equal(0); |
| }).on('complete', function () { |
| setTimeout(function () { |
| done(); |
| }, 50); |
| }); |
| }); |
| }); |
| |
| it('doesn\'t throw if opts.complete is undefined', function (done) { |
| var db = new PouchDB(dbs.name); |
| db.put({_id: 'foo'}).then(function () { |
| db.changes().on('change', function () { |
| done(); |
| }).on('error', function (err) { |
| done(err); |
| }); |
| }, done); |
| }); |
| |
| it('handle individual changes in live replication', function (done) { |
| if (testUtils.isIE()) { |
| return done(); |
| } |
| var db = new PouchDB(dbs.name); |
| var len = 80; |
| var called = 0; |
| var changesDone = false; |
| var changesWritten = 0; |
| var changes = db.changes({live: true}); |
| |
| changes.on('change', function () { |
| called++; |
| if (called === len) { |
| changes.cancel(); |
| } |
| }).on('error', done).on('complete', function () { |
| changesDone = true; |
| maybeDone(); |
| }); |
| |
| var i = -1; |
| |
| function maybeDone() { |
| if (changesDone && changesWritten === len) { |
| done(); |
| } |
| } |
| |
| function after() { |
| changesWritten++; |
| db.listeners('destroyed').should.have.length.lessThan(5); |
| maybeDone(); |
| } |
| |
| while (++i < len) { |
| db.post({}).then(after).catch(done); |
| } |
| |
| }); |
| |
| it('changes-filter without filter', function (done) { |
| var docs1 = [ |
| {_id: '0', integer: 0}, |
| {_id: '1', integer: 1}, |
| {_id: '2', integer: 2}, |
| {_id: '3', integer: 3} |
| ]; |
| var docs2 = [ |
| {_id: '4', integer: 4}, |
| {_id: '5', integer: 5}, |
| {_id: '6', integer: 6}, |
| {_id: '7', integer: 7} |
| ]; |
| var db = new PouchDB(dbs.name); |
| var count = 0; |
| db.bulkDocs({ docs: docs1 }, function () { |
| var changes = db.changes({ |
| live: true |
| }).on('complete', function (result) { |
| result.status.should.equal('cancelled'); |
| done(); |
| }).on('change', function () { |
| count += 1; |
| if (count === 8) { |
| changes.cancel(); |
| } |
| }).on('error', done); |
| db.bulkDocs({ docs: docs2 }); |
| }); |
| }); |
| |
| |
| it('#3539 - Exception in changes is fine', function (done) { |
| |
| var db = new PouchDB(dbs.name); |
| var count = 0; |
| |
| var changes = db.changes({live: true}); |
| |
| changes.on('change', function () { |
| ++count; |
| if (count === 1) { |
| throw new Error('deliberate error in changes'); |
| } else if (count === 3) { |
| changes.cancel(); |
| } |
| }); |
| |
| changes.on('complete', function () { |
| done(); |
| }); |
| |
| db.post({ test: 'some stuff' }).then(function () { |
| return db.post({ test: 'more stuff' }); |
| }).then(function () { |
| db.post({ test: 'and more stuff' }); |
| }); |
| }); |
| |
| it('Changes with selector', function (done) { |
| if (!testUtils.isCouchMaster() && adapter === 'http') { |
| return done(); |
| } |
| |
| var docs = [ |
| {_id: '0', user: 'foo'}, |
| {_id: '1', user: 'bar'}, |
| {_id: '2', user: 'foo'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| |
| db.bulkDocs({ docs: docs }, function () { |
| db.changes({ |
| return_docs: true, |
| selector: {"user": "foo"}, |
| include_docs: true |
| }).on('complete', function (results) { |
| results.results.length.should.equal(2); |
| var first = findById(results.results, '0'); |
| first.doc.user.should.equal('foo'); |
| var second = findById(results.results, '2'); |
| second.doc.user.should.equal('foo'); |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('Changes with selector, explicit filter', function (done) { |
| if (!testUtils.isCouchMaster() && adapter === 'http') { |
| return done(); |
| } |
| |
| var docs = [ |
| {_id: '0', user: 'foo'}, |
| {_id: '1', user: 'bar'}, |
| {_id: '2', user: 'foo'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| |
| db.bulkDocs({ docs: docs }, function () { |
| db.changes({ |
| return_docs: true, |
| selector: {"user": "foo"}, |
| filter: '_selector', |
| include_docs: true |
| }).on('complete', function (results) { |
| results.results.length.should.equal(2); |
| var first = findById(results.results, '0'); |
| first.doc.user.should.equal('foo'); |
| var second = findById(results.results, '2'); |
| second.doc.user.should.equal('foo'); |
| done(); |
| }).on('error', done); |
| }); |
| }); |
| |
| it('Changes with selector and mismatched filter', function (done) { |
| var db = new PouchDB(dbs.name); |
| |
| db.changes({ |
| selector: {"user": "foo"}, |
| filter: function () { return false; } |
| }).on('complete', function () { |
| done('expected failure'); |
| }).on('error', function (err) { |
| err.message.should.equal('selector invalid for filter "function"'); |
| done(); |
| }); |
| }); |
| |
| it('Changes with limit and selector', function (done) { |
| if (!testUtils.isCouchMaster() && adapter === 'http') { |
| return done(); |
| } |
| |
| var docs = [ |
| {_id: '0', user: 'foo'}, |
| {_id: '1', user: 'bar'}, |
| {_id: '2', user: 'foo'} |
| ]; |
| var db = new PouchDB(dbs.name); |
| |
| db.bulkDocs({ docs: docs }, function () { |
| return db.changes({ |
| return_docs: true, |
| limit: 1, |
| selector: {"user": "foo"}, |
| include_docs: true |
| }).on('complete', function (results) { |
| results.results.length.should.equal(1); |
| var first = results.results[0].doc; |
| var last_seq = results.last_seq; |
| |
| return db.changes({ |
| return_docs: true, |
| limit: 1, |
| selector: {"user": "foo"}, |
| include_docs: true, |
| since: last_seq |
| }).on('complete', function (results) { |
| results.results.length.should.equal(1); |
| var second = results.results[0].doc; |
| |
| first._id.should.not.equal(second._id); |
| first.user.should.equal('foo'); |
| second.user.should.equal('foo'); |
| done(); |
| }).on('error', done) |
| .catch(done); |
| }).on('error', done); |
| }).catch(done); |
| }); |
| |
| }); |
| }); |
| |
| describe('changes-standalone', function () { |
| |
| it.skip('Changes reports errors', function (done) { |
| this.timeout(2000); |
| var db = new PouchDB('http://infiniterequest.com', { skip_setup: true }); |
| db.changes({ |
| timeout: 1000 |
| }).on('error', function (err) { |
| should.exist(err); |
| done(); |
| }); |
| }); |
| |
| }); |