From 046548a84c7640a5336f58e07a8ad975ba5ae316 Mon Sep 17 00:00:00 2001 From: Steve Blaurock Date: Mon, 21 Oct 2019 17:07:26 -0600 Subject: [PATCH 1/5] Introduce browse module and define first test. --- spec/src/modules/browse.js | 334 +++++++++++++++++++++++++++++++++++++ src/constructorio.js | 3 + src/modules/browse.js | 156 +++++++++++++++++ src/modules/search.js | 126 +------------- src/modules/tracker.js | 2 +- 5 files changed, 495 insertions(+), 126 deletions(-) create mode 100644 spec/src/modules/browse.js create mode 100644 src/modules/browse.js diff --git a/spec/src/modules/browse.js b/spec/src/modules/browse.js new file mode 100644 index 00000000..a6c44b5f --- /dev/null +++ b/spec/src/modules/browse.js @@ -0,0 +1,334 @@ +/* eslint-disable no-unused-expressions */ +const jsdom = require('mocha-jsdom'); +const dotenv = require('dotenv'); +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const fetchPonyfill = require('fetch-ponyfill'); +const Promise = require('es6-promise'); +const ConstructorIO = require('../../../src/constructorio'); +const helpers = require('../../mocha.helpers'); + +chai.use(chaiAsPromised); +chai.use(sinonChai); +dotenv.config(); + +const testApiKey = process.env.TEST_API_KEY; +const { fetch } = fetchPonyfill({ Promise }); + +describe('ConstructorIO - Browse', () => { + const clientVersion = 'cio-mocha'; + let fetchSpy; + + jsdom({ url: 'http://localhost' }); + + beforeEach(() => { + global.CLIENT_VERSION = clientVersion; + fetchSpy = sinon.spy(fetch); + }); + + afterEach(() => { + delete global.CLIENT_VERSION; + + fetchSpy = null; + }); + + describe.only('getBrowseResults', () => { + const filterName = 'group_id'; + const filterValue = 'drill_collection'; + + it('Should return a response with a valid filterName and filterValue', (done) => { + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request).to.have.property('searchandizing_filter'); + expect(res.request.searchandizing_filter).to.have.property(filterName); + expect(res.request.searchandizing_filter[filterName]).to.equal(filterValue); + expect(res.response).to.have.property('results').to.be.an('array'); + expect(fetchSpy).to.have.been.called; + expect(requestedUrlParams).to.have.property('key'); + expect(requestedUrlParams).to.have.property('i'); + expect(requestedUrlParams).to.have.property('s'); + expect(requestedUrlParams).to.have.property('c').to.equal(clientVersion); + expect(requestedUrlParams).to.have.property('_dt'); + done(); + }); + }); + + //it('Should return a response with a valid group_id, section and testCells', (done) => { + //const testCells = { foo: 'bar' }; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //testCells, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); + //expect(requestedUrlParams).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section and segments', (done) => { + //const segments = ['foo', 'bar']; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //segments, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request.us).to.deep.equal(segments); + //expect(requestedUrlParams).to.have.property('us').to.deep.equal(segments); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section and user id', (done) => { + //const userId = 'user-id'; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //userId, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(requestedUrlParams).to.have.property('ui').to.equal(userId); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section, and page', (done) => { + //const page = 1; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters, + //page, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request.page).to.equal(page); + //expect(requestedUrlParams).to.have.property('page').to.equal(page.toString()); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section, and resultsPerPage', (done) => { + //const resultsPerPage = 2; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //resultsPerPage, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request.num_results_per_page).to.equal(resultsPerPage); + //expect(res.response).to.have.property('results').to.be.an('array'); + //expect(requestedUrlParams).to.have.property('num_results_per_page').to.equal(resultsPerPage.toString()); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section, and additional filters', (done) => { + //const additionalFilters = { keywords: ['battery-powered'] }; + //const combinedFilters = Object.assign({}, additionalFilters, filters); + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters: combinedFilters, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request.filters).to.deep.equal(combinedFilters); + //expect(requestedUrlParams).to.have.property('filters'); + //expect(requestedUrlParams.filters).to.have.property('keywords').to.equal(Object.values(additionalFilters)[0][0]); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section, and sortBy', (done) => { + //const sortBy = 'relevance'; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //sortBy, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request.sort_by).to.deep.equal(sortBy); + //expect(requestedUrlParams).to.have.property('sort_by').to.equal(sortBy); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section, and sortOrder', (done) => { + //const sortOrder = 'ascending'; + //const { search } = new ConstructorIO({ + //apiKey: testApiKey, + //fetch: fetchSpy, + //}); + + //search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //sortOrder, + //}).then((res) => { + //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.request.sort_order).to.deep.equal(sortOrder); + //expect(requestedUrlParams).to.have.property('sort_order').to.equal(sortOrder); + //done(); + //}); + //}); + + //it('Should return a response with a valid group_id, section with a result_id appended to each result', (done) => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //}).then((res) => { + //expect(res).to.have.property('request').to.be.an('object'); + //expect(res).to.have.property('response').to.be.an('object'); + //expect(res).to.have.property('result_id').to.be.an('string'); + //expect(res.response).to.have.property('results').to.be.an('array'); + //res.response.results.forEach((result) => { + //expect(result).to.have.property('result_id').to.be.a('string').to.equal(res.result_id); + //}); + //done(); + //}); + //}); + + //it('Should be rejected when invalid page parameter is provided', () => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //return expect(search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //page: 'abc', + //})).to.eventually.be.rejected; + //}); + + //it('Should be rejected when invalid page parameter is provided', () => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //return expect(search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //resultsPerPage: 'abc', + //})).to.eventually.be.rejected; + //}); + + //it('Should be rejected when invalid filters parameter is provided', () => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //return expect(search.getBrowseResults({ + //section, + //filters: 'abc', + //})).to.eventually.be.rejected; + //}); + + //it('Should be rejected when invalid sortBy parameter is provided', () => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //return expect(search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //sortBy: { foo: 'bar' }, + //})).to.eventually.be.rejected; + //}); + + //it('Should be rejected when invalid sortOrder parameter is provided', () => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //return expect(search.getBrowseResults({ + //section, + //filters: { group_id: groupId }, + //sortOrder: 'abc', + //})).to.eventually.be.rejected; + //}); + + //it('Should be rejected when invalid section parameter is provided', () => { + //const { search } = new ConstructorIO({ apiKey: testApiKey }); + + //return expect(search.getBrowseResults({ + //section: 123, + //filters: { group_id: groupId }, + //})).to.eventually.be.rejected; + //}); + + //it('Should be rejected when invalid apiKey is provided', () => { + //const { search } = new ConstructorIO({ apiKey: 'fyzs7tfF8L161VoAXQ8u' }); + + //return expect(search.getBrowseResults(groupId, { section })).to.eventually.be.rejected; + //}); + }); +}); diff --git a/src/constructorio.js b/src/constructorio.js index 77bc0df5..47e940bc 100644 --- a/src/constructorio.js +++ b/src/constructorio.js @@ -3,6 +3,7 @@ const ConstructorioID = require('@constructor-io/constructorio-id'); // Modules const Search = require('./modules/search'); +const Browse = require('./modules/browse'); const Autocomplete = require('./modules/autocomplete'); const Recommendations = require('./modules/recommendations'); const Tracker = require('./modules/tracker'); @@ -21,6 +22,7 @@ class ConstructorIO { * @param {string} [sessionId] - Session id, defaults to value supplied by 'constructorio-id' * @param {string} [userId] - User id * @property {object} [search] - Interface to {@link module:search} + * @property {object} [search] - Interface to {@link module:browse} * @property {object} [autocomplete] - Interface to {@link module:autocomplete} * @property {object} [recommendations] - Interface to {@link module:recommendations} * @property {object} [tracker] - Interface to {@link module:tracker} @@ -59,6 +61,7 @@ class ConstructorIO { // Expose global modules this.search = new Search(this.options); + this.browse = new Browse(this.options); this.autocomplete = new Autocomplete(this.options); this.recommendations = new Recommendations(this.options); this.tracker = new Tracker(this.options); diff --git a/src/modules/browse.js b/src/modules/browse.js new file mode 100644 index 00000000..00ce1a4c --- /dev/null +++ b/src/modules/browse.js @@ -0,0 +1,156 @@ +/* eslint-disable object-curly-newline, no-underscore-dangle */ +const qs = require('qs'); +const fetchPonyfill = require('fetch-ponyfill'); +const Promise = require('es6-promise'); +const { throwHttpErrorFromResponse, cleanParams } = require('../utils/helpers'); + +// Create URL from supplied filter name, value and parameters +function createBrowseUrl(filterName, filterValue, parameters, options) { + const { + apiKey, + version, + serviceUrl, + sessionId, + clientId, + userId, + segments, + testCells, + } = options; + let queryParams = { c: version }; + + queryParams.key = apiKey; + queryParams.i = clientId; + queryParams.s = sessionId; + + // Validate filter name is provided + if (!filterName || typeof filterName !== 'string') { + throw new Error('filterName is a required parameter of type string'); + } + + // Validate filter value is provided + if (!filterValue || typeof filterValue !== 'string') { + throw new Error('filterValue is a required parameter of type string'); + } + + // Pull test cells from options + if (testCells) { + Object.keys(testCells).forEach((testCellKey) => { + queryParams[`ef-${testCellKey}`] = testCells[testCellKey]; + }); + } + + // Pull user segments from options + if (segments && segments.length) { + queryParams.us = segments; + } + + // Pull user id from options + if (userId) { + queryParams.ui = userId; + } + + if (parameters) { + const { page, resultsPerPage, filters, sortBy, sortOrder, section } = parameters; + + // Pull page from parameters + if (page) { + queryParams.page = page; + } + + // Pull results per page from parameters + if (resultsPerPage) { + queryParams.num_results_per_page = resultsPerPage; + } + + if (filters) { + queryParams.filters = filters; + } + + // Pull sort by from parameters + if (sortBy) { + queryParams.sort_by = sortBy; + } + + // Pull sort order from parameters + if (sortOrder) { + queryParams.sort_order = sortOrder; + } + + // Pull section from parameters + if (section) { + queryParams.section = section; + } + } + + queryParams._dt = Date.now(); + queryParams = cleanParams(queryParams); + + const queryString = qs.stringify(queryParams, { indices: false }); + + return `${serviceUrl}/browse/${filterName}/${filterValue}?${queryString}`; +} + +/** + * Interface to browse related API calls + * + * @module browse + * @inner + * @returns {object} + */ +class Browse { + constructor(options) { + this.options = options; + } + + /** + * Retrieve browse results from API + * + * @function getBrowseResults + * @param {string} filterName - Filter name to display results from + * @param {string} filterValue - Filter value to display results from + * @param {object} [parameters] - Additional parameters to refine result set + * @param {number} [parameters.page] - The page number of the results + * @param {number} [parameters.resultsPerPage] - The number of results per page to return + * @param {object} [parameters.filters] - Filters used to refine + * @param {string} [parameters.sortBy='relevance'] - The sorting method + * @param {string} [parameters.sortOrder='descending'] - The sort order for search results + * @returns {Promise} + * @see https://docs.constructor.io + */ + getBrowseResults(filterName, filterValue, parameters) { + let requestUrl; + const fetch = (this.options && this.options.fetch) || fetchPonyfill({ Promise }).fetch; + + try { + requestUrl = createBrowseUrl(filterName, filterValue, parameters, this.options); + } catch (e) { + return Promise.reject(e); + } + + return fetch(requestUrl) + .then((response) => { + if (response.ok) { + return response.json(); + } + + return throwHttpErrorFromResponse(new Error(), response); + }) + .then((json) => { + if (json.response && json.response.results) { + if (json.result_id) { + // Append `result_id` to each result item + json.response.results.forEach((result) => { + // eslint-disable-next-line no-param-reassign + result.result_id = json.result_id; + }); + } + + return json; + } + + throw new Error('getBrowseResults response data is malformed'); + }); + } +} + +module.exports = Browse; diff --git a/src/modules/search.js b/src/modules/search.js index 5bc01c74..d0f702bc 100644 --- a/src/modules/search.js +++ b/src/modules/search.js @@ -86,84 +86,8 @@ function createSearchUrl(query, parameters, options) { return `${serviceUrl}/search/${encodeURIComponent(query)}?${queryString}`; } -// Create URL from supplied group ID and parameters -function createBrowseUrl(parameters, options) { - const { - apiKey, - version, - serviceUrl, - sessionId, - clientId, - userId, - segments, - testCells, - } = options; - let queryParams = { c: version }; - - queryParams.key = apiKey; - queryParams.i = clientId; - queryParams.s = sessionId; - - // Pull test cells from options - if (testCells) { - Object.keys(testCells).forEach((testCellKey) => { - queryParams[`ef-${testCellKey}`] = testCells[testCellKey]; - }); - } - - // Pull user segments from options - if (segments && segments.length) { - queryParams.us = segments; - } - - // Pull user id from options - if (userId) { - queryParams.ui = userId; - } - - if (parameters) { - const { page, resultsPerPage, filters, sortBy, sortOrder, section } = parameters; - - // Pull page from parameters - if (page) { - queryParams.page = page; - } - - // Pull results per page from parameters - if (resultsPerPage) { - queryParams.num_results_per_page = resultsPerPage; - } - - if (filters) { - queryParams.filters = filters; - } - - // Pull sort by from parameters - if (sortBy) { - queryParams.sort_by = sortBy; - } - - // Pull sort order from parameters - if (sortOrder) { - queryParams.sort_order = sortOrder; - } - - // Pull section from parameters - if (section) { - queryParams.section = section; - } - } - - queryParams._dt = Date.now(); - queryParams = cleanParams(queryParams); - - const queryString = qs.stringify(queryParams, { indices: false }); - - return `${serviceUrl}/search/?${queryString}`; -} - /** - * Interface to search related API calls. + * Interface to search related API calls * * @module search * @inner @@ -227,54 +151,6 @@ class Search { throw new Error('getSearchResults response data is malformed'); }); } - - /** - * Retrieve browse results from API - * - * @function getBrowseResults - * @param {object} [parameters] - Additional parameters to refine result set - * @param {number} [parameters.page] - The page number of the results - * @param {number} [parameters.resultsPerPage] - The number of results per page to return - * @param {object} [parameters.filters] - Filters used to refine search - * @param {string} [parameters.sortBy='relevance'] - The sorting method - * @param {string} [parameters.sortOrder='descending'] - The sort order for search results - * @returns {Promise} - * @see https://docs.constructor.io - */ - getBrowseResults(parameters) { - let requestUrl; - const fetch = (this.options && this.options.fetch) || fetchPonyfill({ Promise }).fetch; - - try { - requestUrl = createBrowseUrl(parameters, this.options); - } catch (e) { - return Promise.reject(e); - } - - return fetch(requestUrl) - .then((response) => { - if (response.ok) { - return response.json(); - } - - return throwHttpErrorFromResponse(new Error(), response); - }) - .then((json) => { - if (json.response && json.response.results) { - if (json.result_id) { - // Append `result_id` to each result item - json.response.results.forEach((result) => { - // eslint-disable-next-line no-param-reassign - result.result_id = json.result_id; - }); - } - - return json; - } - - throw new Error('getBrowseResults response data is malformed'); - }); - } } module.exports = Search; diff --git a/src/modules/tracker.js b/src/modules/tracker.js index d2d401a3..2bfcff55 100644 --- a/src/modules/tracker.js +++ b/src/modules/tracker.js @@ -39,7 +39,7 @@ function createQueryString(parameters, options) { } /** - * Interface to tracking related API calls. + * Interface to tracking related API calls * * @module Tracker * @inner From 91b681cdd77eaf01271ad16417bde2b62ad55d30 Mon Sep 17 00:00:00 2001 From: Steve Blaurock Date: Mon, 21 Oct 2019 17:31:18 -0600 Subject: [PATCH 2/5] Add exhaustive tests for browse module. --- spec/src/modules/browse.js | 508 ++++++++++++++++++------------------- 1 file changed, 242 insertions(+), 266 deletions(-) diff --git a/spec/src/modules/browse.js b/spec/src/modules/browse.js index a6c44b5f..e2770762 100644 --- a/spec/src/modules/browse.js +++ b/spec/src/modules/browse.js @@ -64,271 +64,247 @@ describe('ConstructorIO - Browse', () => { }); }); - //it('Should return a response with a valid group_id, section and testCells', (done) => { - //const testCells = { foo: 'bar' }; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //testCells, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); - //expect(requestedUrlParams).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section and segments', (done) => { - //const segments = ['foo', 'bar']; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //segments, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request.us).to.deep.equal(segments); - //expect(requestedUrlParams).to.have.property('us').to.deep.equal(segments); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section and user id', (done) => { - //const userId = 'user-id'; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //userId, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(requestedUrlParams).to.have.property('ui').to.equal(userId); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section, and page', (done) => { - //const page = 1; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters, - //page, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request.page).to.equal(page); - //expect(requestedUrlParams).to.have.property('page').to.equal(page.toString()); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section, and resultsPerPage', (done) => { - //const resultsPerPage = 2; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //resultsPerPage, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request.num_results_per_page).to.equal(resultsPerPage); - //expect(res.response).to.have.property('results').to.be.an('array'); - //expect(requestedUrlParams).to.have.property('num_results_per_page').to.equal(resultsPerPage.toString()); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section, and additional filters', (done) => { - //const additionalFilters = { keywords: ['battery-powered'] }; - //const combinedFilters = Object.assign({}, additionalFilters, filters); - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters: combinedFilters, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request.filters).to.deep.equal(combinedFilters); - //expect(requestedUrlParams).to.have.property('filters'); - //expect(requestedUrlParams.filters).to.have.property('keywords').to.equal(Object.values(additionalFilters)[0][0]); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section, and sortBy', (done) => { - //const sortBy = 'relevance'; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //sortBy, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request.sort_by).to.deep.equal(sortBy); - //expect(requestedUrlParams).to.have.property('sort_by').to.equal(sortBy); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section, and sortOrder', (done) => { - //const sortOrder = 'ascending'; - //const { search } = new ConstructorIO({ - //apiKey: testApiKey, - //fetch: fetchSpy, - //}); - - //search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //sortOrder, - //}).then((res) => { - //const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.request.sort_order).to.deep.equal(sortOrder); - //expect(requestedUrlParams).to.have.property('sort_order').to.equal(sortOrder); - //done(); - //}); - //}); - - //it('Should return a response with a valid group_id, section with a result_id appended to each result', (done) => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //}).then((res) => { - //expect(res).to.have.property('request').to.be.an('object'); - //expect(res).to.have.property('response').to.be.an('object'); - //expect(res).to.have.property('result_id').to.be.an('string'); - //expect(res.response).to.have.property('results').to.be.an('array'); - //res.response.results.forEach((result) => { - //expect(result).to.have.property('result_id').to.be.a('string').to.equal(res.result_id); - //}); - //done(); - //}); - //}); - - //it('Should be rejected when invalid page parameter is provided', () => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //return expect(search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //page: 'abc', - //})).to.eventually.be.rejected; - //}); - - //it('Should be rejected when invalid page parameter is provided', () => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //return expect(search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //resultsPerPage: 'abc', - //})).to.eventually.be.rejected; - //}); - - //it('Should be rejected when invalid filters parameter is provided', () => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //return expect(search.getBrowseResults({ - //section, - //filters: 'abc', - //})).to.eventually.be.rejected; - //}); - - //it('Should be rejected when invalid sortBy parameter is provided', () => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //return expect(search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //sortBy: { foo: 'bar' }, - //})).to.eventually.be.rejected; - //}); - - //it('Should be rejected when invalid sortOrder parameter is provided', () => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //return expect(search.getBrowseResults({ - //section, - //filters: { group_id: groupId }, - //sortOrder: 'abc', - //})).to.eventually.be.rejected; - //}); - - //it('Should be rejected when invalid section parameter is provided', () => { - //const { search } = new ConstructorIO({ apiKey: testApiKey }); - - //return expect(search.getBrowseResults({ - //section: 123, - //filters: { group_id: groupId }, - //})).to.eventually.be.rejected; - //}); - - //it('Should be rejected when invalid apiKey is provided', () => { - //const { search } = new ConstructorIO({ apiKey: 'fyzs7tfF8L161VoAXQ8u' }); - - //return expect(search.getBrowseResults(groupId, { section })).to.eventually.be.rejected; - //}); + it('Should return a response with a valid filterName, filterValue and testCells', (done) => { + const testCells = { foo: 'bar' }; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + testCells, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); + expect(requestedUrlParams).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and segments', (done) => { + const segments = ['foo', 'bar']; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + segments, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(requestedUrlParams).to.have.property('us').to.deep.equal(segments); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and user id', (done) => { + const userId = 'user-id'; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + userId, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(requestedUrlParams).to.have.property('ui').to.equal(userId); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and page', (done) => { + const page = 1; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue, { page }).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request.page).to.equal(page); + expect(requestedUrlParams).to.have.property('page').to.equal(page.toString()); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and resultsPerPage', (done) => { + const resultsPerPage = 2; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue, { resultsPerPage }).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request.num_results_per_page).to.equal(resultsPerPage); + expect(res.response).to.have.property('results').to.be.an('array'); + expect(requestedUrlParams).to.have.property('num_results_per_page').to.equal(resultsPerPage.toString()); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and additional filters', (done) => { + const filters = { keywords: ['battery-powered'] }; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue, { filters }).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request.filters).to.deep.equal(filters); + expect(requestedUrlParams).to.have.property('filters'); + expect(requestedUrlParams.filters).to.have.property('keywords').to.equal(Object.values(filters)[0][0]); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and sortBy', (done) => { + const sortBy = 'relevance'; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue, { sortBy }).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request.sort_by).to.equal(sortBy); + expect(requestedUrlParams).to.have.property('sort_by').to.equal(sortBy); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and sortOrder', (done) => { + const sortOrder = 'ascending'; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue, { sortOrder }).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request.sort_order).to.equal(sortOrder); + expect(requestedUrlParams).to.have.property('sort_order').to.equal(sortOrder); + done(); + }); + }); + + it('Should return a response with a valid filterName, filterValue and section', (done) => { + const section = 'Products'; + const { browse } = new ConstructorIO({ + apiKey: testApiKey, + fetch: fetchSpy, + }); + + browse.getBrowseResults(filterName, filterValue, { section }).then((res) => { + const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); + + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.request.section).to.equal(section); + expect(requestedUrlParams).to.have.property('section').to.equal(section); + done(); + }); + }); + + it('Should return a response with a valid filterName and filterValue with a result_id appended to each result', (done) => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + browse.getBrowseResults(filterName, filterValue).then((res) => { + expect(res).to.have.property('request').to.be.an('object'); + expect(res).to.have.property('response').to.be.an('object'); + expect(res).to.have.property('result_id').to.be.an('string'); + expect(res.response).to.have.property('results').to.be.an('array'); + res.response.results.forEach((result) => { + expect(result).to.have.property('result_id').to.be.a('string').to.equal(res.result_id); + }); + done(); + }); + }); + + it('Should be rejected when invalid page parameter is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, filterValue, { + page: 'abc', + })).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid resultsPerPage parameter is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, filterValue, { + resultsPerPage: 'abc', + })).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid filters parameter is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, filterValue, { + filters: 123, + })).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid sortBy parameter is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, filterValue, { + sortBy: { foo: 'bar' }, + })).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid sortOrder parameter is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, filterValue, { + sortOrder: 'abc', + })).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid section parameter is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, filterValue, { + section: 123, + })).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid apiKey is provided', () => { + const { browse } = new ConstructorIO({ apiKey: 'fyzs7tfF8L161VoAXQ8u' }); + + return expect(browse.getBrowseResults(filterName, filterValue)).to.eventually.be.rejected; + }); }); }); From e729821257c9261da12df6a6bdb9168ef5b26265 Mon Sep 17 00:00:00 2001 From: Steve Blaurock Date: Mon, 21 Oct 2019 17:38:20 -0600 Subject: [PATCH 3/5] Add tests against required browse parameters. --- spec/src/modules/browse.js | 26 +++- spec/src/modules/search.js | 305 ------------------------------------- 2 files changed, 25 insertions(+), 306 deletions(-) diff --git a/spec/src/modules/browse.js b/spec/src/modules/browse.js index e2770762..06e8227c 100644 --- a/spec/src/modules/browse.js +++ b/spec/src/modules/browse.js @@ -34,7 +34,7 @@ describe('ConstructorIO - Browse', () => { fetchSpy = null; }); - describe.only('getBrowseResults', () => { + describe('getBrowseResults', () => { const filterName = 'group_id'; const filterValue = 'drill_collection'; @@ -253,6 +253,30 @@ describe('ConstructorIO - Browse', () => { }); }); + it('Should be rejected when invalid filterName is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults([], filterValue)).to.eventually.be.rejected; + }); + + it('Should be rejected when no filterName is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(null, filterValue)).to.eventually.be.rejected; + }); + + it('Should be rejected when invalid filterValue is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, [])).to.eventually.be.rejected; + }); + + it('Should be rejected when no filterValue is provided', () => { + const { browse } = new ConstructorIO({ apiKey: testApiKey }); + + return expect(browse.getBrowseResults(filterName, null)).to.eventually.be.rejected; + }); + it('Should be rejected when invalid page parameter is provided', () => { const { browse } = new ConstructorIO({ apiKey: testApiKey }); diff --git a/spec/src/modules/search.js b/spec/src/modules/search.js index 51a30c51..cfdcbf7f 100644 --- a/spec/src/modules/search.js +++ b/spec/src/modules/search.js @@ -341,309 +341,4 @@ describe('ConstructorIO - Search', () => { return expect(search.getSearchResults(query, { section })).to.eventually.be.rejected; }); }); - - describe('getBrowseResults', () => { - const section = 'Products'; - const groupId = 'drill_collection'; - const filters = { group_id: [groupId] }; - - it('Should return a response with a valid group_id, and section', (done) => { - const { search } = new ConstructorIO({ - apiKey: testApiKey, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request).to.have.property('filters'); - expect(res.request.filters).to.have.property('group_id').to.be.an('array'); - expect(res.request.filters.group_id).to.include(groupId); - expect(res.request.section).to.equal(section); - expect(res.response).to.have.property('results').to.be.an('array'); - expect(fetchSpy).to.have.been.called; - expect(requestedUrlParams).to.have.property('key'); - expect(requestedUrlParams).to.have.property('i'); - expect(requestedUrlParams).to.have.property('s'); - expect(requestedUrlParams).to.have.property('section').to.equal(section); - expect(requestedUrlParams).to.have.property('filters'); - expect(requestedUrlParams).to.have.property('c').to.equal(clientVersion); - expect(requestedUrlParams).to.have.property('_dt'); - done(); - }); - }); - - it('Should return a response with a valid group_id, section and testCells', (done) => { - const testCells = { foo: 'bar' }; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - testCells, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); - expect(requestedUrlParams).to.have.property(`ef-${Object.keys(testCells)[0]}`).to.equal(Object.values(testCells)[0]); - done(); - }); - }); - - it('Should return a response with a valid group_id, section and segments', (done) => { - const segments = ['foo', 'bar']; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - segments, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request.us).to.deep.equal(segments); - expect(requestedUrlParams).to.have.property('us').to.deep.equal(segments); - done(); - }); - }); - - it('Should return a response with a valid group_id, section and user id', (done) => { - const userId = 'user-id'; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - userId, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(requestedUrlParams).to.have.property('ui').to.equal(userId); - done(); - }); - }); - - it('Should return a response with a valid group_id, section, and page', (done) => { - const page = 1; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters, - page, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request.page).to.equal(page); - expect(requestedUrlParams).to.have.property('page').to.equal(page.toString()); - done(); - }); - }); - - it('Should return a response with a valid group_id, section, and resultsPerPage', (done) => { - const resultsPerPage = 2; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters: { group_id: groupId }, - resultsPerPage, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request.num_results_per_page).to.equal(resultsPerPage); - expect(res.response).to.have.property('results').to.be.an('array'); - expect(requestedUrlParams).to.have.property('num_results_per_page').to.equal(resultsPerPage.toString()); - done(); - }); - }); - - it('Should return a response with a valid group_id, section, and additional filters', (done) => { - const additionalFilters = { keywords: ['battery-powered'] }; - const combinedFilters = Object.assign({}, additionalFilters, filters); - const { search } = new ConstructorIO({ - apiKey: testApiKey, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters: combinedFilters, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request.filters).to.deep.equal(combinedFilters); - expect(requestedUrlParams).to.have.property('filters'); - expect(requestedUrlParams.filters).to.have.property('keywords').to.equal(Object.values(additionalFilters)[0][0]); - done(); - }); - }); - - it('Should return a response with a valid group_id, section, and sortBy', (done) => { - const sortBy = 'relevance'; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters: { group_id: groupId }, - sortBy, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request.sort_by).to.deep.equal(sortBy); - expect(requestedUrlParams).to.have.property('sort_by').to.equal(sortBy); - done(); - }); - }); - - it('Should return a response with a valid group_id, section, and sortOrder', (done) => { - const sortOrder = 'ascending'; - const { search } = new ConstructorIO({ - apiKey: testApiKey, - fetch: fetchSpy, - }); - - search.getBrowseResults({ - section, - filters: { group_id: groupId }, - sortOrder, - }).then((res) => { - const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy); - - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.request.sort_order).to.deep.equal(sortOrder); - expect(requestedUrlParams).to.have.property('sort_order').to.equal(sortOrder); - done(); - }); - }); - - it('Should return a response with a valid group_id, section with a result_id appended to each result', (done) => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - search.getBrowseResults({ - section, - filters: { group_id: groupId }, - }).then((res) => { - expect(res).to.have.property('request').to.be.an('object'); - expect(res).to.have.property('response').to.be.an('object'); - expect(res).to.have.property('result_id').to.be.an('string'); - expect(res.response).to.have.property('results').to.be.an('array'); - res.response.results.forEach((result) => { - expect(result).to.have.property('result_id').to.be.a('string').to.equal(res.result_id); - }); - done(); - }); - }); - - it('Should be rejected when invalid page parameter is provided', () => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - return expect(search.getBrowseResults({ - section, - filters: { group_id: groupId }, - page: 'abc', - })).to.eventually.be.rejected; - }); - - it('Should be rejected when invalid page parameter is provided', () => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - return expect(search.getBrowseResults({ - section, - filters: { group_id: groupId }, - resultsPerPage: 'abc', - })).to.eventually.be.rejected; - }); - - it('Should be rejected when invalid filters parameter is provided', () => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - return expect(search.getBrowseResults({ - section, - filters: 'abc', - })).to.eventually.be.rejected; - }); - - it('Should be rejected when invalid sortBy parameter is provided', () => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - return expect(search.getBrowseResults({ - section, - filters: { group_id: groupId }, - sortBy: { foo: 'bar' }, - })).to.eventually.be.rejected; - }); - - it('Should be rejected when invalid sortOrder parameter is provided', () => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - return expect(search.getBrowseResults({ - section, - filters: { group_id: groupId }, - sortOrder: 'abc', - })).to.eventually.be.rejected; - }); - - it('Should be rejected when invalid section parameter is provided', () => { - const { search } = new ConstructorIO({ apiKey: testApiKey }); - - return expect(search.getBrowseResults({ - section: 123, - filters: { group_id: groupId }, - })).to.eventually.be.rejected; - }); - - it('Should be rejected when invalid apiKey is provided', () => { - const { search } = new ConstructorIO({ apiKey: 'fyzs7tfF8L161VoAXQ8u' }); - - return expect(search.getBrowseResults(groupId, { section })).to.eventually.be.rejected; - }); - }); }); From 4b7b6b7e1faea351862e0058949779691960c1f8 Mon Sep 17 00:00:00 2001 From: Steve Blaurock Date: Mon, 21 Oct 2019 17:41:34 -0600 Subject: [PATCH 4/5] Update readme to support browse module. --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f87ea941..147ca937 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ var constructorio = new ConstructorIOClient({ ## 4. Retrieve Results -After instantiating an instance of the client, four modules will be exposed as properties to help retrieve data from Constructor.io: `search`, `autocomplete`, `recommendations` and `tracking`. +After instantiating an instance of the client, four modules will be exposed as properties to help retrieve data from Constructor.io: `search`, `browse`, `autocomplete`, `recommendations` and `tracker`. ### Search -The search module can be used to retrieve search and browse results. Responses will be delivered via a Promise. The `parameters` object is optional. +The search module can be used to retrieve search results. Responses will be delivered via a Promise. The `parameters` object is optional. #### Retrieve search results ```javascript @@ -43,9 +43,22 @@ constructorio.search.getSearchResults('dogs', { }); ``` -##### Retrieve browse results +| Parameter | Type | Description | +| --- | --- | --- | +| `section` | string | Section to display results from | +| `page` | number | Page number of results | +| `resultsPerPage` | number | Number of results per page | +| `filters` | object | The criteria by which search results should be filtered | +| `sortBy` | string | The criteria by which search results should be sorted | +| `sortOrder` | string | The sort order by which search results should be sorted (descending or ascending) | + +### Browse + +The browse module can be used to retrieve browse results. Responses will be delivered via a Promise. The `parameters` object is optional. + +#### Retrieve search results ```javascript -constructorio.search.getBrowseResults({ +constructorio.browse.getBrowseResults('filter-name', 'filter-value', { parameters }).then(function(response) { console.log(response); From 34818d3565d336a3d89bb453146e61f34ceda7c6 Mon Sep 17 00:00:00 2001 From: Steve Blaurock Date: Tue, 22 Oct 2019 13:29:36 -0600 Subject: [PATCH 5/5] CR feedback. --- src/constructorio.js | 2 +- src/modules/browse.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constructorio.js b/src/constructorio.js index 47e940bc..bf4ee62c 100644 --- a/src/constructorio.js +++ b/src/constructorio.js @@ -22,7 +22,7 @@ class ConstructorIO { * @param {string} [sessionId] - Session id, defaults to value supplied by 'constructorio-id' * @param {string} [userId] - User id * @property {object} [search] - Interface to {@link module:search} - * @property {object} [search] - Interface to {@link module:browse} + * @property {object} [browse] - Interface to {@link module:browse} * @property {object} [autocomplete] - Interface to {@link module:autocomplete} * @property {object} [recommendations] - Interface to {@link module:recommendations} * @property {object} [tracker] - Interface to {@link module:tracker} diff --git a/src/modules/browse.js b/src/modules/browse.js index 00ce1a4c..d3e71903 100644 --- a/src/modules/browse.js +++ b/src/modules/browse.js @@ -87,7 +87,7 @@ function createBrowseUrl(filterName, filterValue, parameters, options) { const queryString = qs.stringify(queryParams, { indices: false }); - return `${serviceUrl}/browse/${filterName}/${filterValue}?${queryString}`; + return `${serviceUrl}/browse/${encodeURIComponent(filterName)}/${encodeURIComponent(filterValue)}?${queryString}`; } /**