From f10d267ee464ee44ab351d31de8b7f0769726a66 Mon Sep 17 00:00:00 2001 From: Stephan Tabor Date: Tue, 13 Oct 2015 00:14:16 -0400 Subject: [PATCH] [test] add stubs and unit tests --- gulpfile.js | 4 +- lib/index.js | 7 +- npm-debug.log | 114 --------------- package.json | 6 +- test/integration.js | 293 +++++++++++++++++++++++++++++++++++++ test/stubs/data.js | 37 +++++ test/stubs/request.js | 15 ++ test/{index.js => unit.js} | 8 +- 8 files changed, 359 insertions(+), 125 deletions(-) delete mode 100644 npm-debug.log create mode 100644 test/integration.js create mode 100644 test/stubs/data.js create mode 100644 test/stubs/request.js rename test/{index.js => unit.js} (98%) diff --git a/gulpfile.js b/gulpfile.js index 2f2b474..ee09ec1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -36,9 +36,9 @@ gulp.task('pre-test', function () { gulp.task('test', ['pre-test'], function (cb) { var mochaErr; - gulp.src('test/**/*.js') + gulp.src(['test/unit.js', 'test/integration.js']) .pipe(plumber()) - .pipe(mocha({reporter: 'spec'})) + .pipe(mocha({reporter: 'nyan'})) .on('error', function (err) { mochaErr = err; }) diff --git a/lib/index.js b/lib/index.js index 6647719..f7323a1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,9 +3,10 @@ import Promise from 'bluebird'; import {extend, urlJoin} from './utils.js'; class Pkmn { - constructor() { + constructor(di) { this.baseUrl = 'http://pokeapi.co'; this.queryUrl = urlJoin(this.baseUrl, 'api/v1'); + this.request = di || request; } static listResources() { @@ -34,7 +35,7 @@ class Pkmn { return this.resource(resource); } if (/pokedex/.test(resource)) { - return this.resource('/api/v1/pokedex/1/'); + return this.resource('/api/v1/pokedex/1'); } if (id === undefined) { return this.buildQuery(resource); @@ -58,7 +59,7 @@ class Pkmn { end(query) { return new Promise((resolve, reject) => { - request + this.request .get(query, (err, res, body) => { if (res.statusCode !== 200) { let e = new Error(res.statusCode); diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index e02b89f..0000000 --- a/npm-debug.log +++ /dev/null @@ -1,114 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ '/usr/local/Cellar/node/4.1.1/bin/node', -1 verbose cli '/usr/local/bin/npm', -1 verbose cli 'publish' ] -2 info using npm@2.14.4 -3 info using node@v4.1.1 -4 verbose publish [ '.' ] -5 silly cache add args [ '.', null ] -6 verbose cache add spec . -7 silly cache add parsed spec Result { -7 silly cache add raw: '.', -7 silly cache add scope: null, -7 silly cache add name: null, -7 silly cache add rawSpec: '.', -7 silly cache add spec: '/Users/stephantabor/HR/projects/pokemon', -7 silly cache add type: 'directory' } -8 verbose addLocalDirectory /Users/stephantabor/.npm/pkmn/1.0.3/package.tgz not in flight; packing -9 verbose tar pack [ '/Users/stephantabor/.npm/pkmn/1.0.3/package.tgz', -9 verbose tar pack '/Users/stephantabor/HR/projects/pokemon' ] -10 verbose tarball /Users/stephantabor/.npm/pkmn/1.0.3/package.tgz -11 verbose folder /Users/stephantabor/HR/projects/pokemon -12 info prepublish pkmn@1.0.3 -13 verbose unsafe-perm in lifecycle true -14 verbose addLocalTarball adding from inside cache /Users/stephantabor/.npm/pkmn/1.0.3/package.tgz -15 silly cache afterAdd pkmn@1.0.3 -16 verbose afterAdd /Users/stephantabor/.npm/pkmn/1.0.3/package/package.json not in flight; writing -17 verbose afterAdd /Users/stephantabor/.npm/pkmn/1.0.3/package/package.json written -18 silly publish { name: 'pkmn', -18 silly publish version: '1.0.3', -18 silly publish description: 'a node.js pokeapi.co api wrapper', -18 silly publish homepage: 'https://github.com/stephantabor/pkmn#readme', -18 silly publish repository: -18 silly publish { type: 'git', -18 silly publish url: 'git+https://github.com/stephantabor/pkmn.git' }, -18 silly publish author: -18 silly publish { name: 'Stephan Tabor', -18 silly publish email: 'stephantabor@gmail.com', -18 silly publish url: 'http://stephantabor.com' }, -18 silly publish files: [ 'dist' ], -18 silly publish main: 'dist/index.js', -18 silly publish keywords: [ 'pokemon', 'pokémon', 'pokeapi', 'pokedex' ], -18 silly publish devDependencies: -18 silly publish { 'babel-core': '^5.5.0', -18 silly publish chai: '^3.3.0', -18 silly publish 'chai-as-promised': '^5.1.0', -18 silly publish gulp: '^3.6.0', -18 silly publish 'gulp-babel': '^5.1.0', -18 silly publish 'gulp-eslint': '^1.0.0', -18 silly publish 'gulp-exclude-gitignore': '^1.0.0', -18 silly publish 'gulp-istanbul': '^0.9.0', -18 silly publish 'gulp-mocha': '^2.0.0', -18 silly publish 'gulp-nsp': '^0.4.5', -18 silly publish 'gulp-plumber': '^1.0.0', -18 silly publish isparta: '^3.0.3' }, -18 silly publish scripts: { prepublish: 'gulp prepublish', test: 'gulp' }, -18 silly publish license: 'MIT', -18 silly publish dependencies: { bluebird: '^2.10.2', request: '^2.64.0' }, -18 silly publish readme: '# pkmn [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url]\n> pokeapi.co wrapper. Details about the api can be found here: http://pokeapi.co/docs/\n\n\n## Install\n\n```sh\n$ npm install --save pkmn\n```\n\n\n## Usage\n\n```js\nvar Pkmn = require(\'pkmn\');\nvar p = new Pkmn();\n\n```\n\n### Get\nAll methods return a bluebird promise.\n\n`.get` requires two arguments, a `resource` and an `id`. `id` can be a `string`, `number`, or `array`.\n\n```js\np.get(\'pokemon\', \'meowth\')\n .then(pokemon => /* do something */)\n .catch(err => /* handle error */);\n \np.get(\'pokemon\', [1, \'mew\'])\n .then(console.log) // logs [{name: \'Bulbasaur\' ...}, {name: \'Mew\'...}]\n```\n\nErrors and responses from the pokeapi that are not of status code 200 will\nbe rejected.\n\n```js\np.get(\'pokemon\', \'Vegeta\')\n .catch(console.error) // logs [Error] 404\n```\nToo few arguments to `.get` also throws an error. All resources except `pokedex` require an `id` argument to passed to `.get` with them. \n```js \np.get(\'pokemon\')\n .then(console.log) // doesn\'t log\n .catch(console.error) // logs [Error] id argument required\n \np.get(\'pokedex\')\n .then(console.log) // logs the pokedex\n```\n\nThe full list of valid resources is: \n```\npokedex, pokemon, egg, type, description, move, ability, sprite, game\n```\n\nAdditionally, `.get` can take a `resource_id` as an argument. This is useful because most responses from pokeapi will have have a `resource_id` field. For example, a pokemon will come with a list of moves, each move contains a `resource_id` that you can pass directly to `.get` to retrieve the details of that move.\n\n```js\np.get(\'/api/v1/pokemon/bulbasaur\')\n...\n```\n\n\n### Convenience Methods\n\nAll valid resource argumets to `.get` also have convenience methods: \n```js\np.pokemon([33, \'snorlax\', 209)\np.ability(56)\np.egg(2)\np.pokedex()\n\n// etc...\n```\n\nThe full list of api resources can obtained from the api via `.api`\n\n```js\np.api()\n```\n\n## License\n\nMIT © [Stephan Tabor](http://stephantabor.com)\n\n\n[npm-image]: https://badge.fury.io/js/pkmn.svg\n[npm-url]: https://npmjs.org/package/pkmn\n[travis-image]: https://travis-ci.org/stephantabor/pkmn.svg?branch=master\n[travis-url]: https://travis-ci.org/stephantabor/pkmn\n[daviddm-image]: https://david-dm.org/stephantabor/pkmn.svg?theme=shields.io\n[daviddm-url]: https://david-dm.org/stephantabor/pkmn\n', -18 silly publish readmeFilename: 'README.md', -18 silly publish gitHead: 'a69ca8a465e72bca3d0945242c57cd13ed0455ce', -18 silly publish bugs: { url: 'https://github.com/stephantabor/pkmn/issues' }, -18 silly publish _id: 'pkmn@1.0.3', -18 silly publish _shasum: '3fc135e6258308f8d29af3963c9cc67f7dde248c', -18 silly publish _from: '.' } -19 verbose getPublishConfig undefined -20 silly mapToRegistry name pkmn -21 silly mapToRegistry using default registry -22 silly mapToRegistry registry https://registry.npmjs.org/ -23 silly mapToRegistry uri https://registry.npmjs.org/pkmn -24 verbose publish registryBase https://registry.npmjs.org/ -25 silly publish uploading /Users/stephantabor/.npm/pkmn/1.0.3/package.tgz -26 verbose request uri https://registry.npmjs.org/pkmn -27 verbose request sending authorization for write operation -28 info attempt registry request try #1 at 9:30:45 PM -29 verbose request using bearer token for auth -30 verbose request id 9bc0df55b0ada910 -31 http request PUT https://registry.npmjs.org/pkmn -32 http 403 https://registry.npmjs.org/pkmn -33 verbose headers { 'content-type': 'application/json', -33 verbose headers 'cache-control': 'max-age=60', -33 verbose headers 'content-length': '95', -33 verbose headers 'accept-ranges': 'bytes', -33 verbose headers date: 'Sun, 11 Oct 2015 01:30:46 GMT', -33 verbose headers via: '1.1 varnish', -33 verbose headers connection: 'keep-alive', -33 verbose headers 'x-served-by': 'cache-atl6227-ATL', -33 verbose headers 'x-cache': 'MISS', -33 verbose headers 'x-cache-hits': '0', -33 verbose headers 'x-timer': 'S1444527045.880144,VS0,VE966' } -34 verbose request invalidating /Users/stephantabor/.npm/registry.npmjs.org/pkmn on PUT -35 error publish Failed PUT 403 -36 verbose stack Error: "You cannot publish over the previously published version 1.0.3." : pkmn -36 verbose stack at makeError (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:263:12) -36 verbose stack at CachingRegistryClient. (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:251:14) -36 verbose stack at Request._callback (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:171:14) -36 verbose stack at Request.self.callback (/usr/local/lib/node_modules/npm/node_modules/request/request.js:198:22) -36 verbose stack at emitTwo (events.js:87:13) -36 verbose stack at Request.emit (events.js:172:7) -36 verbose stack at Request. (/usr/local/lib/node_modules/npm/node_modules/request/request.js:1073:14) -36 verbose stack at emitOne (events.js:82:20) -36 verbose stack at Request.emit (events.js:169:7) -36 verbose stack at IncomingMessage. (/usr/local/lib/node_modules/npm/node_modules/request/request.js:1019:12) -37 verbose statusCode 403 -38 verbose pkgid pkmn -39 verbose cwd /Users/stephantabor/HR/projects/pokemon -40 error Darwin 15.0.0 -41 error argv "/usr/local/Cellar/node/4.1.1/bin/node" "/usr/local/bin/npm" "publish" -42 error node v4.1.1 -43 error npm v2.14.4 -44 error code E403 -45 error "You cannot publish over the previously published version 1.0.3." : pkmn -46 error If you need help, you may report this error at: -46 error -47 verbose exit [ 1, true ] diff --git a/package.json b/package.json index bb0de1d..9882c9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pkmn", - "version": "1.0.4", + "version": "1.0.5", "description": "a node.js pokeapi.co api wrapper", "homepage": "", "repository": "stephantabor/pkmn", @@ -31,7 +31,9 @@ "gulp-mocha": "^2.0.0", "gulp-nsp": "^0.4.5", "gulp-plumber": "^1.0.0", - "isparta": "^3.0.3" + "isparta": "^3.0.3", + "rewire": "^2.3.4", + "sinon": "^1.17.1" }, "scripts": { "prepublish": "gulp prepublish", diff --git a/test/integration.js b/test/integration.js new file mode 100644 index 0000000..2b03764 --- /dev/null +++ b/test/integration.js @@ -0,0 +1,293 @@ +import Pkmn from '../lib'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('pkmn api integration tests', function () { + let p; + this.timeout(5000); + + beforeEach(() => { + p = new Pkmn(); + }); + + describe('end', () => { + it('should fail for malformed query', () => { + p.query = 'ayyy.lmao'; + return expect(p.end()).to.eventually.be.rejected; + }); + }); + describe('get', () => { + it('should get resource by name', () => { + return expect(p.get('pokemon', 'bulbasaur')) + .to.eventually.be.fulfilled; + }); + + it('should get resource by resource_uri', () => { + var uri = '/api/v1/description/4'; + return expect(p.get(uri).get('pokemon').get('name')) + .to.eventually.equal('bulbasaur'); + }); + + it('should reject for too few arguments', () => { + return expect(p.get('pokemon')).to.eventually.be.rejected; + }); + + it('should get several resources for id array', () => { + let names = p.get('pokemon', ['bulbasaur', 1, 1]) + .reduce((name, poke) => + name === poke.name ? 'Bulbasaur' : false, 'Bulbasaur'); + + + expect(names).to.eventually.equal('Bulbasaur'); + }); + }); + describe('resource', () => { + it('should get resource by resource_uri', () => { + const uri = '/api/v1/description/4/'; + return expect(p.resource(uri).get('pokemon').get('name')) + .to.eventually.equal('bulbasaur'); + }); + }); + describe('pokemon', () => { + let poke; + before(() => { + poke = p.pokemon(1); + }); + + it('should get pokemon', () => { + return expect(poke).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(poke).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(poke).to.eventually.have.property('name'); + }); + + it('should be \'Bulbasaur\'', () => { + return expect(poke.get('name')) + .to.eventually.equal('Bulbasaur'); + }); + + it('should fail to get nonexistent pokemon', () => { + return expect(p.pokemon('Rick_James')).to.eventually.be.rejected; + }); + + }); + + + describe('game', () => { + let game; + before(() => { + game = p.game(1); + }); + it('should get pokedexs', () => { + return expect(game).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(game).to.eventually.be.an('object'); + }); + + it('should have a release year', () => { + return expect(game) + .to.eventually.have.property('release_year'); + }); + + it('should be 1996', () => { + return expect(game.get('release_year')) + .to.eventually.equal(1996); + }); + + it('should fail to get nonexistent game', () => { + return expect(p.game('ayyy_lmao')).to.eventually.be.rejected; + }); + }); + describe('pokedex', () => { + let pokedex; + before(() => { + pokedex = p.pokedex(1); + }); + it('should get the pokedex', () => { + return expect(pokedex).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(pokedex).to.eventually.be.an('object'); + }); + + it('should have a name property', () => { + return expect(pokedex).to.eventually.have.property('name'); + }); + + it('should be \'national\'', () => { + return expect(pokedex.get('name')) + .to.eventually.equal('national'); + }); + + }); + describe('type', () => { + let type; + before(() => { + type = p.type(1); + }); + it('should get type', () => { + return expect(type).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(type).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(type).to.eventually.have.property('name'); + }); + + it('should be \'Normal\'', () => { + return expect(type.get('name')) + .to.eventually.equal('Normal'); + }); + + it('should fail to get nonexistent type', () => { + return expect(p.type('stupid')).to.eventually.be.rejected; + }); + }); + describe('move', () => { + let move; + before(() => { + move = p.move(1); + }); + it('should get moves', () => { + return expect(move).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(move).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(move).to.eventually.have.property('name'); + }); + + it('should be \'Pound\'', () => { + return expect(move.get('name')) + .to.eventually.equal('Pound'); + }); + + it('should fail to get nonexistent moves', () => { + return expect(p.move('kamehameha')).to.eventually.be.rejected; + }); + }); + describe('ability', () => { + let ability; + before(() => { + ability = p.ability(1); + }); + it('should get abilities', () => { + return expect(ability).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(ability).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(ability).to.eventually.have.property('name'); + }); + + it('should be \'Stench\'', () => { + return expect(ability.get('name')) + .to.eventually.equal('Stench'); + }); + + it('should fail to get nonexistent abilities', () => { + return expect(p.ability('instant_transmission')) + .to.eventually.be.rejected; + }); + }); + describe('egg', () => { + let egg; + before(() => { + egg = p.egg(1); + }); + it('should get eggs', () => { + return expect(egg).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(egg).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(egg).to.eventually.have.property('name'); + }); + + it('should be \'Monster\'', () => { + return expect(egg.get('name')) + .to.eventually.equal('Monster'); + }); + + it('should fail to get nonexistent eggs', () => { + return expect(p.egg('scrambled')).to.eventually.be.rejected; + }); + }); + describe('description', () => { + let description; + before(() => { + description = p.description(2); + }); + it('should get descriptions', () => { + return expect(description).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(description).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(description).to.eventually.have.property('name'); + }); + + it('should be \'Bulbasaur_gen_1\'', () => { + return expect(description.get('name')) + .to.eventually.equal('Bulbasaur_gen_1'); + }); + + it('should fail to get nonexistent descriptions', () => { + return expect(p.description('super_hairy')) + .to.eventually.be.rejected; + }); + }); + describe('sprite', () => { + let sprite; + before(() => { + sprite = p.sprite(1); + }); + it('should get sprites', () => { + return expect(sprite).to.eventually.be.fulfilled; + }); + + it('should be an object', () => { + return expect(sprite).to.eventually.be.an('object'); + }); + + it('should have a name', () => { + return expect(sprite).to.eventually.have.property('name'); + }); + + it('should be \'Bulbasaur_red_blue\'', () => { + return expect(sprite.get('name')) + .to.eventually.equal('Bulbasaur_red_blue'); + }); + + it('should fail to get nonexistent sprites', () => { + return expect(p.sprite('obey_your_thirst')) + .to.eventually.be.rejected; + }); + }); + +}); diff --git a/test/stubs/data.js b/test/stubs/data.js new file mode 100644 index 0000000..c133908 --- /dev/null +++ b/test/stubs/data.js @@ -0,0 +1,37 @@ +let data = { + 'http://pokeapi.co/api/v1/pokemon/1': { + name: 'Bulbasaur' + }, + 'http://pokeapi.co/api/v1/pokemon/bulbasaur': { + name: 'Bulbasaur' + }, + 'http://pokeapi.co/api/v1/description/4': { + pokemon: {name: 'bulbasaur'} + }, + 'http://pokeapi.co/api/v1/game/1': { + release_year: 1996 // eslint-disable-line + }, + 'http://pokeapi.co/api/v1/pokedex/1': { + name: 'national' + }, + 'http://pokeapi.co/api/v1/type/1': { + name: 'Normal' + }, + 'http://pokeapi.co/api/v1/move/1': { + name: 'Pound' + }, + 'http://pokeapi.co/api/v1/ability/1': { + name: 'Stench' + }, + 'http://pokeapi.co/api/v1/egg/1': { + name: 'Monster' + }, + 'http://pokeapi.co/api/v1/description/2': { + name: 'Bulbasaur_gen_1' + }, + 'http://pokeapi.co/api/v1/sprite/1': { + name: 'Bulbasaur_red_blue' + } +}; + +export default data; diff --git a/test/stubs/request.js b/test/stubs/request.js new file mode 100644 index 0000000..ee81880 --- /dev/null +++ b/test/stubs/request.js @@ -0,0 +1,15 @@ +import data from './data'; + +let requestStub = { + get: (query, cb) => { + let body = JSON.stringify(data[query]); + + if (data[query] === undefined) { + return cb(null, {statusCode: 404}, null); + } else { + return cb(null, {statusCode: 200}, body); + } + } +}; + +export default requestStub; diff --git a/test/index.js b/test/unit.js similarity index 98% rename from test/index.js rename to test/unit.js index 27c717d..324c661 100644 --- a/test/index.js +++ b/test/unit.js @@ -1,15 +1,15 @@ import Pkmn from '../lib'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import requestStub from './stubs/request'; chai.use(chaiAsPromised); const expect = chai.expect; -describe('pkmn', function () { +describe('pkmn unit tests', function () { let p; - this.timeout(5000); beforeEach(() => { - p = new Pkmn(); + p = new Pkmn(requestStub); }); describe('constructor', () => { @@ -69,7 +69,7 @@ describe('pkmn', function () { }); describe('resource', () => { it('should get resource by resource_uri', () => { - const uri = '/api/v1/description/4/'; + const uri = '/api/v1/description/4'; return expect(p.resource(uri).get('pokemon').get('name')) .to.eventually.equal('bulbasaur'); });