Skip to content

Commit

Permalink
feat(middlewares): add middlewares prop to ais-instant-search (algoli…
Browse files Browse the repository at this point in the history
…a/vue-instantsearch#939)

* feat(middlewares): add middlewares prop to ais-instant-search

* unsubscribe removed middlewares

* update bundlesize

* update instantsearch.js
  • Loading branch information
Eunjae Lee authored Apr 6, 2021
1 parent cb6b8cd commit c32e06f
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 3 deletions.
4 changes: 2 additions & 2 deletions packages/vue-instantsearch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"dependencies": {
"algoliasearch-helper": "^3.1.0",
"instantsearch.js": "^4.16.1"
"instantsearch.js": "^4.20.0"
},
"peerDependencies": {
"algoliasearch": ">= 3.32.0 < 5",
Expand Down Expand Up @@ -114,7 +114,7 @@
"bundlesize": [
{
"path": "./dist/vue-instantsearch.js",
"maxSize": "52.50 kB"
"maxSize": "52.75 kB"
},
{
"path": "./dist/vue-instantsearch.common.js",
Expand Down
4 changes: 4 additions & 0 deletions packages/vue-instantsearch/src/components/InstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export default createInstantSearchComponent({
return false;
},
},
middlewares: {
type: Array,
default: null,
},
},
data() {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import InstantSearch from '../InstantSearch';
import { createWidgetMixin } from '../../mixins/widget';
import { createFakeClient } from '../../util/testutils/client';

import SearchBox from '../SearchBox.vue';
jest.unmock('instantsearch.js/es');

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

it('child widgets get added to its parent instantsearch', () => {
const widgetInstance = {
render() {},
Expand Down Expand Up @@ -32,3 +35,154 @@ it('child widgets get added to its parent instantsearch', () => {
widgetInstance
);
});

describe('middlewares', () => {
const createFakeMiddleware = () => {
const middlewareSpy = {
onStateChange: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
};
const middleware = jest.fn(() => middlewareSpy);

return [middleware, middlewareSpy];
};

it('subscribes middlewares', async () => {
const [middleware, middlewareSpy] = createFakeMiddleware();

mount(InstantSearch, {
propsData: {
searchClient: createFakeClient(),
indexName: 'indexName',
middlewares: [middleware],
},
});
await Vue.nextTick();

expect(middlewareSpy.subscribe).toHaveBeenCalledTimes(1);
});

it('subscribes newly added middleware', async () => {
const [middleware1, middlewareSpy1] = createFakeMiddleware();

const wrapper = mount({
components: {
AisInstantSearch: InstantSearch,
AisSearchBox: SearchBox,
},
template: `
<ais-instant-search
:search-client="searchClient"
:index-name="indexName"
:middlewares="middlewares"
>
<ais-search-box />
</ais-instant-search>
`,
data() {
return {
searchClient: createFakeClient(),
indexName: 'indexName',
middlewares: [middleware1],
};
},
});

await wait(20);
expect(middlewareSpy1.subscribe).toHaveBeenCalledTimes(1);

await wrapper.find('input').setValue('a');
await Vue.nextTick();

expect(middlewareSpy1.onStateChange).toHaveBeenCalledTimes(1);
expect(middlewareSpy1.onStateChange).toHaveBeenCalledWith({
uiState: { indexName: { query: 'a' } },
});

const [middleware2, middlewareSpy2] = createFakeMiddleware();
wrapper.setData({
middlewares: [middleware1, middleware2],
});
await Vue.nextTick();

expect(middlewareSpy2.subscribe).toHaveBeenCalledTimes(1);
expect(middlewareSpy2.onStateChange).toHaveBeenCalledTimes(0);

await wrapper.find('input').setValue('b');
await Vue.nextTick();

expect(middlewareSpy1.onStateChange).toHaveBeenCalledTimes(2);
expect(middlewareSpy1.onStateChange).toHaveBeenCalledWith({
uiState: { indexName: { query: 'b' } },
});
expect(middlewareSpy2.onStateChange).toHaveBeenCalledTimes(1);
expect(middlewareSpy2.onStateChange).toHaveBeenCalledWith({
uiState: { indexName: { query: 'b' } },
});

expect(middlewareSpy1.unsubscribe).toHaveBeenCalledTimes(0);
expect(middlewareSpy2.unsubscribe).toHaveBeenCalledTimes(0);
});

it('unsubscribes removed middleware', async () => {
const [middleware1, middlewareSpy1] = createFakeMiddleware();
const [middleware2, middlewareSpy2] = createFakeMiddleware();

const wrapper = mount({
components: {
AisInstantSearch: InstantSearch,
AisSearchBox: SearchBox,
},
template: `
<ais-instant-search
:search-client="searchClient"
:index-name="indexName"
:middlewares="middlewares"
>
<ais-search-box />
</ais-instant-search>
`,
data() {
return {
searchClient: createFakeClient(),
indexName: 'indexName',
middlewares: [middleware1, middleware2],
};
},
});

await wait(20);
expect(middlewareSpy1.subscribe).toHaveBeenCalledTimes(1);
expect(middlewareSpy2.subscribe).toHaveBeenCalledTimes(1);

await wrapper.find('input').setValue('a');
await Vue.nextTick();

expect(middlewareSpy1.onStateChange).toHaveBeenCalledTimes(1);
expect(middlewareSpy1.onStateChange).toHaveBeenCalledWith({
uiState: { indexName: { query: 'a' } },
});
expect(middlewareSpy2.onStateChange).toHaveBeenCalledTimes(1);
expect(middlewareSpy2.onStateChange).toHaveBeenCalledWith({
uiState: { indexName: { query: 'a' } },
});

wrapper.setData({
middlewares: [middleware1],
});
await Vue.nextTick();

expect(middlewareSpy1.unsubscribe).toHaveBeenCalledTimes(0);
expect(middlewareSpy2.unsubscribe).toHaveBeenCalledTimes(1);

await wrapper.find('input').setValue('b');
await Vue.nextTick();

expect(middlewareSpy1.onStateChange).toHaveBeenCalledTimes(2);
expect(middlewareSpy1.onStateChange).toHaveBeenCalledWith({
uiState: { indexName: { query: 'b' } },
});
expect(middlewareSpy2.onStateChange).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ export const createInstantSearchComponent = component =>
// private InstantSearch.js API:
this.instantSearchInstance._searchFunction = searchFunction;
},
middlewares: {
immediate: true,
handler(next, prev) {
(prev || [])
.filter(middleware => (next || []).indexOf(middleware) === -1)
.forEach(middlewareToRemove => {
this.instantSearchInstance.unuse(middlewareToRemove);
});

(next || [])
.filter(middleware => (prev || []).indexOf(middleware) === -1)
.forEach(middlewareToAdd => {
this.instantSearchInstance.use(middlewareToAdd);
});
},
},
},
created() {
const searchClient = this.instantSearchInstance.client;
Expand Down

0 comments on commit c32e06f

Please sign in to comment.