diff --git a/.jscsrc b/.jscsrc index c7b647d3..4c17aaf9 100644 --- a/.jscsrc +++ b/.jscsrc @@ -15,7 +15,7 @@ "requireBlocksOnNewline": 1, "maximumLineLength": { "value": 100, - "tabSize": 4, + "tabSize": 2, "allowUrlComments": true, "allowRegex": true }, diff --git a/Gruntfile.js b/Gruntfile.js index 57512bde..63933aa1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -31,6 +31,15 @@ module.exports = function( grunt ) { src: files.lib }, tests: { + options: { + maximumLineLength: { + // Longer max line length in test files + value: 150, + tabSize: 2, + allowUrlComments: true, + allowRegex: true + } + }, src: files.tests } }, diff --git a/lib/taxonomies.js b/lib/taxonomies.js index 56133e95..ff86c62c 100644 --- a/lib/taxonomies.js +++ b/lib/taxonomies.js @@ -50,22 +50,29 @@ function TaxonomiesRequest( options ) { /** * A hash of values to assemble into the API request path * + * Default to requesting the taxonomies "collection" (dictionary of publicly- + * registered taxonomies) if no other collection is specified + * * @property _path * @type Object * @private * @default {} */ - this._path = {}; + this._path = { collection: 'taxonomies' }; /** * The URL template that will be used to assemble endpoint paths * + * There is no path validation for taxonomies requests: terms can be numeric + * (categories) or strings (tags), and the list of registered collections is + * not fixed (it can be augmented or modified through plugin and theme behavior). + * * @property _template * @type String * @private - * @default 'taxonomies(/:taxonomy)(/:action)(/:term)' + * @default '(:collection)(/:term)' */ - this._template = 'taxonomies(/:taxonomy)(/:action)(/:term)'; + this._template = '(:collection)(/:term)'; /** * @property _supportedMethods @@ -83,35 +90,24 @@ function TaxonomiesRequest( options ) { inherit( TaxonomiesRequest, CollectionRequest ); /** - * A hash of path keys to regex validators for those path elements + * Specify the name of the taxonomy collection to query * - * @property _pathValidators - * @type Object - * @private - */ -TaxonomiesRequest.prototype._pathValidators = { - - /** - * The only "action" permitted on a taxonomy is to get a list of terms - * - * @property _pathValidators.action - * @type {RegExp} - */ - action: /terms/ - - // No validation on :taxonomy or :term: they can be numeric or a string -}; - -/** - * Specify the name of the taxonomy to query + * The collections will not be a strict match to defined taxonomies: *e.g.*, to + * get the list of terms for the taxonomy "category," you must specify the + * collection name "categories" (similarly, specify "tags" to get a list of terms + * for the "post_tag" taxonomy). * - * @method taxonomy + * To get the dictionary of all available taxonomies, specify the collection + * "taxonomy" (slight misnomer: this case will return an object, not the array + * that would usually be expected with a "collection" request). + * + * @method collection * @chainable - * @param {String} taxonomyName The name of the taxonomy to query + * @param {String} taxonomyCollection The name of the taxonomy collection to query * @return {TaxonomiesRequest} The TaxonomiesRequest instance (for chaining) */ -TaxonomiesRequest.prototype.taxonomy = function( taxonomyName ) { - this._path.taxonomy = taxonomyName; +TaxonomiesRequest.prototype.collection = function( taxonomyCollection ) { + this._path.collection = taxonomyCollection; return this; }; @@ -125,21 +121,29 @@ TaxonomiesRequest.prototype.taxonomy = function( taxonomyName ) { * @return {TaxonomiesRequest} The TaxonomiesRequest instance (for chaining) */ TaxonomiesRequest.prototype.term = function( term ) { - this._path.action = 'terms'; this._path.term = term; return this; }; /** - * Specify that we are requesting a collection of terms for a taxonomy + * Search for hierarchical taxonomy terms that are children of the parent term + * indicated by the provided term ID + * + * @example + * + * wp.categories().parent( 42 ).then(function( categories ) { + * console.log( 'all of these categories are sub-items of cat ID#42:' ); + * console.log( categories ); + * }); * - * @method terms + * @method parent * @chainable + * @param {Number} parentId The ID of a (hierarchical) taxonomy term * @return {TaxonomiesRequest} The TaxonomiesRequest instance (for chaining) */ -TaxonomiesRequest.prototype.terms = function() { - this._path.action = 'terms'; +TaxonomiesRequest.prototype.parent = function( parentId ) { + this.param( 'parent', parentId, true ); return this; }; diff --git a/tests/integration/categories.js b/tests/integration/categories.js new file mode 100644 index 00000000..485d5684 --- /dev/null +++ b/tests/integration/categories.js @@ -0,0 +1,312 @@ +'use strict'; +var chai = require( 'chai' ); +// Variable to use as our "success token" in promise assertions +var SUCCESS = 'success'; +// Chai-as-promised and the `expect( prom ).to.eventually.equal( SUCCESS ) is +// used to ensure that the assertions running within the promise chains are +// actually run. +chai.use( require( 'chai-as-promised' ) ); +var expect = chai.expect; + +var WP = require( '../../' ); +var WPRequest = require( '../../lib/shared/wp-request.js' ); + +// Define some arrays to use ensuring the returned data is what we expect +// it to be (e.g. an array of the names from categories on the first page) +var expectedResults = { + names: { + page1: [ + 'aciform', + 'antiquarianism', + 'arrangement', + 'asmodeus', + 'Blogroll', + 'broder', + 'buying', + 'Cat A', + 'Cat B', + 'Cat C' + ], + page2: [ + 'championship', + 'chastening', + 'Child 1', + 'Child 2', + 'Child Category 01', + 'Child Category 02', + 'Child Category 03', + 'Child Category 04', + 'Child Category 05', + 'clerkship' + ], + pageLast: [ + 'ween', + 'wellhead', + 'wellintentioned', + 'whetstone', + 'years' + ] + } +}; + +// Inspecting the titles of the returned categories arrays is an easy way to +// validate that the right page of results was returned +function getNames( categories ) { + return categories.map(function( category ) { + return category.name; + }); +} + +describe( 'integration: categories()', function() { + var wp; + + beforeEach(function() { + wp = new WP({ + endpoint: 'http://wpapi.loc/wp-json' + }); + }); + + it( 'can be used to retrieve a collection of category terms', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 10 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'retrieves the first 10 categories by default', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 10 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + describe( 'paging properties', function() { + + it( 'are exposed as _paging on the response array', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories ).to.have.property( '_paging' ); + expect( categories._paging ).to.be.an( 'object' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'include the total number of categories', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories._paging ).to.have.property( 'total' ); + expect( categories._paging.total ).to.equal( '65' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'include the total number of pages available', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories._paging ).to.have.property( 'totalPages' ); + expect( categories._paging.totalPages ).to.equal( '7' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'provides a bound WPRequest for the next page as .next', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories._paging ).to.have.property( 'next' ); + expect( categories._paging.next ).to.be.an( 'object' ); + expect( categories._paging.next ).to.be.an.instanceOf( WPRequest ); + expect( categories._paging.next._options.endpoint ).to + .equal( 'http://wpapi.loc/wp-json/wp/v2/categories?page=2' ); + // Get last page & ensure "next" no longer appears + return wp.categories().page( categories._paging.totalPages ).get().then(function( categories ) { + expect( categories._paging ).not.to.have.property( 'next' ); + expect( getNames( categories ) ).to.deep.equal( expectedResults.names.pageLast ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'allows access to the next page of results via .next', function() { + var prom = wp.categories().get().then(function( categories ) { + return categories._paging.next.get().then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 10 ); + expect( getNames( categories ) ).to.deep.equal( expectedResults.names.page2 ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'provides a bound WPRequest for the previous page as .prev', function() { + var prom = wp.categories().get().then(function( categories ) { + expect( categories._paging ).not.to.have.property( 'prev' ); + return categories._paging.next.get().then(function( categories ) { + expect( categories._paging ).to.have.property( 'prev' ); + expect( categories._paging.prev ).to.be.an( 'object' ); + expect( categories._paging.prev ).to.be.an.instanceOf( WPRequest ); + expect( categories._paging.prev._options.endpoint ).to + .equal( 'http://wpapi.loc/wp-json/wp/v2/categories?page=1' ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'allows access to the previous page of results via .prev', function() { + var prom = wp.categories().page( 2 ).get().then(function( categories ) { + expect( getNames( categories ) ).to.deep.equal( expectedResults.names.page2 ); + return categories._paging.prev.get().then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 10 ); + expect( getNames( categories ) ).to.deep.equal( expectedResults.names.page1 ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + + describe( 'term()', function() { + + it( 'can be used to access an individual category term', function() { + var selectedCategory; + var prom = wp.categories().get().then(function( categories ) { + // Pick one of the categories + selectedCategory = categories[ 3 ]; + // Query for that category directly + return wp.categories().term( selectedCategory.id ); + }).then(function( category ) { + expect( category ).to.be.an( 'object' ); + expect( category ).to.have.property( 'id' ); + expect( category.id ).to.equal( selectedCategory.id ); + expect( category ).to.have.property( 'slug' ); + expect( category.slug ).to.equal( selectedCategory.slug ); + expect( category ).to.have.property( 'taxonomy' ); + expect( category.taxonomy ).to.equal( 'category' ); + expect( category ).to.have.property( 'parent' ); + expect( category.parent ).to.equal( 0 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + + describe( 'search()', function() { + + it( 'can be used to retrieve a category by slug', function() { + var selectedCategory; + var prom = wp.categories().get().then(function( categories ) { + // Pick one of the categories + selectedCategory = categories[ 3 ]; + // Search for that category by slug + return wp.categories().search( selectedCategory.slug ); + }).then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 1 ); + return categories[ 0 ]; + }).then(function( category ) { + expect( category ).to.be.an( 'object' ); + expect( category ).to.have.property( 'id' ); + expect( category.id ).to.equal( selectedCategory.id ); + expect( category ).to.have.property( 'slug' ); + expect( category.slug ).to.equal( selectedCategory.slug ); + expect( category ).to.have.property( 'taxonomy' ); + expect( category.taxonomy ).to.equal( 'category' ); + expect( category ).to.have.property( 'parent' ); + expect( category.parent ).to.equal( 0 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'returns all categories matching the provided search string', function() { + var prom = wp.categories().search( 'parent' ).get().then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 4 ); + var slugs = categories.map(function( cat ) { + return cat.slug; + }).sort().join( ' ' ); + expect( slugs ).to.equal( 'foo-a-foo-parent foo-parent parent parent-category' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'can be used to retrieve a category by slug from a set of search results', function() { + var prom = wp.categories().search( 'parent' ).get().then(function( categories ) { + // Iterating over response of search is the best we can do until + // filtering for taxonomy term collections is reinstated + for ( var i = 0; i < 4; i++ ) { + if ( categories[ i ].slug === 'parent' ) { + return categories[ i ]; + } + } + }).then(function( category ) { + expect( category ).to.have.property( 'slug' ); + expect( category.slug ).to.equal( 'parent' ); + expect( category ).to.have.property( 'name' ); + expect( category.name ).to.equal( 'Parent' ); + expect( category ).to.have.property( 'parent' ); + expect( category.parent ).to.equal( 0 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + + describe( 'parent()', function() { + + it( 'can be used to retrieve direct children of a specific category', function() { + var parentCat; + var childCat1; + var childCat2; + // First, find the "parent" category + var prom = wp.categories().search( 'parent' ).get().then(function( categories ) { + for ( var i = 0; i < 4; i++ ) { + if ( categories[ i ].slug === 'parent' ) { + // Return a query for the matching category's child + parentCat = categories[ i ]; + return wp.categories().parent( parentCat.id ); + } + } + }).then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 1 ); + var category = categories[ 0 ]; + expect( category ).to.have.property( 'name' ); + expect( category.name ).to.equal( 'Child 1' ); + expect( category ).to.have.property( 'parent' ); + expect( category.parent ).to.equal( parentCat.id ); + childCat1 = category; + // Go one level deeper + return wp.categories().parent( childCat1.id ); + }).then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 1 ); + var category = categories[ 0 ]; + expect( category ).to.have.property( 'name' ); + expect( category.name ).to.equal( 'Child 2' ); + expect( category ).to.have.property( 'parent' ); + expect( category.parent ).to.equal( childCat1.id ); + childCat2 = category; + // Go one level deeper + return wp.categories().parent( childCat2.id ); + }).then(function( categories ) { + expect( categories ).to.be.an( 'array' ); + expect( categories.length ).to.equal( 0 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + +}); diff --git a/tests/integration/tags.js b/tests/integration/tags.js new file mode 100644 index 00000000..b3c89415 --- /dev/null +++ b/tests/integration/tags.js @@ -0,0 +1,267 @@ +'use strict'; +var chai = require( 'chai' ); +// Variable to use as our "success token" in promise assertions +var SUCCESS = 'success'; +// Chai-as-promised and the `expect( prom ).to.eventually.equal( SUCCESS ) is +// used to ensure that the assertions running within the promise chains are +// actually run. +chai.use( require( 'chai-as-promised' ) ); +var expect = chai.expect; + +var WP = require( '../../' ); +var WPRequest = require( '../../lib/shared/wp-request.js' ); + +// Define some arrays to use ensuring the returned data is what we expect +// it to be (e.g. an array of the names from tags on the first page) +var expectedResults = { + names: { + page1: [ + '8BIT', + 'alignment', + 'Articles', + 'aside', + 'audio', + 'captions', + 'categories', + 'chat', + 'chattels', + 'cienaga' + ], + page2: [ + 'claycold', + 'Codex', + 'comments', + 'content', + 'crushing', + 'css', + 'depo', + 'dinarchy', + 'doolie', + 'dowork' + ], + pageLast: [ + 'trackbacks', + 'twitter', + 'unculpable', + 'Unseen', + 'video', + 'videopress', + 'withered brandnew', + 'WordPress', + 'wordpress.tv', + 'xanthopsia' + ] + } +}; + +// Inspecting the titles of the returned tags arrays is an easy way to +// validate that the right page of results was returned +function getNames( tags ) { + return tags.map(function( category ) { + return category.name; + }); +} + +describe( 'integration: tags()', function() { + var wp; + + beforeEach(function() { + wp = new WP({ + endpoint: 'http://wpapi.loc/wp-json' + }); + }); + + it( 'can be used to retrieve a collection of category terms', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags ).to.be.an( 'array' ); + expect( tags.length ).to.equal( 10 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'retrieves the first 10 tags by default', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags ).to.be.an( 'array' ); + expect( tags.length ).to.equal( 10 ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + describe( 'paging properties', function() { + + it( 'are exposed as _paging on the response array', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags ).to.have.property( '_paging' ); + expect( tags._paging ).to.be.an( 'object' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'include the total number of tags', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags._paging ).to.have.property( 'total' ); + expect( tags._paging.total ).to.equal( '110' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'include the total number of pages available', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags._paging ).to.have.property( 'totalPages' ); + expect( tags._paging.totalPages ).to.equal( '11' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'provides a bound WPRequest for the next page as .next', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags._paging ).to.have.property( 'next' ); + expect( tags._paging.next ).to.be.an( 'object' ); + expect( tags._paging.next ).to.be.an.instanceOf( WPRequest ); + expect( tags._paging.next._options.endpoint ).to + .equal( 'http://wpapi.loc/wp-json/wp/v2/tags?page=2' ); + // Get last page & ensure "next" no longer appears + return wp.tags().page( tags._paging.totalPages ).get().then(function( tags ) { + expect( tags._paging ).not.to.have.property( 'next' ); + expect( getNames( tags ) ).to.deep.equal( expectedResults.names.pageLast ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'allows access to the next page of results via .next', function() { + var prom = wp.tags().get().then(function( tags ) { + return tags._paging.next.get().then(function( tags ) { + expect( tags ).to.be.an( 'array' ); + expect( tags.length ).to.equal( 10 ); + expect( getNames( tags ) ).to.deep.equal( expectedResults.names.page2 ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'provides a bound WPRequest for the previous page as .prev', function() { + var prom = wp.tags().get().then(function( tags ) { + expect( tags._paging ).not.to.have.property( 'prev' ); + return tags._paging.next.get().then(function( tags ) { + expect( tags._paging ).to.have.property( 'prev' ); + expect( tags._paging.prev ).to.be.an( 'object' ); + expect( tags._paging.prev ).to.be.an.instanceOf( WPRequest ); + expect( tags._paging.prev._options.endpoint ).to + .equal( 'http://wpapi.loc/wp-json/wp/v2/tags?page=1' ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'allows access to the previous page of results via .prev', function() { + var prom = wp.tags().page( 2 ).get().then(function( tags ) { + expect( getNames( tags ) ).to.deep.equal( expectedResults.names.page2 ); + return tags._paging.prev.get().then(function( tags ) { + expect( tags ).to.be.an( 'array' ); + expect( tags.length ).to.equal( 10 ); + expect( getNames( tags ) ).to.deep.equal( expectedResults.names.page1 ); + return SUCCESS; + }); + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + + describe( 'term()', function() { + + it( 'can be used to access an individual tag term', function() { + var selectedTag; + var prom = wp.tags().get().then(function( tags ) { + // Pick one of the tags + selectedTag = tags[ 3 ]; + // Query for that tag directly + return wp.tags().term( selectedTag.id ); + }).then(function( tag ) { + expect( tag ).to.be.an( 'object' ); + expect( tag ).to.have.property( 'id' ); + expect( tag.id ).to.equal( selectedTag.id ); + expect( tag ).to.have.property( 'slug' ); + expect( tag.slug ).to.equal( selectedTag.slug ); + expect( tag ).to.have.property( 'taxonomy' ); + expect( tag.taxonomy ).to.equal( 'post_tag' ); + expect( tag ).not.to.have.property( 'parent' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + + describe( 'search()', function() { + + it( 'can be used to retrieve a tag by slug', function() { + var selectedTag; + var prom = wp.tags().get().then(function( tags ) { + // Pick one of the tags + selectedTag = tags[ 3 ]; + // Search for that tag by slug + return wp.tags().search( selectedTag.slug ); + }).then(function( tags ) { + expect( tags ).to.be.an( 'array' ); + expect( tags.length ).to.equal( 1 ); + return tags[ 0 ]; + }).then(function( tag ) { + expect( tag ).to.be.an( 'object' ); + expect( tag ).to.have.property( 'id' ); + expect( tag.id ).to.equal( selectedTag.id ); + expect( tag ).to.have.property( 'slug' ); + expect( tag.slug ).to.equal( selectedTag.slug ); + expect( tag ).to.have.property( 'taxonomy' ); + expect( tag.taxonomy ).to.equal( 'post_tag' ); + expect( tag ).not.to.have.property( 'parent' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'returns all tags matching the provided search string', function() { + var prom = wp.tags().search( 'post' ).get().then(function( tags ) { + expect( tags ).to.be.an( 'array' ); + expect( tags.length ).to.equal( 2 ); + var slugs = tags.map(function( tag ) { + return tag.slug; + }).sort().join( ' ' ); + expect( slugs ).to.equal( 'post post-formats' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'can be used to retrieve a tag by slug from a set of search results', function() { + var prom = wp.tags().search( 'post' ).get().then(function( tags ) { + // Iterating over response of search is the best we can do until + // filtering for taxonomy term collections is reinstated + for ( var i = 0; i < tags.length; i++ ) { + if ( tags[ i ].slug === 'post' ) { + return tags[ i ]; + } + } + }).then(function( tag ) { + expect( tag ).to.have.property( 'slug' ); + expect( tag.slug ).to.equal( 'post' ); + expect( tag ).to.have.property( 'name' ); + expect( tag.name ).to.equal( 'post' ); + expect( tag ).not.to.have.property( 'parent' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + }); + +}); diff --git a/tests/integration/taxonomies.js b/tests/integration/taxonomies.js new file mode 100644 index 00000000..67247405 --- /dev/null +++ b/tests/integration/taxonomies.js @@ -0,0 +1,92 @@ +'use strict'; +var chai = require( 'chai' ); +// Variable to use as our "success token" in promise assertions +var SUCCESS = 'success'; +// Chai-as-promised and the `expect( prom ).to.eventually.equal( SUCCESS ) is +// used to ensure that the assertions running within the promise chains are +// actually run. +chai.use( require( 'chai-as-promised' ) ); +var expect = chai.expect; + +var WP = require( '../../' ); + +describe( 'integration: taxonomies()', function() { + var wp; + + beforeEach(function() { + wp = new WP({ + endpoint: 'http://wpapi.loc/wp-json' + }); + }); + + it( 'can be used to retrieve a dictionary of registered taxonomies', function() { + var prom = wp.taxonomies().get().then(function( taxonomies ) { + expect( taxonomies ).to.be.an( 'object' ); + expect( Object.keys( taxonomies ).length ).to.equal( 2 ); + expect( taxonomies ).to.have.property( 'category' ); + expect( taxonomies ).to.have.property( 'post_tag' ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'can be chained with a term() call to fetch the category taxonomy', function() { + var prom = wp.taxonomies().term( 'category' ).get().then(function( category ) { + expect( category ).to.be.an( 'object' ); + expect( category ).to.have.property( 'slug' ); + expect( category.slug ).to.equal( 'category' ); + expect( category ).to.have.property( 'hierarchical' ); + expect( category.hierarchical ).to.equal( true ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'can be chained with a term() call to fetch the post_tag taxonomy', function() { + var prom = wp.taxonomies().term( 'post_tag' ).get().then(function( tag ) { + expect( tag ).to.be.an( 'object' ); + expect( tag ).to.have.property( 'slug' ); + expect( tag.slug ).to.equal( 'post_tag' ); + expect( tag ).to.have.property( 'hierarchical' ); + expect( tag.hierarchical ).to.equal( false ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + +}); + +describe( 'integration: taxonomy()', function() { + var wp; + + beforeEach(function() { + wp = new WP({ + endpoint: 'http://wpapi.loc/wp-json' + }); + }); + + it( 'can be used to directly retrieve the category taxonomy object', function() { + var prom = wp.taxonomy( 'category' ).get().then(function( category ) { + expect( category ).to.be.an( 'object' ); + expect( category ).to.have.property( 'slug' ); + expect( category.slug ).to.equal( 'category' ); + expect( category ).to.have.property( 'hierarchical' ); + expect( category.hierarchical ).to.equal( true ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + + it( 'can be used to directly retrieve the post_tag taxonomy object', function() { + var prom = wp.taxonomy( 'post_tag' ).get().then(function( tag ) { + expect( tag ).to.be.an( 'object' ); + expect( tag ).to.have.property( 'slug' ); + expect( tag.slug ).to.equal( 'post_tag' ); + expect( tag ).to.have.property( 'hierarchical' ); + expect( tag.hierarchical ).to.equal( false ); + return SUCCESS; + }); + return expect( prom ).to.eventually.equal( SUCCESS ); + }); + +}); diff --git a/tests/unit/lib/taxonomies.js b/tests/unit/lib/taxonomies.js index 657c9283..72e1e7d1 100644 --- a/tests/unit/lib/taxonomies.js +++ b/tests/unit/lib/taxonomies.js @@ -35,9 +35,9 @@ describe( 'wp.taxonomies', function() { it( 'should intitialize instance properties', function() { var _supportedMethods = taxonomies._supportedMethods.sort().join( '|' ); expect( taxonomies._filters ).to.deep.equal( {} ); - expect( taxonomies._path ).to.deep.equal( {} ); + expect( taxonomies._path ).to.deep.equal({ collection: 'taxonomies' }); expect( taxonomies._params ).to.deep.equal( {} ); - expect( taxonomies._template ).to.equal( 'taxonomies(/:taxonomy)(/:action)(/:term)' ); + expect( taxonomies._template ).to.equal( '(:collection)(/:term)' ); expect( _supportedMethods ).to.equal( 'get|head' ); }); @@ -59,17 +59,6 @@ describe( 'wp.taxonomies', function() { }); - describe( '_pathValidators', function() { - - it( 'has a validator for the "action" property', function() { - var taxonomies = new TaxonomiesRequest(); - expect( taxonomies._pathValidators ).to.deep.equal({ - action: /terms/ - }); - }); - - }); - describe( 'URL Generation', function() { var taxonomies; @@ -81,31 +70,19 @@ describe( 'wp.taxonomies', function() { }; }); - it( 'should create the URL for retrieving all taxonomies', function() { - var url = taxonomies._renderURI(); + it( 'should create the URL for retrieving a specific collection', function() { + var url = taxonomies.collection( 'taxonomies' )._renderURI(); expect( url ).to.equal( '/wp-json/wp/v2/taxonomies' ); }); it( 'should create the URL for retrieving a specific taxonomy', function() { - var url = taxonomies.taxonomy( 'my-tax' )._renderURI(); + var url = taxonomies.collection( 'taxonomies' ).term( 'my-tax' )._renderURI(); expect( url ).to.equal( '/wp-json/wp/v2/taxonomies/my-tax' ); }); - it( 'should create the URL for retrieving all terms for a specific taxonomy', function() { - var url = taxonomies.taxonomy( 'my-tax' ).terms()._renderURI(); - expect( url ).to.equal( '/wp-json/wp/v2/taxonomies/my-tax/terms' ); - }); - - it( 'should error if any _path.action other than "terms" is set', function() { - taxonomies._path.action = 'something', - expect(function actionMustBeTerms() { - taxonomies._renderURI(); - }).to.throw(); - }); - - it( 'should create the URL for retrieving a specific taxonomy term', function() { - var url = taxonomies.taxonomy( 'my-tax' ).terms().term( 1337 )._renderURI(); - expect( url ).to.equal( '/wp-json/wp/v2/taxonomies/my-tax/terms/1337' ); + it( 'should create the URL for retrieving taxonomies with a shared parent', function() { + var url = taxonomies.collection( 'categories' ).parent( 42 )._renderURI(); + expect( url ).to.equal( '/wp-json/wp/v2/categories?parent=42' ); }); }); diff --git a/tests/unit/wp.js b/tests/unit/wp.js index 700510dc..465c420d 100644 --- a/tests/unit/wp.js +++ b/tests/unit/wp.js @@ -220,17 +220,17 @@ describe( 'wp', function() { describe( 'taxonomy shortcut handlers', function() { - it( 'defines a .categories() shortcut for the category taxonomy terms', function() { + it( 'defines a .categories() shortcut for the category terms collection', function() { var categories = site.categories(); expect( categories instanceof TaxonomiesRequest ).to.be.true; expect( categories._renderURI() ).to - .equal( 'endpoint/url/wp/v2/taxonomies/category/terms' ); + .equal( 'endpoint/url/wp/v2/categories' ); }); - it( 'defines a .tags() shortcut for the tag taxonomy terms', function() { + it( 'defines a .tags() shortcut for the tag terms collection', function() { var tags = site.tags(); expect( tags instanceof TaxonomiesRequest ).to.be.true; - expect( tags._renderURI() ).to.equal( 'endpoint/url/wp/v2/taxonomies/post_tag/terms' ); + expect( tags._renderURI() ).to.equal( 'endpoint/url/wp/v2/tags' ); }); it( 'defines a generic .taxonomy() handler for arbitrary taxonomy objects', function() { diff --git a/wp.js b/wp.js index 5a47cfcc..9f07b8f2 100644 --- a/wp.js +++ b/wp.js @@ -122,7 +122,7 @@ WP.prototype.posts = function( options ) { }; /** - * Start a request against the `taxonomies` endpoint + * Start a request for a taxonomy or taxonomy term collection * * @method taxonomies * @param {Object} [options] An options hash for a new TaxonomiesRequest @@ -137,21 +137,22 @@ WP.prototype.taxonomies = function( options ) { /** * Start a request for a specific taxonomy object * - * It is repetitive to have to type `.taxonomies().taxonomy()` whenever you want to request - * a taxonomy object or list of terms for a taxonomy. This convenience method lets you - * create a `TaxonomiesRequest` object that is bound to the provided taxonomy name. + * It is slightly unintuitive to consider the name of a taxonomy a "term," as is + * needed in order to retrieve the taxonomy object from the .taxonomies() method. + * This convenience method lets you create a `TaxonomiesRequest` object that is + * bound to the provided taxonomy name, without having to utilize the "term" method. * * @example * If your site uses two custom taxonomies, book_genre and book_publisher, before you would * have had to request these terms using the verbose form: * - * wp.taxonomies().taxonomy( 'book_genre' ).terms()... - * wp.taxonomies().taxonomy( 'book_publisher' )... + * wp.taxonomies().term( 'book_genre' ) + * wp.taxonomies().term( 'book_publisher' ) * * Using `.taxonomy()`, the same query can be achieved much more succinctly: * - * wp.taxonomy( 'book_genre' ).terms()... - * wp.taxonomy( 'book_publisher' )... + * wp.taxonomy( 'book_genre' ) + * wp.taxonomy( 'book_publisher' ) * * @method taxonomy * @param {String} taxonomyName The name of the taxonomy to request @@ -159,7 +160,7 @@ WP.prototype.taxonomies = function( options ) { */ WP.prototype.taxonomy = function( taxonomyName ) { var options = extend( {}, this._options ); - return new TaxonomiesRequest( options ).taxonomy( taxonomyName ); + return new TaxonomiesRequest( options ).term( taxonomyName ); }; /** @@ -170,34 +171,35 @@ WP.prototype.taxonomy = function( taxonomyName ) { * @example * These are equivalent: * - * wp.taxonomies().taxonomy( 'category' ).terms() + * wp.taxonomies().collection( 'categories' ) * wp.categories() * * @method categories - * @return {TaxonomiesRequest} A TaxonomiesRequest object bound to the terms for "category" + * @return {TaxonomiesRequest} A TaxonomiesRequest object bound to the categories collection */ WP.prototype.categories = function() { var options = extend( {}, this._options ); - return new TaxonomiesRequest( options ).taxonomy( 'category' ).terms(); + return new TaxonomiesRequest( options ).collection( 'categories' ); }; /** * Request a list of post_tag terms * - * This is a shortcut method to retrieve the terms for the "post_tag" taxonomy + * This is a shortcut method to interact with the collection of terms for the + * "post_tag" taxonomy. * * @example * These are equivalent: * - * wp.taxonomies().taxonomy( 'post_tag' ).terms() + * wp.taxonomies().collection( 'tags' ) * wp.tags() * * @method tags - * @return {TaxonomiesRequest} A TaxonomiesRequest object bound to the terms for "post_tag" + * @return {TaxonomiesRequest} A TaxonomiesRequest object bound to the tags collection */ WP.prototype.tags = function() { var options = extend( {}, this._options ); - return new TaxonomiesRequest( options ).taxonomy( 'post_tag' ).terms(); + return new TaxonomiesRequest( options ).collection( 'tags' ); }; /**