@@ -4,7 +4,7 @@ import qs from 'qs';
44import { createSearchClient } from '../../../test/mock/createSearchClient' ;
55import { createWidget } from '../../../test/mock/createWidget' ;
66import { runAllMicroTasks } from '../../../test/utils/runAllMicroTasks' ;
7- import { Router , Widget , StateMapping , RouteState } from '../../types' ;
7+ import { Router , Widget , UiState , StateMapping , RouteState } from '../../types' ;
88import historyRouter from '../routers/history' ;
99import instantsearch from '../main' ;
1010
@@ -35,6 +35,42 @@ const createFakeStateMapping = (
3535 ...args ,
3636} ) ;
3737
38+ type HistoryState = {
39+ index : number ;
40+ entries : object [ ] ;
41+ listeners : Array < ( value : object ) => void > ;
42+ } ;
43+
44+ const createFakeHistory = (
45+ {
46+ index = - 1 ,
47+ entries = [ ] ,
48+ listeners = [ ] ,
49+ } : HistoryState = { } as HistoryState
50+ ) : any => {
51+ const state : HistoryState = {
52+ index,
53+ entries,
54+ listeners,
55+ } ;
56+
57+ return {
58+ subscribe ( listener : ( ) => void ) {
59+ state . listeners . push ( listener ) ;
60+ } ,
61+ push ( value : object ) {
62+ state . entries . push ( value ) ;
63+ state . index ++ ;
64+ } ,
65+ back ( ) {
66+ state . index -- ;
67+ listeners . forEach ( listener => {
68+ listener ( state . entries [ state . index ] ) ;
69+ } ) ;
70+ } ,
71+ } ;
72+ } ;
73+
3874const createFakeSearchBox = ( ) : Widget =>
3975 createWidget ( {
4076 render ( { helper } ) {
@@ -118,6 +154,56 @@ describe('RoutingManager', () => {
118154 } ) ;
119155 } ) ;
120156
157+ test ( 'should update the searchParameters on router state update' , done => {
158+ const searchClient = createSearchClient ( ) ;
159+
160+ let onRouterUpdateCallback : ( args : UiState ) => void ;
161+ const router = createFakeRouter ( {
162+ onUpdate : fn => {
163+ onRouterUpdateCallback = fn ;
164+ } ,
165+ } ) ;
166+
167+ const search = instantsearch ( {
168+ indexName : 'indexName' ,
169+ searchClient,
170+ routing : {
171+ router,
172+ } ,
173+ } ) ;
174+
175+ const widget = {
176+ render : jest . fn ( ) ,
177+ getWidgetSearchParameters : jest . fn ( ( searchParameters , { uiState } ) =>
178+ searchParameters . setQuery ( uiState . query )
179+ ) ,
180+ } ;
181+
182+ search . addWidget ( widget ) ;
183+
184+ search . start ( ) ;
185+
186+ search . once ( 'render' , ( ) => {
187+ // initialization is done at this point
188+
189+ expect ( search . mainIndex . getHelper ( ) ! . state . query ) . toBeUndefined ( ) ;
190+
191+ // this simulates a router update with a uiState of {query: 'a'}
192+ onRouterUpdateCallback ( {
193+ indexName : {
194+ query : 'a' ,
195+ } ,
196+ } ) ;
197+
198+ search . once ( 'render' , ( ) => {
199+ // the router update triggers a new search
200+ // and given that the widget reads q as a query parameter
201+ expect ( search . mainIndex . getHelper ( ) ! . state . query ) . toEqual ( 'a' ) ;
202+ done ( ) ;
203+ } ) ;
204+ } ) ;
205+ } ) ;
206+
121207 test ( 'should apply state mapping on differences after searchfunction' , done => {
122208 const searchClient = createSearchClient ( ) ;
123209
@@ -282,6 +368,80 @@ describe('RoutingManager', () => {
282368 } ,
283369 } ) ;
284370 } ) ;
371+
372+ test ( 'should keep the UI state up to date on router.update' , async ( ) => {
373+ const searchClient = createSearchClient ( ) ;
374+ const stateMapping = createFakeStateMapping ( { } ) ;
375+ const history = createFakeHistory ( ) ;
376+ const router = createFakeRouter ( {
377+ onUpdate ( fn ) {
378+ history . subscribe ( state => {
379+ fn ( state ) ;
380+ } ) ;
381+ } ,
382+ write : jest . fn ( state => {
383+ history . push ( state ) ;
384+ } ) ,
385+ } ) ;
386+
387+ const search = instantsearch ( {
388+ indexName : 'indexName' ,
389+ searchClient,
390+ routing : {
391+ router,
392+ stateMapping,
393+ } ,
394+ } ) ;
395+
396+ const fakeSearchBox : any = createFakeSearchBox ( ) ;
397+ const fakeHitsPerPage = createFakeHitsPerPage ( ) ;
398+
399+ search . addWidget ( fakeSearchBox ) ;
400+ search . addWidget ( fakeHitsPerPage ) ;
401+
402+ search . start ( ) ;
403+
404+ await runAllMicroTasks ( ) ;
405+
406+ // Trigger an update - push a change
407+ fakeSearchBox . refine ( 'Apple' ) ;
408+
409+ expect ( router . write ) . toHaveBeenCalledTimes ( 1 ) ;
410+ expect ( router . write ) . toHaveBeenLastCalledWith ( {
411+ indexName : {
412+ query : 'Apple' ,
413+ } ,
414+ } ) ;
415+
416+ // Trigger an update - push a change
417+ fakeSearchBox . refine ( 'Apple iPhone' ) ;
418+
419+ expect ( router . write ) . toHaveBeenCalledTimes ( 2 ) ;
420+ expect ( router . write ) . toHaveBeenLastCalledWith ( {
421+ indexName : {
422+ query : 'Apple iPhone' ,
423+ } ,
424+ } ) ;
425+
426+ await runAllMicroTasks ( ) ;
427+
428+ // Trigger an update - Apple iPhone → Apple
429+ history . back ( ) ;
430+
431+ await runAllMicroTasks ( ) ;
432+
433+ // Trigger getConfiguration
434+ search . removeWidget ( fakeHitsPerPage ) ;
435+
436+ await runAllMicroTasks ( ) ;
437+
438+ expect ( router . write ) . toHaveBeenCalledTimes ( 3 ) ;
439+ expect ( router . write ) . toHaveBeenLastCalledWith ( {
440+ indexName : {
441+ query : 'Apple' ,
442+ } ,
443+ } ) ;
444+ } ) ;
285445 } ) ;
286446
287447 describe ( 'windowTitle' , ( ) => {
0 commit comments