vue-instantsearch
Version:
👀 Lightning-fast Algolia search for Vue apps
1,292 lines (1,152 loc) • 34.5 kB
JavaScript
/**
* @jest-environment jsdom
*/
/* eslint-disable jest/no-done-callback */
import {
AlgoliaSearchHelper,
SearchParameters,
SearchResults,
} from 'algoliasearch-helper';
import VueI18n from 'vue-i18n';
import { createI18n } from 'vue-i18n-vue3';
import Router from 'vue-router';
import Vuex from 'vuex';
import { createStore } from 'vuex4';
import { mount, createSSRApp } from '../../../test/utils';
import Configure from '../../components/Configure';
import Index from '../../components/Index';
import InstantSearchSsr from '../../components/InstantSearchSsr';
import SearchBox from '../../components/SearchBox.vue';
import { createWidgetMixin } from '../../mixins/widget';
import { createServerRootMixin } from '../createServerRootMixin';
import { createFakeClient } from '../testutils/client';
import { createSerializedState } from '../testutils/helper';
import { isVue3, isVue2, Vue2, renderCompat } from '../vue-compat';
jest.unmock('instantsearch.js/es');
function renderToString(app) {
if (isVue3) {
return require('@vue/server-renderer').renderToString(app);
} else {
return new Promise((resolve, reject) => {
require('vue-server-renderer/basic')(app, (err, res) => {
if (err) reject(err);
resolve(res);
});
});
}
}
const forceIsServerMixin = {
beforeCreate() {
Object.setPrototypeOf(
this,
new Proxy(Object.getPrototypeOf(this), {
get: (target, key, receiver) =>
key === '$isServer' ? true : Reflect.get(target, key, receiver),
})
);
},
};
process.env.VUE_ENV = 'server';
describe('createServerRootMixin', () => {
describe('creation', () => {
it('requires searchClient', () => {
expect(() =>
createSSRApp({
mixins: [
createServerRootMixin({
searchClient: undefined,
indexName: 'lol',
}),
],
})
).toThrowErrorMatchingInlineSnapshot(`
"The \`searchClient\` option is required.
See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/"
`);
});
it('creates an instantsearch instance on "data"', () => {
const App = {
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'lol',
}),
],
render: () => null,
};
const wrapper = mount(App);
expect(wrapper.vm.$data).toEqual({
instantsearch: expect.objectContaining({
start: expect.any(Function),
}),
});
});
it('provides the instantsearch instance', (done) => {
const App = {
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'myIndexName',
}),
],
template: `<div><slot /></div>`,
};
const Child = {
mixins: [createWidgetMixin({ connector: true })],
mounted() {
expect(this.instantSearchInstance).toEqual(
expect.objectContaining({
start: expect.any(Function),
dispose: expect.any(Function),
mainIndex: expect.any(Object),
addWidgets: expect.any(Function),
removeWidgets: expect.any(Function),
})
);
done();
},
render() {
return null;
},
};
mount({
components: { App, InstantSearchSsr, Child },
template: `
<App>
<InstantSearchSsr>
<Child />
</InstantSearchSsr>
</App>
`,
});
});
});
describe('findResultsState', () => {
it('provides findResultsState', () => {
return new Promise((resolve) => {
const app = createSSRApp({
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'hello',
}),
],
render: renderCompat((h) => h(InstantSearchSsr, {})),
created() {
expect(typeof this.instantsearch.findResultsState).toBe('function');
resolve();
},
});
renderToString(app);
});
});
it('requires renderToString', async () => {
const searchClient = createFakeClient();
const app = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
expect(() =>
this.instantsearch.findResultsState({ component: this })
).toThrowErrorMatchingInlineSnapshot(
`"findResultsState requires \`renderToString: (component) => Promise<string>\` in the first argument."`
);
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(app)),
});
await renderToString(wrapper);
});
it('detects child widgets', async () => {
const searchClient = createFakeClient();
let mainIndex;
const app = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
render: renderCompat((h) =>
/**
* This code triggers this warning in Vue 3:
* > Non-function value encountered for default slot. Prefer function slots for better performance.
*
* To fix it, replace the third argument
* > [h(...), h(...)]
* with
* > { default: () => [h(...), h(...)] }
*
* but it's not important (and not compatible in vue2), we're leaving it as-is.
*/
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
created() {
mainIndex = this.instantsearch.mainIndex;
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(app)),
});
await renderToString(wrapper);
expect(mainIndex.getWidgetUiState({})).toMatchInlineSnapshot(`
{
"hello": {
"configure": {
"hitsPerPage": 100,
},
},
}
`);
expect(searchClient.search).toHaveBeenCalledTimes(1);
expect(searchClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
[
{
"indexName": "hello",
"params": {
"hitsPerPage": 100,
"query": "",
},
},
]
`);
});
it('returns correct results state', () => {
const searchClient = createFakeClient();
return new Promise((resolve, reject) => {
const app = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
h(
Index,
{
attrs: {
indexName: 'hello',
indexId: 'nestedIndex',
},
},
[]
),
])
),
async serverPrefetch() {
const state = await this.instantsearch.findResultsState({
component: this,
renderToString,
});
try {
expect(state).toEqual({
hello: expect.objectContaining({}),
nestedIndex: expect.objectContaining({}),
});
expect(state.hello).toEqual({
requestParams: [
{
hitsPerPage: 100,
query: '',
},
],
results: [
{
query: '',
},
],
state: {
disjunctiveFacets: [],
disjunctiveFacetsRefinements: {},
facets: [],
facetsExcludes: {},
facetsRefinements: {},
hierarchicalFacets: [],
hierarchicalFacetsRefinements: {},
hitsPerPage: 100,
index: 'hello',
numericRefinements: {},
query: '',
tagRefinements: [],
},
});
// Parent's widgets state should not be merged into nested index state
expect(state.nestedIndex).toEqual({
requestParams: [
{
hitsPerPage: 100,
query: '',
},
],
results: [
{
query: '',
},
],
state: {
disjunctiveFacets: [],
disjunctiveFacetsRefinements: {},
facets: [],
facetsExcludes: {},
facetsRefinements: {},
hierarchicalFacets: [],
hierarchicalFacetsRefinements: {},
index: 'hello',
numericRefinements: {},
tagRefinements: [],
},
});
} catch (err) {
reject(err);
}
resolve();
return state;
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(app)),
});
renderToString(wrapper);
});
});
it('forwards router', async () => {
const searchClient = createFakeClient();
let router;
if (isVue3) {
const Router4 = require('vue-router4');
router = Router4.createRouter({
history: Router4.createMemoryHistory(),
routes: [{ path: '', component: {} }],
});
} else {
router = new Router({});
}
// there are two renders of App, each with an assertion
expect.assertions(2);
if (isVue2) {
Vue2.use(Router);
}
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
data() {
expect(this.$router).toBe(router);
return {};
},
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(App)),
...(isVue2 ? { router } : {}),
});
if (isVue3) {
wrapper.use(router);
}
await renderToString(wrapper);
});
it('forwards vuex', async () => {
const searchClient = createFakeClient();
if (isVue2) {
Vue2.use(Vuex);
}
const store = isVue3 ? createStore() : new Vuex.Store();
// there are two renders of App, each with an assertion
expect.assertions(2);
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
data() {
expect(this.$store).toBe(store);
return {};
},
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
...(isVue2 ? { store } : {}),
render: renderCompat((h) => h(App)),
});
if (isVue3) {
wrapper.use(store);
}
await renderToString(wrapper);
});
it('forwards i18n', async () => {
const searchClient = createFakeClient();
if (isVue2) {
Vue2.use(VueI18n);
}
const i18n = isVue3 ? createI18n() : new VueI18n();
// there are two renders of App, each with an assertion
expect.assertions(2);
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
data() {
expect(this.$i18n).toBe(isVue3 ? i18n.global : i18n);
return {};
},
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
...(isVue2 ? { i18n } : {}),
render: renderCompat((h) => h(App)),
});
if (isVue3) {
wrapper.use(i18n);
}
await renderToString(wrapper);
});
if (isVue2) {
it('forwards props', async () => {
const searchClient = createFakeClient();
// there are two renders of App, each with an assertion
expect.assertions(2);
const someProp = { data: Math.random() };
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
props: {
someProp: {
required: true,
type: Object,
validator(value) {
expect(value).toBe(someProp);
return value === someProp;
},
},
},
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(App, { props: { someProp } })),
});
await renderToString(wrapper);
});
it('forwards slots', () => {
const searchClient = createFakeClient();
expect.assertions(2);
return new Promise((resolve, reject) => {
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
components: { InstantSearchSsr },
template: `
<InstantSearchSsr>
<slot />
</InstantSearchSsr>
`,
serverPrefetch() {
return (
this.instantsearch
.findResultsState({ component: this, renderToString })
.then((res) => {
// eslint-disable-next-line jest/no-conditional-expect
expect(
this.instantsearch.mainIndex
.getWidgets()
.map((w) => w.$$type)
).toEqual(['ais.configure']);
// eslint-disable-next-line jest/no-conditional-expect
expect(res.hello.state.hitsPerPage).toBe(100);
})
// jest throws an error we need to catch, since stuck in the flow
.catch((e) => {
reject(e);
})
);
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
components: { App, Configure },
template: `
<App>
<Configure :hits-per-page.camel="100" />
</App>
`,
});
renderToString(wrapper).then(resolve);
});
});
// TODO: forwarding of scoped slots doesn't yet work.
it.skip('forwards scoped slots', async (done) => {
const searchClient = createFakeClient();
expect.assertions(2);
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [this.$scopedSlots.default({ test: true })])
),
serverPrefetch() {
return (
this.instantsearch
.findResultsState({ component: this, renderToString })
.then((res) => {
// eslint-disable-next-line jest/no-conditional-expect
expect(
this.instantsearch.mainIndex
.getWidgets()
.map((w) => w.$$type)
).toEqual(['ais.configure']);
// eslint-disable-next-line jest/no-conditional-expect
expect(res.hello._state.hitsPerPage).toBe(100);
})
// jest throws an error we need to catch, since stuck in the flow
.catch((e) => {
done.fail(e);
})
);
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) =>
h(App, {
scopedSlots: {
default({ test }) {
if (test) {
return h(Configure, {
hitsPerPage: 100,
});
}
return null;
},
},
})
),
});
await renderToString(wrapper);
done();
});
it('forwards root', async () => {
const searchClient = createFakeClient();
// there are two renders of App, each with an assertion
expect.assertions(2);
const App = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
render: renderCompat(function (h) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
expect(this.$root).toBe(wrapper);
return h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
]);
}),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(App)),
});
await renderToString(wrapper);
});
it('forwards nuxt', async () => {
const searchClient = createFakeClient();
let nuxt = 0;
// every time the function gets called, we get a different "nuxt"
// this can be used to assert both "nuxt" objects are equal
const getNuxtCounter = () => ++nuxt;
// there are two renders of App, each with an assertion
expect.assertions(2);
const App = {
mixins: [
{
beforeCreate() {
this.$nuxt = getNuxtCounter();
},
},
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
data() {
expect(this.$nuxt).toEqual(1);
return {};
},
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(App)),
});
await renderToString(wrapper);
});
it('searches only once', async () => {
const searchClient = createFakeClient();
const app = {
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
render: renderCompat((h) =>
/**
* This code triggers this warning in Vue 3:
* > Non-function value encountered for default slot. Prefer function slots for better performance.
*
* To fix it, replace the third argument
* > [h(...), h(...)]
* with
* > { default: () => [h(...), h(...)] }
*
* but it's not important (and not compatible in vue2), we're leaving it as-is.
*/
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
};
const wrapper = createSSRApp({
mixins: [forceIsServerMixin],
render: renderCompat((h) => h(app)),
});
await renderToString(wrapper);
expect(searchClient.search).toHaveBeenCalledTimes(1);
expect(searchClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
[
{
"indexName": "hello",
"params": {
"hitsPerPage": 100,
"query": "",
},
},
]
`);
});
it('works when component is at root (and therefore has no $vnode)', async () => {
const searchClient = createFakeClient();
let mainIndex;
const app = {
render: renderCompat((h) =>
/**
* This code triggers this warning in Vue 3:
* > Non-function value encountered for default slot. Prefer function slots for better performance.
*
* To fix it, replace the third argument
* > [h(...), h(...)]
* with
* > { default: () => [h(...), h(...)] }
*
* but it's not important (and not compatible in vue2), we're leaving it as-is.
*/
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
};
const wrapper = createSSRApp({
mixins: [
forceIsServerMixin,
createServerRootMixin({
searchClient,
indexName: 'hello',
}),
],
serverPrefetch() {
return this.instantsearch.findResultsState({
component: this,
renderToString,
});
},
created() {
mainIndex = this.instantsearch.mainIndex;
},
render: renderCompat((h) => h(app)),
});
await renderToString(wrapper);
expect(mainIndex.getWidgetUiState({})).toMatchInlineSnapshot(`
{
"hello": {
"configure": {
"hitsPerPage": 100,
},
},
}
`);
expect(searchClient.search).toHaveBeenCalledTimes(1);
expect(searchClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
[
{
"indexName": "hello",
"params": {
"hitsPerPage": 100,
"query": "",
},
},
]
`);
});
}
});
describe('hydrate', () => {
it('sets _initialResults', () => {
const serialized = createSerializedState();
const app = {
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'hello',
}),
],
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
// in test, beforeCreated doesn't have $data yet, but IRL it does
created() {
this.instantsearch.hydrate({
hello: serialized,
});
},
};
const {
vm: { instantsearch },
} = mount(app);
expect(instantsearch._initialResults).toEqual(
expect.objectContaining({
hello: {
state: expect.any(Object),
results: expect.any(Object),
},
})
);
expect(instantsearch._initialResults.hello).toEqual(
expect.objectContaining(serialized)
);
});
it('inits the main index', () => {
const serialized = createSerializedState();
const app = {
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'hello',
}),
],
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
};
const {
vm: { instantsearch },
} = mount(app);
expect(instantsearch.mainIndex.getHelper()).toBe(null);
instantsearch.hydrate({
hello: serialized,
});
expect(instantsearch.mainIndex.getHelper()).toEqual(
expect.any(AlgoliaSearchHelper)
);
expect(instantsearch.mainIndex.getHelper()).not.toBeNull();
});
it('sets helper & mainHelper', () => {
const serialized = createSerializedState();
const app = {
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'hello',
}),
],
render: renderCompat((h) =>
h(InstantSearchSsr, {}, [
h(Configure, {
attrs: {
hitsPerPage: 100,
},
}),
h(SearchBox),
])
),
};
const {
vm: { instantsearch },
} = mount(app);
expect(instantsearch.helper).toBe(null);
expect(instantsearch.mainHelper).toBe(null);
instantsearch.hydrate({
hello: serialized,
});
expect(instantsearch.helper).toEqual(expect.any(AlgoliaSearchHelper));
expect(instantsearch.mainHelper).toEqual(expect.any(AlgoliaSearchHelper));
});
});
describe('__forceRender', () => {
it('calls render on widget', () => {
let instantSearchInstance;
mount({
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'lol',
}),
],
created() {
instantSearchInstance = this.instantsearch;
},
render() {},
});
const widget = {
init: jest.fn(),
render: jest.fn(),
};
instantSearchInstance.hydrate({
lol: createSerializedState(),
});
instantSearchInstance.__forceRender(
widget,
instantSearchInstance.mainIndex
);
expect(widget.init).toHaveBeenCalledTimes(0);
expect(widget.render).toHaveBeenCalledTimes(1);
const renderArgs = widget.render.mock.calls[0][0];
expect(renderArgs).toMatchInlineSnapshot(
{
helper: expect.anything(),
results: expect.anything(),
scopedResults: expect.arrayContaining([
expect.objectContaining({
helper: expect.anything(),
indexId: expect.any(String),
results: expect.anything(),
}),
]),
parent: expect.anything(),
state: expect.anything(),
instantSearchInstance: expect.anything(),
},
`
{
"createURL": [Function],
"helper": Anything,
"instantSearchInstance": Anything,
"parent": Anything,
"results": Anything,
"scopedResults": ArrayContaining [
ObjectContaining {
"helper": Anything,
"indexId": Any<String>,
"results": Anything,
},
],
"searchMetadata": {
"isSearchStalled": false,
},
"state": Anything,
"templatesConfig": {},
}
`
);
});
it('uses the results passed to hydrate for rendering', () => {
let instantSearchInstance;
mount({
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'lol',
}),
],
created() {
instantSearchInstance = this.instantsearch;
},
render() {},
});
const widget = {
init: jest.fn(),
render: jest.fn(),
};
const resultsState = createSerializedState();
const state = new SearchParameters(resultsState.state);
const localState = new SearchParameters({
index: 'lol',
});
const results = new SearchResults(state, resultsState.results);
instantSearchInstance.hydrate({
lol: resultsState,
});
instantSearchInstance.__forceRender(
widget,
instantSearchInstance.mainIndex
);
expect(widget.init).toHaveBeenCalledTimes(0);
expect(widget.render).toHaveBeenCalledTimes(1);
const renderArgs = widget.render.mock.calls[0][0];
// renders with local state, not the one from results
expect(renderArgs.state).toEqual(localState);
results._state = localState;
expect(renderArgs.results).toEqual(results);
expect(renderArgs.scopedResults).toHaveLength(1);
expect(renderArgs.scopedResults[0].indexId).toEqual('lol');
expect(renderArgs.scopedResults[0].results).toEqual(results);
});
describe('createURL', () => {
it('returns # if instantsearch has no routing', () => {
let instantSearchInstance;
mount({
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'lol',
}),
],
created() {
instantSearchInstance = this.instantsearch;
},
render() {},
});
const widget = {
init: jest.fn(),
render: jest.fn(),
};
instantSearchInstance.hydrate({
lol: createSerializedState(),
});
instantSearchInstance.__forceRender(
widget,
instantSearchInstance.mainIndex
);
const renderArgs = widget.render.mock.calls[0][0];
expect(renderArgs.createURL()).toBe('#');
});
it('allows for widgets without getWidgetState', () => {
let instantSearchInstance;
mount({
mixins: [
createServerRootMixin({
searchClient: createFakeClient(),
indexName: 'lol',
}),
],
created() {
instantSearchInstance = this.instantsearch;
},
render() {},
});
const widget = {
init: jest.fn(),
render: jest.fn(),
getWidgetState(uiState) {
return uiState;
},
};
const widgetWithoutGetWidgetState = {
init: jest.fn(),
render: jest.fn(),
};
instantSearchInstance.hydrate({
lol: createSerializedState(),
});
instantSearchInstance.addWidgets([widget, widgetWithoutGetWidgetState]);
instantSearchInstance.__forceRender(
widget,
instantSearchInstance.mainIndex
);
const renderArgs = widget.render.mock.calls[0][0];
expect(renderArgs.createURL()).toBe('#');
});
});
});
});