Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(createInstantSearchManager): drop outdated response #1765

Merged
merged 1 commit into from
Dec 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* eslint-env jest, jasmine */
/* eslint-disable no-console */

import createInstantSearchManager from './createInstantSearchManager';

import algoliaClient from 'algoliasearch';

jest.useFakeTimers();

jest.mock('algoliasearch-helper/src/algoliasearch.helper.js', () => {
let count = 0;
console.log('setup');
const Helper = require.requireActual('algoliasearch-helper/src/algoliasearch.helper.js');
Helper.prototype._handleResponse = function(state) {
this.emit('error', {count: count++}, state);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid having several files, maybe something you could do is emitting a different event depending of the query present in the state parameter?

};
return Helper;
});

const client = algoliaClient('latency', '249078a3d4337a8231f1665ec5a44966');
client.search = jest.fn((queries, cb) => {
if (cb) {
setImmediate(() => {
// We do not care about the returned values because we also control how
// it will handle in the helper
cb(null, null);
});
return undefined;
}

return new Promise(resolve => {
// cf comment above
resolve(null);
});
});

describe('createInstantSearchManager', () => {
describe('with error from algolia', () => {
describe('on widget lifecycle', () => {
it('updates the store and searches', () => {
const ism = createInstantSearchManager({
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
});

ism.widgetsManager.registerWidget({
getSearchParameters: params => params.setQuery('search'),
});

expect(ism.store.getState().error).toBe(null);

jest.runAllTimers();

const store = ism.store.getState();
expect(store.error).toEqual({count: 0});
expect(store.results).toBe(null);

ism.widgetsManager.update();

jest.runAllTimers();

const store1 = ism.store.getState();
expect(store1.error).toEqual({count: 1});
expect(store1.results).toBe(null);
});
});
describe('on external updates', () => {
it('updates the store and searches', () => {
const ism = createInstantSearchManager({
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
});

ism.onExternalStateUpdate({});

expect(ism.store.getState().error).toBe(null);

jest.runAllTimers();

const store = ism.store.getState();
expect(store.error).toEqual({count: 2});
expect(store.results).toBe(null);
});
});
});
});
65 changes: 32 additions & 33 deletions packages/react-instantsearch/src/core/createInstantSearchManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,21 @@ import highlightTags from './highlightTags.js';
*/
export default function createInstantSearchManager({
indexName,
initialState,
initialState = {},
algoliaClient,
searchParameters = {},
}) {
const helper = algoliasearchHelper(algoliaClient);
const baseSP = new SearchParameters({
...searchParameters,
index: indexName,
...highlightTags,
});

const helper = algoliasearchHelper(algoliaClient, indexName, baseSP);
helper.on('result', handleSearchSuccess);
helper.on('error', handleSearchError);

const initialSearchParameters = helper.state;

const widgetsManager = createWidgetsManager(onWidgetsUpdate);

Expand All @@ -36,7 +46,7 @@ export default function createInstantSearchManager({
.map(widget => widget.getMetadata(state));
}

function getSearchParameters(initialSearchParameters) {
function getSearchParameters() {
return widgetsManager.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.reduce(
Expand All @@ -46,37 +56,26 @@ export default function createInstantSearchManager({
}

function search() {
const baseSP = new SearchParameters({
...searchParameters,
index: indexName,
...highlightTags,
const widgetSearchParameters = getSearchParameters(helper.state);

helper.setState(widgetSearchParameters)
.search();
}

function handleSearchSuccess(content) {
store.setState({
...store.getState(),
results: content,
searching: false,
});
}

function handleSearchError(error) {
store.setState({
...store.getState(),
error,
searching: false,
});
const widgetSearchParameters = getSearchParameters(baseSP);

helper
.searchOnce(widgetSearchParameters)
.then(({content}) => {
store.setState({
...store.getState(),
results: content,
searching: false,
});
}, error => {
store.setState({
...store.getState(),
error,
searching: false,
});
})
.catch(error => {
// Since setState is synchronous, any error that occurs in the render of a
// component will be swallowed by this promise.
// This is a trick to make the error show up correctly in the console.
// See http://stackoverflow.com/a/30741722/969302
setTimeout(() => {
throw error;
});
});
}

// Called whenever a widget has been rendered with new props.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* eslint-env jest, jasmine */
/* eslint-disable no-console */

import createInstantSearchManager from './createInstantSearchManager';

import algoliaClient from 'algoliasearch';

jest.useFakeTimers();

jest.mock('algoliasearch-helper/src/algoliasearch.helper.js', () => {
let count = 0;
const Helper = require.requireActual('algoliasearch-helper/src/algoliasearch.helper.js');
Helper.prototype._handleResponse = function(state) {
this.emit('result', {count: count++}, state);
};
return Helper;
});

const client = algoliaClient('latency', '249078a3d4337a8231f1665ec5a44966');
client.search = jest.fn((queries, cb) => {
if (cb) {
setImmediate(() => {
// We do not care about the returned values because we also control how
// it will handle in the helper
cb(null, null);
});
return undefined;
}

return new Promise(resolve => {
// cf comment above
resolve(null);
});
});

describe('createInstantSearchManager', () => {
describe('with correct result from algolia', () => {
describe('on widget lifecycle', () => {
it('updates the store and searches', () => {
const ism = createInstantSearchManager({
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
});

ism.widgetsManager.registerWidget({
getSearchParameters: params => params.setQuery('search'),
});

expect(ism.store.getState().results).toBe(null);

jest.runAllTimers();

const store = ism.store.getState();
expect(store.results).toEqual({count: 0});
expect(store.error).toBe(null);

ism.widgetsManager.update();

jest.runAllTimers();

const store1 = ism.store.getState();
expect(store1.results).toEqual({count: 1});
expect(store1.error).toBe(null);
});
});
describe('on external updates', () => {
it('updates the store and searches', () => {
const ism = createInstantSearchManager({
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
});

ism.onExternalStateUpdate({});

expect(ism.store.getState().results).toBe(null);

jest.runAllTimers();

const store = ism.store.getState();
expect(store.results).toEqual({count: 2});
expect(store.error).toBe(null);
});
});
});
});
Loading