diff --git a/circle.yml b/circle.yml index 1174d6a..ab84af1 100644 --- a/circle.yml +++ b/circle.yml @@ -7,7 +7,4 @@ dependencies: - npm install -g npm@3.8.6 machine: node: - version: 5.11.1 -test: - override: - - DEBUG=wpcom* make test \ No newline at end of file + version: 5.11.1 \ No newline at end of file diff --git a/lib/util/send-request.js b/lib/util/send-request.js index 85ddcf3..aa70a52 100644 --- a/lib/util/send-request.js +++ b/lib/util/send-request.js @@ -7,6 +7,9 @@ import debugFactory from 'debug'; const debug = debugFactory( 'wpcom:send-request' ); const debug_res = debugFactory( 'wpcom:send-request:res' ); +export const apiVersionPattern = /^(?:(\d+)\.)?(\d+)$/; +export const apiNamespacePattern = /^(?:([a-z]+)\/)?v(\d+)$/; + /** * Request to WordPress REST API * @@ -40,21 +43,33 @@ export default function sendRequest( params, query, body, fn ) { // query could be `null` query = query || {}; - // Handle special query parameters - // - `apiVersion` - if ( query.apiVersion ) { - params.apiVersion = query.apiVersion; - debug( 'apiVersion: %o', params.apiVersion ); - delete query.apiVersion; + const { apiVersion, apiNamespace } = query; + + if ( typeof apiVersion !== 'undefined' && typeof apiNamespace !== 'undefined' ) { + throw new Error( 'apiVersion and apiNamespace cannot be simultaneously defined.' ); + } + + if ( typeof apiVersion === 'undefined' ) { + if ( typeof apiNamespace === 'undefined' ) { + // set apiVersion default value + params.apiVersion = this.apiVersion; + } else { + params.apiNamespace = apiNamespace; + delete query.apiNamespace; + } } else { - params.apiVersion = this.apiVersion; + params.apiVersion = apiVersion; + delete query.apiVersion; + } + + // check apiVersion shape + if ( params.apiVersion && ! apiVersionPattern.test( params.apiVersion ) ) { + throw new Error( `'{ apiVersion: '${ params.apiVersion }' } value is invalid.` ); } - // - `apiNamespace` - if ( query.apiNamespace ) { - params.apiNamespace = query.apiNamespace; - debug( 'apiNamespace: %o', params.apiNamespace ); - delete query.apiNamespace; + // check apiNamespace shape` + if ( params.apiNamespace && ! apiNamespacePattern.test( params.apiNamespace ) ) { + throw new Error( `'{ apiNamespace: '${ params.apiNamespace }' } value is invalid.` ); } // - `proxyOrigin` @@ -91,4 +106,4 @@ export default function sendRequest( params, query, body, fn ) { err ? reject( err ) : resolve( res ); } ); } ); -}; +} diff --git a/package.json b/package.json index 2e34bb1..329735c 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ ], "dependencies": { "babel-runtime": "^6.9.2", + "chai": "^3.5.0", "debug": "^2.2.0", "qs": "^4.0.0", "wpcom-xhr-request": "1.0.0" diff --git a/test/test.util.send-request.js b/test/test.util.send-request.js new file mode 100644 index 0000000..7d11222 --- /dev/null +++ b/test/test.util.send-request.js @@ -0,0 +1,85 @@ +/** + * Module dependencies + */ +import { expect, assert } from 'chai'; + +/** + * Internal dependencies + */ +import wpcomFactory, { + defaultApiVersion +} from '../'; + +import sendRequest, { + apiVersionPattern, + apiNamespacePattern, +} +from '../lib/util/send-request'; + +const wpcom = wpcomFactory(); + +/** + * Testing data + */ +import fixture from './fixture'; + +describe( 'wpcom', () => { + describe( 'wpcom.util.sendRequest', () => { + describe( 'apiVersion shape', () => { + it( 'should not accept `v1` as a valid version value', () => { + expect( apiVersionPattern.test( 'v1' ) ).to.be.false; + } ); + + it( 'should accept `1` as a valid version value', () => { + expect( apiVersionPattern.test( '1' ) ).to.be.true; + } ); + + it( 'should not accept `1.` as a valid version value', () => { + expect( apiVersionPattern.test( '1.' ) ).to.be.false; + } ); + + it( 'should accept `1.3` as a valid version value', () => { + expect( apiVersionPattern.test( '1.3' ) ).to.be.true; + } ); + } ); + + describe( 'apiNamespace shape', () => { + it( 'should not accept `wpcom` as a valid version value', () => { + expect( apiNamespacePattern.test( 'wpcom' ) ).to.be.false; + } ); + + it( 'should accept `wpcom/v1` as a valid version value', () => { + expect( apiNamespacePattern.test( 'wpcom/v1' ) ).to.be.true; + } ); + + it( 'should not accept `wpcom/` as a valid version value', () => { + expect( apiNamespacePattern.test( 'wpcom/' ) ).to.be.false; + } ); + + it( 'should not accept `wpcom/1` as a valid version value', () => { + expect( apiNamespacePattern.test( 'wpcom/1' ) ).to.be.false; + } ); + } ); + + describe( 'parameters parameters', () => { + it( 'should throw Error when `apiVersion` is malformed', () => { + const path = '/endpoint/path'; + const promise = sendRequest.bind( wpcom, path, { apiVersion: 'version1' }, {} ); + assert.throws( promise, Error, '{ apiVersion: \'version1\' } value is invalid.' ); + } ); + + it( 'should throw Error when `apiNamespace` is malformed', () => { + const path = '/endpoint/path'; + const promise = sendRequest.bind( wpcom, path, { apiNamespace: 'wpcom' }, {} ); + assert.throws( promise, Error, '{ apiNamespace: \'wpcom\' } value is invalid.' ); + } ); + + it( 'should throw Error when `apiVersion` and `apiNamespace` are both defined', () => { + const path = '/timezones'; + + const promise = sendRequest.bind( wpcom, path, { apiVersion: 'v1.4', apiNamespace: 'wpcom/v2' }, {} ); + assert.throws( promise, Error, 'apiVersion and apiNamespace cannot be simultaneously defined.' ); + } ); + } ); + } ); +} ); diff --git a/test/test.wpcom.site.follow.js b/test/test.wpcom.site.follow.js deleted file mode 100644 index 59e52ae..0000000 --- a/test/test.wpcom.site.follow.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Module dependencies - */ -var util = require( './util' ); -var assert = require( 'assert' ); - -/** - * site.follow - */ -describe( 'wpcom.site.follow', function() { - // Global instances - var wpcom = util.wpcom(); - var site = wpcom.site( util.site() ); - var follow = site.follow(); - - describe( 'wpcom.site.follow.follow', function() { - it( 'should follow site', done => { - follow.follow() - .then( data => { - assert.ok( data ); - assert.equal( true, data.is_following ); - - done(); - } ) - .catch( done ); - } ); - } ); - - describe( 'wpcom.site.follow.unfollow', function() { - it( 'should unfollow site', done => { - follow.unfollow() - .then( data => { - assert.ok( data ); - assert.equal( false, data.is_following ); - - done(); - } ) - .catch( done ); - } ); - } ); - - describe( 'wpcom.site.follow.mine', function() { - it( 'should get follow status', done => { - follow.mine() - .then( data => { - assert.ok( data ); - assert.equal( false, data.is_following ); - - done(); - } ) - .catch( done ); - } ); - } ); -} ); diff --git a/test/test.wpcom.site.taxonomy.term.js b/test/test.wpcom.site.taxonomy.term.js deleted file mode 100644 index 107284e..0000000 --- a/test/test.wpcom.site.taxonomy.term.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Module dependencies - */ -var util = require( './util' ); -var assert = require( 'assert' ); - -/** - * site.taxonomy.term - */ -describe( 'wpcom.site.taxonomy.term', () => { - // Global instances - const wpcom = util.wpcom(); - const site = wpcom.site( util.site() ); - const taxonomy = site.taxonomy( 'category' ); - const testTermAttributes = { name: 'Chicken and Ribs', description: 'noms' }; - let testTerm; - let testTermTwo; - - // Create a testTerm - before( done => { - taxonomy.term().add( testTermAttributes ) - .then( term => { - testTerm = term; - done(); - } ) - .catch( done ); - } ); - - after( done => { - // delete testTerm - taxonomy.term( testTerm.slug ).delete() - .then( () => done() ) - .catch( done ); - } ); - - describe( 'wpcom.site.taxonomy.term.get', () => { - it( 'should return term details', done => { - taxonomy.term( testTerm.slug ).get() - .then( data => { - assert.ok( data ); - assert.ok( 'string', typeof data.name ); - assert.ok( 'string', typeof data.slug ); - assert.ok( 'string', typeof data.description ); - assert.ok( 'number', typeof data.post_count ); - assert.ok( 'number', typeof data.parent ); - assert.ok( 'number', typeof data.ID ); - assert.equal( testTerm.ID, data.ID ); - assert.equal( testTerm.name, data.name ); - done(); - } ) - .catch( done ); - } ); - - it( 'should not return an error for unknown_taxonomy', done => { - taxonomy.term( 'i-ate-all-the-chicken-and-ribs' ).get() - .catch( data => { - assert.ok( data ); - assert.equal( data.error, 'unknown_taxonomy' ); - done(); - } ); - } ); - } ); - - describe( 'wpcom.site.taxonomy.term.add', () => { - it( 'should create a new term', done => { - taxonomy.term().add( { name: 'chunky bacon', parent: testTerm.ID, description: 'I LOVE BACON MOAR' } ) - .then( data => { - testTermTwo = data; - assert.ok( data ); - assert.equal( data.name, 'chunky bacon' ); - assert.equal( data.parent, testTerm.ID ); - assert.equal( data.description, 'I LOVE BACON MOAR' ); - done(); - } ) - .catch( done ); - } ); - } ); - - describe( 'wpcom.site.taxonomy.term.update', () => { - it( 'should update the term', done => { - taxonomy.term( testTermTwo.slug ).update( { parent: 0, description: 'I LOVE RIBS AND BACON' } ) - .then( data => { - assert.ok( data ); - assert.equal( data.slug, testTermTwo.slug ); - assert.equal( data.description, 'I LOVE RIBS AND BACON' ); - assert.equal( data.parent, 0 ); - done(); - } ) - .catch( done ); - } ); - } ); - - describe( 'wpcom.site.taxonomy.term.delete', () => { - it( 'should update the term', done => { - taxonomy.term( testTermTwo.slug ).delete() - .then( data => { - assert.ok( data ); - done(); - } ) - .catch( done ); - } ); - } ); -} ); diff --git a/test/util.js b/test/util.js index 591b08c..53aae5a 100644 --- a/test/util.js +++ b/test/util.js @@ -8,7 +8,7 @@ import fixture from './fixture'; var configFactory; try { - configFactory = require( './config' ); + configFactory = require( './config' ).default; } catch ( ex ) { configFactory = {}; } @@ -27,17 +27,13 @@ const env = isClientSide && ( ? 'production' : 'development'; -console.log( 'environment: %j', env ); - const config = configFactory[ env ] || {}; if ( isClientSide ) { const clientId = config.oauth.client_id; - console.log( 'clientId: %o', clientId ); qryString = qs.parse( document.location.search.replace( /^\?/, '' ) ); reqHandler = qryString.handler || 'wpcom-proxy-request'; - console.log( `reqHandler: %o`, reqHandler ); if ( 'wpcom-xhr-request' === reqHandler || @@ -73,7 +69,6 @@ function wpcom() { reqHandler = qryString.handler || 'wpcom-proxy-request'; if ( 'wpcom-proxy-request' === reqHandler ) { - console.log( 'PROXY request handler' ); let proxy = require( 'wpcom-proxy-request' ); _wpcom = wpcomFactory( proxy ); _wpcom.request( {