diff --git a/.gitignore b/.gitignore index f06235c..0e75fe5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules dist +coverage diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4a63e93 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +sudo: false +language: node_js + +cache: + directories: + - node_modules + +node_js: + - "0.10" + +services: + - couchdb + +before_install: + - npm i -g npm@^2.0.0 + +before_script: + - npm prune + +script: npm run $COMMAND + +env: + matrix: + - COMMAND='helper -- lint' + - COMMAND='helper -- js-test' + - COMMAND='build' + +#after_success: +# - npm run helper -- semantic-release + diff --git a/README.md b/README.md index 80d2dc3..8bd0566 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,38 @@ pouchdb-show ============ +[![Build Status](https://travis-ci.org/pouchdb/pouchdb-show.svg?branch=master)](https://travis-ci.org/pouchdb/pouchdb-show) +[![Dependency Status](https://david-dm.org/pouchdb/pouchdb-show.svg)](https://david-dm.org/pouchdb/pouchdb-show) +[![devDependency Status](https://david-dm.org/pouchdb/pouchdb-show/dev-status.svg)](https://david-dm.org/pouchdb/pouchdb-show#info=devDependencies) + A PouchDB plug-in that allows you to re-use your CouchDB show functions on the client side. A browser version is available. -See also [pouchdb-show's documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-show-plug-in) +TODO: rst -> md, update & restructure +```rst +.. _pouchdb-show-plug-in: + +PouchDB Show plug-in +==================== ++----------------------+-----------------+ +| NodeJS package name: | `pouchdb-show`_ | ++----------------------+-----------------+ +| Browser object name: | ``window.Show`` | ++----------------------+-----------------+ + +First, make sure you understand how show functions work in CouchDB. A +good start is `the CouchDB guide entry on shows`_. + +.. _pouchdb-show: https://www.npmjs.org/package/pouchdb-show +.. _the CouchDB guide entry on shows: http://guide.couchdb.org/draft/formats.html + +.. js:function:: Show.show(showPath[, options[, callback]]) + + Similar to the :js:func:`List.list` function, but then for show + functions. Only differences are documented. + + :param string showPath: specifies the show (and optionally the + document) to use. Has the following form: + ``designDocName/showName[/docId]`` -[Website of this plug-in and a few others](http://python-pouchdb.marten-de-vries.nl/plugins.html) +``` diff --git a/index.js b/index.js index 63e62b7..3806bca 100644 --- a/index.js +++ b/index.js @@ -68,7 +68,7 @@ function offlineQuery(db, designDocName, showName, docId, req, options) { } return designDoc; }); - var docPromise = db.get(docId, options).catch(function (err) { + var docPromise = db.get(docId, options).catch(function () { //doc might not exist - that's ok and expected. return null; }); diff --git a/package.json b/package.json index ca6e500..b0a8354 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,37 @@ { - "name": "pouchdb-show", - "version": "1.0.7", - "main": "index.js", - "description": "A PouchDB plug-in that allows you to re-use your CouchDB show functions on the client side.", - "repository": "pouchdb/pouchdb-show", - "homepage": "http://python-pouchdb.marten-de-vries.nl/plugins.html", - "keywords": [ - "pouch", - "pouchdb", - "couch", - "couchdb", - "show", - "design" - ], - "license": "Apache-2.0", - "author": "Marten de Vries", - "dependencies": { - "couchdb-objects": "^1.0.0", - "couchdb-render": "^1.0.0", - "pouchdb-req-http-query": "^1.0.0", - "promise-nodify": "^1.0.0", - "pouchdb-promise": "^0.0.0", - "pouchdb-plugin-error": "^1.0.0" - }, - "devDependencies": { - "browserify": "^4.1.8", - "uglify-js": "^2.4.13", - "es3ify": "^0.1.3" - }, - "scripts": { - "build-js": "mkdir -p dist && browserify index.js -s Show -g es3ify -o dist/pouchdb-show.js", - "build": "npm run build-js; cd dist; uglifyjs pouchdb-show.js -mc > pouchdb-show.min.js" - } + "name": "pouchdb-show", + "version": "1.0.7", + "main": "index.js", + "description": "A PouchDB plug-in that allows you to re-use your CouchDB show functions on the client side.", + "repository": { + "type": "git", + "url": "https://github.com/pouchdb/pouchdb-show.git" + }, + "keywords": [ + "pouch", + "pouchdb", + "couch", + "couchdb", + "show", + "design" + ], + "license": "Apache-2.0", + "author": "Marten de Vries", + "dependencies": { + "couchdb-objects": "^1.0.0", + "couchdb-render": "^1.0.0", + "pouchdb-req-http-query": "^1.0.0", + "promise-nodify": "^1.0.0", + "pouchdb-promise": "^0.0.0", + "pouchdb-plugin-error": "^1.0.0" + }, + "devDependencies": { + "navigator": "^1.0.1", + "pouchdb-plugin-helper": "^2.0.0" + }, + "scripts": { + "helper": "./node_modules/.bin/pouchdb-plugin-helper", + "test": "npm run helper -- test", + "build": "npm run helper -- build Show" + } } diff --git a/test/features.js b/test/features.js new file mode 100644 index 0000000..fc69a19 --- /dev/null +++ b/test/features.js @@ -0,0 +1,302 @@ +import {setup, teardown, showDocument, shouldThrowError, should, checkUserAgent, checkUuid} from './utils'; + +let db; + +describe('Sync show tests', () => { + beforeEach(async () => { + db = setup(); + await db.put(showDocument); + }); + afterEach(teardown); + + it('should fail when given an invalid response object', async () => { + const err = await shouldThrowError(async () => { + await db.show('test/invalidRespObject'); + }); + err.status.should.equal(500); + err.name.should.equal('external_response_error'); + err.message.should.equal('Invalid data from external server: {<<"abc">>,<<"test">>}'); + }); + + it('show without doc with a browser-like env', async () => { + global.window = {navigator: require('navigator')}; + + const result = await db.show('test/myshow'); + result.code.should.equal(200); + + delete global.window; + }); + + it('invalid return type and provides', async () => { + const result = await db.show('test/invalidReturnTypeAndProvides'); + result.code.should.equal(200); + result.body.should.equal(''); + }); + + it('throwing error', async () => { + const err = await shouldThrowError(async () => { + await db.show('test/throwingError'); + }); + err.status.should.equal(500); + }); + + it('throwing error in provides', async () => { + const err = await shouldThrowError(async () => { + await db.show('test/throwingErrorInProvides'); + }); + err.status.should.equal(500); + }); + + it('show without doc', async () => { + const result = await db.show('test/myshow'); + result.code.should.equal(200); + result.headers['Content-Type'].should.equal('text/html; charset=utf-8'); + result.headers.Vary.should.equal('Accept'); + result.body.should.equal('no doc'); + }); + + it('show with doc', async () => { + await db.post({_id: 'mytest', description: 'Hello World!'}); + + const result = await db.show('test/myshow/mytest'); + result.body.should.equal('Hello World!'); + }); + + it('overwrite args', async () => { + const resp = await db.show('test/args', {method: 'POST'}); + const req = JSON.parse(resp.body).args[1]; + req.method.should.equal('POST'); + }); + + it('overwrite header', async () => { + const resp = await db.show('test/args', {headers: {Host: 'example.com'}}); + const req = JSON.parse(resp.body).args[1]; + // check if the header update was succesful. + req.headers.Host.should.equal('example.com'); + // check if other headers (test subject is Accept) are still set. + req.headers.Accept.should.equal('text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'); + }); + + it('show args', async () => { + const [doc, req] = JSON.parse((await db.show('test/args')).body).args; + + // test doc - well, the unavailability of it... + should.equal(doc, null); + + // test request object + req.body.should.equal('undefined'); + req.cookie.should.eql({}); + req.form.should.eql({}); + + req.headers.Host.should.equal('localhost:5984'); + req.headers.Accept.should.equal('text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'); + req.headers['Accept-Language'].should.contain('en'); + req.headers['Accept-Language'].should.contain('en-us'); + checkUserAgent(req.headers['User-Agent']); + + should.equal(req.id, null); + req.info.db_name.should.equal('test'); + req.info.should.have.property('update_seq'); + req.method.should.equal('GET'); + req.path.should.eql(['test', '_design', 'test', '_show', 'args']); + req.peer.should.equal('127.0.0.1'); + req.query.should.eql({}); + req.raw_path.should.equal('/test/_design/test/_show/args'); + req.requested_path.should.eql(['test', '_design', 'test', '_show', 'args']); + req.secObj.should.eql({}); + req.userCtx.should.eql({ + db: 'test', + name: null, + roles: [ + '_admin' + ] + }); + checkUuid(req.uuid); + }); + + it('unexisting doc', async () => { + const [doc, req] = JSON.parse((await db.show('test/args/abc')).body).args; + should.equal(doc, null); + req.id.should.equal('abc'); + req.path.should.contain('abc'); + }); + + it('with design doc as arg', async () => { + const resp = await db.show('test/args/_design/test'); + const [doc, req] = JSON.parse(resp.body).args; + req.id.should.equal('_design/test'); + req.raw_path.should.equal('/test/_design/test/_show/args/_design/test'); + doc.should.have.property('shows'); + }); + + it('with fake design doc as arg', async () => { + const resp = await db.show('test/args/_design'); + const [doc, req] = JSON.parse(resp.body).args; + should.equal(doc, null); + req.id.should.equal('_design'); + req.raw_path.should.equal('/test/_design/test/_show/args/_design'); + }); + + it('setting query', async () => { + const resp = await db.show('test/args', {query: {a: 1}}); + const [doc, req] = JSON.parse(resp.body).args; + should.equal(doc, null); + req.raw_path.slice(-4).should.equal('?a=1'); + req.requested_path.should.eql(['test', '_design', 'test', '_show', 'args?a=1']); + req.path.should.eql(['test', '_design', 'test', '_show', 'args']); + }); + + it('setting form', async () => { + const resp = await db.show('test/args', {form: {a: 1}}); + const [doc, req] = JSON.parse(resp.body).args; + should.equal(doc, null); + req.body.should.equal('a=1'); + req.headers['Content-Type'].should.equal('application/x-www-form-urlencoded'); + req.headers['Content-Length'].should.equal('3'); + req.method.should.equal('POST'); + }); + + it('unexisting design doc', async () => { + const err = await shouldThrowError(async () => { + await db.show('abc/args'); + }); + err.name.should.equal('not_found'); + err.message.should.equal('missing'); + }); + + it('unexisting show function', async () => { + const err = await shouldThrowError(async () => { + await db.show('test/unexisting-show'); + }); + err.status.should.equal(404); + err.name.should.equal('not_found'); + err.message.should.equal("missing show function unexisting-show on design doc _design/test"); + }); + + it('providers default', async () => { + const resp = await db.show('test/usingProviders'); + resp.code.should.equal(200); + resp.body.should.equal('