@internetarchive/collection-browser 0.4.15-alpha.9 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -280,11 +280,9 @@ export class CollectionBrowser
280
280
  letterFilters = true,
281
281
  sort = false,
282
282
  } = {}): void {
283
- console.log('clearing filters');
284
283
  // Don't bother clearing facets if none are checked, so that we don't
285
284
  // trigger unnecessary update cycles.
286
285
  if (facets && this.hasCheckedFacets) {
287
- console.log('clearing facets');
288
286
  this.selectedFacets = getDefaultSelectedFacets();
289
287
  }
290
288
 
@@ -323,6 +321,14 @@ export class CollectionBrowser
323
321
  return false;
324
322
  }
325
323
 
324
+ /**
325
+ * Manually requests to perform a search, which will only be executed if one of
326
+ * the query, the search type, or the sort has changed.
327
+ */
328
+ requestSearch() {
329
+ this.handleQueryChange();
330
+ }
331
+
326
332
  render() {
327
333
  this.setPlaceholderType();
328
334
  return html`
@@ -339,7 +345,7 @@ export class CollectionBrowser
339
345
 
340
346
  private setPlaceholderType() {
341
347
  this.placeholderType = null;
342
- if (!this.baseQuery) {
348
+ if (!this.baseQuery?.trim()) {
343
349
  this.placeholderType = 'empty-query';
344
350
  }
345
351
 
@@ -681,13 +687,10 @@ export class CollectionBrowser
681
687
  }
682
688
 
683
689
  if (changed.has('baseQuery') || changed.has('searchType')) {
684
- console.log('baseQuery or searchType changed:', this.baseQuery, this.searchType);
685
- // Unless this query/search type update is from the initial page load or the
686
- // result of hitting the back button,
690
+ // Unless this query/search type update is the result of hitting the back button,
687
691
  // we need to clear any existing filters since they may no longer be valid for
688
692
  // the new set of search results.
689
- if (!this.historyPopOccurred && this.initialQueryChangeHappened) {
690
- console.log('will clear filters from query change');
693
+ if (!this.historyPopOccurred) {
691
694
  // Only clear filters that haven't been simultaneously applied in this update
692
695
  this.clearFilters({
693
696
  facets: !changed.has('selectedFacets'),
@@ -801,7 +804,6 @@ export class CollectionBrowser
801
804
  }
802
805
 
803
806
  private emitSearchTypeChanged() {
804
- console.log('emitting search type change', this.searchType);
805
807
  this.dispatchEvent(
806
808
  new CustomEvent<SearchType>('searchTypeChanged', {
807
809
  detail: this.searchType,
@@ -1006,20 +1008,20 @@ export class CollectionBrowser
1006
1008
  /** The base query joined with any title/creator letter filters */
1007
1009
  private get filteredQuery(): string | undefined {
1008
1010
  if (!this.baseQuery) return undefined;
1009
- let filteredQuery = this.baseQuery;
1011
+ let filteredQuery = this.baseQuery.trim();
1010
1012
 
1011
1013
  const { sortFilterQueries } = this;
1012
1014
  if (sortFilterQueries) {
1013
1015
  filteredQuery += ` AND ${sortFilterQueries}`;
1014
1016
  }
1015
1017
 
1016
- return filteredQuery;
1018
+ return filteredQuery.trim();
1017
1019
  }
1018
1020
 
1019
1021
  /** The full query, including year facets and date range clauses */
1020
1022
  private get fullQuery(): string | undefined {
1021
1023
  if (!this.baseQuery) return undefined;
1022
- let fullQuery = this.baseQuery;
1024
+ let fullQuery = this.baseQuery.trim();
1023
1025
 
1024
1026
  const { facetQuery, dateRangeQueryClause, sortFilterQueries } = this;
1025
1027
 
@@ -1032,13 +1034,13 @@ export class CollectionBrowser
1032
1034
  if (sortFilterQueries) {
1033
1035
  fullQuery += ` AND ${sortFilterQueries}`;
1034
1036
  }
1035
- return fullQuery;
1037
+ return fullQuery.trim();
1036
1038
  }
1037
1039
 
1038
1040
  /** The full query without any title/creator letter filters */
1039
1041
  private get fullQueryWithoutAlphaFilters(): string | undefined {
1040
1042
  if (!this.baseQuery) return undefined;
1041
- let fullQuery = this.baseQuery;
1043
+ let fullQuery = this.baseQuery.trim();
1042
1044
 
1043
1045
  const { facetQuery, dateRangeQueryClause } = this;
1044
1046
 
@@ -1048,7 +1050,7 @@ export class CollectionBrowser
1048
1050
  if (dateRangeQueryClause) {
1049
1051
  fullQuery += ` AND ${dateRangeQueryClause}`;
1050
1052
  }
1051
- return fullQuery;
1053
+ return fullQuery.trim();
1052
1054
  }
1053
1055
 
1054
1056
  /**
@@ -1064,7 +1066,7 @@ export class CollectionBrowser
1064
1066
  )) {
1065
1067
  facetClauses.push(this.buildFacetClause(facetName, facetValues));
1066
1068
  }
1067
- return this.joinFacetClauses(facetClauses);
1069
+ return this.joinFacetClauses(facetClauses)?.trim();
1068
1070
  }
1069
1071
 
1070
1072
  /**
@@ -1176,17 +1178,43 @@ export class CollectionBrowser
1176
1178
  aggregationsSize: 10,
1177
1179
  // Note: we don't need an aggregations param to fetch the default aggregations from the PPS.
1178
1180
  // The default aggregations for the search_results page type should be what we need here.
1181
+ uid: this.facetFetchQueryKey,
1179
1182
  };
1180
1183
 
1181
1184
  this.facetsLoading = true;
1182
- const results = await this.searchService?.search(params, this.searchType);
1185
+ const searchResponse = await this.searchService?.search(
1186
+ params,
1187
+ this.searchType
1188
+ );
1189
+ const success = searchResponse?.success;
1183
1190
  this.facetsLoading = false;
1184
1191
 
1185
- this.aggregations = results?.success?.response.aggregations;
1192
+ if (!success) {
1193
+ const errorMsg = searchResponse?.error?.message;
1194
+ const detailMsg = searchResponse?.error?.details?.message;
1195
+
1196
+ if (!errorMsg && !detailMsg) {
1197
+ // @ts-ignore: Property 'Sentry' does not exist on type 'Window & typeof globalThis'
1198
+ window?.Sentry?.captureMessage?.(
1199
+ 'Missing or malformed facet response from backend',
1200
+ 'error'
1201
+ );
1202
+ }
1203
+
1204
+ return;
1205
+ }
1206
+
1207
+ // This is checking to see if the query has changed since the data was fetched.
1208
+ // If so, we just want to discard this set of aggregations because they are
1209
+ // likely no longer valid for the newer query.
1210
+ const returnedUid = (success.request.clientParameters as any).uid;
1211
+ const queryChangedSinceFetch = returnedUid !== this.facetFetchQueryKey;
1212
+ if (queryChangedSinceFetch) return;
1213
+
1214
+ this.aggregations = success?.response.aggregations;
1186
1215
 
1187
1216
  this.fullYearsHistogramAggregation =
1188
- results?.success?.response?.aggregations?.year_histogram ??
1189
- results?.success?.response?.aggregations?.['year-histogram']; // Temp fix until PPS FTS key is fixed to use underscore
1217
+ success?.response?.aggregations?.year_histogram;
1190
1218
  }
1191
1219
 
1192
1220
  private scrollToPage(pageNumber: number): Promise<void> {
@@ -1223,8 +1251,18 @@ export class CollectionBrowser
1223
1251
  * This lets us keep track of queries so we don't persist data that's
1224
1252
  * no longer relevant.
1225
1253
  */
1226
- private get pageFetchQueryKey() {
1227
- return `${this.fullQuery}-${this.searchType}-${this.sortParam?.field}-${this.sortParam?.direction}`;
1254
+ private get pageFetchQueryKey(): string {
1255
+ const sortField = this.sortParam?.field ?? 'none';
1256
+ const sortDirection = this.sortParam?.direction ?? 'none';
1257
+ return `${this.fullQuery}-${this.searchType}-${sortField}-${sortDirection}`;
1258
+ }
1259
+
1260
+ /**
1261
+ * Similar to `pageFetchQueryKey` above, but excludes sort fields since they
1262
+ * are not relevant in determining aggregation queries.
1263
+ */
1264
+ private get facetFetchQueryKey(): string {
1265
+ return `${this.fullQuery}-${this.searchType}`;
1228
1266
  }
1229
1267
 
1230
1268
  // this maps the query to the pages being fetched for that query
@@ -1254,6 +1292,7 @@ export class CollectionBrowser
1254
1292
  sort: sortParams,
1255
1293
  filters: this.filterMap,
1256
1294
  aggregations: { omit: true },
1295
+ uid: this.pageFetchQueryKey,
1257
1296
  };
1258
1297
  const searchResponse = await this.searchService?.search(
1259
1298
  params,
@@ -1278,36 +1317,15 @@ export class CollectionBrowser
1278
1317
  return;
1279
1318
  }
1280
1319
 
1281
- this.totalResults = success.response.totalResults;
1282
-
1283
- // this is checking to see if the query has changed since the data was fetched
1284
- // if so, we just want to discard the data since there should be a new query
1285
- // right behind it
1286
- const searchQuery = success.request.clientParameters.user_query;
1287
- const searchSort = success.request.clientParameters.sort;
1288
- let sortChanged = false;
1289
- if (!searchSort || searchSort.length === 0) {
1290
- // if we went from no sort to sort, the sort has changed
1291
- if (this.sortParam) {
1292
- sortChanged = true;
1293
- }
1294
- } else {
1295
- // check if the sort has changed
1296
- for (const sortType of searchSort) {
1297
- const [field, direction] = sortType.split(':');
1298
- if (
1299
- field !== this.sortParam?.field ||
1300
- direction !== this.sortParam?.direction
1301
- ) {
1302
- sortChanged = true;
1303
- break;
1304
- }
1305
- }
1306
- }
1307
- const queryChangedSinceFetch =
1308
- searchQuery !== this.filteredQuery || sortChanged;
1320
+ // This is checking to see if the query has changed since the data was fetched.
1321
+ // If so, we just want to discard the data since there should be a new query
1322
+ // right behind it.
1323
+ const returnedUid = (success.request.clientParameters as any).uid;
1324
+ const queryChangedSinceFetch = returnedUid !== this.pageFetchQueryKey;
1309
1325
  if (queryChangedSinceFetch) return;
1310
1326
 
1327
+ this.totalResults = success.response.totalResults;
1328
+
1311
1329
  const { results } = success.response;
1312
1330
  if (results && results.length > 0) {
1313
1331
  this.preloadCollectionNames(results);
@@ -93,7 +93,6 @@ export class RestorationStateHandler
93
93
  }
94
94
 
95
95
  private persistQueryStateToUrl(state: RestorationState) {
96
- console.log('persisting...', window.location.href, state);
97
96
  const url = new URL(window.location.href);
98
97
  const oldParams = new URLSearchParams(url.searchParams);
99
98
  const newParams = this.removeRecognizedParams(url.searchParams);
@@ -206,12 +205,9 @@ export class RestorationStateHandler
206
205
  '',
207
206
  url
208
207
  );
209
-
210
- console.log('persisted', window.location.href, state);
211
208
  }
212
209
 
213
210
  private loadQueryStateFromUrl(): RestorationState {
214
- console.log('loading...', window.location.href);
215
211
  const url = new URL(window.location.href);
216
212
  const searchInside = url.searchParams.get('sin');
217
213
  const pageNumber = url.searchParams.get('page');
@@ -237,14 +233,9 @@ export class RestorationStateHandler
237
233
  restorationState.baseQuery = legacySearchQuery;
238
234
  }
239
235
 
240
- switch (searchInside) {
241
- // Eventually there will be TV/Radio search types here too.
242
- case 'TXT':
243
- restorationState.searchType = SearchType.FULLTEXT;
244
- break;
245
- default:
246
- restorationState.searchType = SearchType.METADATA;
247
- break;
236
+ if (searchInside) {
237
+ restorationState.searchType =
238
+ searchInside === 'TXT' ? SearchType.FULLTEXT : SearchType.METADATA;
248
239
  }
249
240
 
250
241
  if (pageNumber) {
@@ -345,7 +336,6 @@ export class RestorationStateHandler
345
336
  });
346
337
  }
347
338
 
348
- console.log('loaded', window.location.href, restorationState);
349
339
  return restorationState;
350
340
  }
351
341
 
@@ -19,6 +19,7 @@ import { MockAnalyticsHandler } from './mocks/mock-analytics-handler';
19
19
  import { analyticsCategories } from '../src/utils/analytics-events';
20
20
  import type { TileDispatcher } from '../src/tiles/tile-dispatcher';
21
21
  import type { CollectionFacets } from '../src/collection-facets';
22
+ import type { EmptyPlaceholder } from '../src/empty-placeholder';
22
23
 
23
24
  /**
24
25
  * Wait for the next tick of the event loop.
@@ -278,7 +279,7 @@ describe('Collection Browser', () => {
278
279
  ).to.contains('Results');
279
280
  });
280
281
 
281
- it('can change search type', async () => {
282
+ it('can request a search when changing search type', async () => {
282
283
  const searchService = new MockSearchService();
283
284
  const el = await fixture<CollectionBrowser>(
284
285
  html`<collection-browser .searchService=${searchService}>
@@ -289,6 +290,7 @@ describe('Collection Browser', () => {
289
290
  await el.updateComplete;
290
291
 
291
292
  el.searchType = SearchType.FULLTEXT;
293
+ el.requestSearch();
292
294
  await el.updateComplete;
293
295
  await nextTick();
294
296
 
@@ -499,6 +501,59 @@ describe('Collection Browser', () => {
499
501
  expect(searchService.searchType).to.equal(SearchType.FULLTEXT);
500
502
  });
501
503
 
504
+ it('trims queries of leading/trailing whitespace', async () => {
505
+ const searchService = new MockSearchService();
506
+ const el = await fixture<CollectionBrowser>(
507
+ html`<collection-browser
508
+ .searchService=${searchService}
509
+ ></collection-browser>`
510
+ );
511
+
512
+ el.baseQuery = ' collection:foo ';
513
+ await el.updateComplete;
514
+
515
+ expect(searchService.searchParams?.query).to.equal('collection:foo');
516
+ });
517
+
518
+ it('shows error message when error response received', async () => {
519
+ const searchService = new MockSearchService();
520
+ const el = await fixture<CollectionBrowser>(
521
+ html`<collection-browser
522
+ .searchService=${searchService}
523
+ ></collection-browser>`
524
+ );
525
+
526
+ el.baseQuery = 'error';
527
+ await el.updateComplete;
528
+ await nextTick();
529
+
530
+ const errorPlaceholder = el.shadowRoot?.querySelector(
531
+ 'empty-placeholder'
532
+ ) as EmptyPlaceholder;
533
+ const errorDetails = errorPlaceholder?.shadowRoot?.querySelector(
534
+ '.error-details'
535
+ ) as HTMLParagraphElement;
536
+
537
+ expect(errorDetails).to.exist;
538
+ expect(errorDetails.textContent).to.contain('foo');
539
+ });
540
+
541
+ it('reports malformed response errors to Sentry', async () => {
542
+ const sentrySpy = sinon.spy();
543
+ (window as any).Sentry = { captureMessage: sentrySpy };
544
+ const searchService = new MockSearchService();
545
+ const el = await fixture<CollectionBrowser>(
546
+ html`<collection-browser
547
+ .searchService=${searchService}
548
+ ></collection-browser>`
549
+ );
550
+
551
+ el.baseQuery = 'malformed';
552
+ await el.updateComplete;
553
+
554
+ expect(sentrySpy.callCount).to.be.greaterThanOrEqual(1);
555
+ });
556
+
502
557
  it('queries for collection names after a fetch', async () => {
503
558
  const searchService = new MockSearchService();
504
559
  const collectionNameCache = new MockCollectionNameCache();
@@ -561,12 +616,16 @@ describe('Collection Browser', () => {
561
616
  el.sortParam = { field: 'foo', direction: 'asc' };
562
617
  await el.updateComplete;
563
618
 
564
- const fetchPromise = el.fetchPage(2);
619
+ // We want to spy exclusively on the first set of results, not the second
620
+ searchService.asyncResponse = false;
621
+ searchService.resultsSpy = () => {};
622
+
565
623
  el.sortParam = { field: 'foo', direction: 'desc' };
566
- await fetchPromise;
624
+ await el.updateComplete;
625
+ await nextTick();
567
626
 
568
627
  // If the different sort param causes the results to be discarded,
569
- // the results array should never be read.
628
+ // the first results array should never be read.
570
629
  expect(resultsSpy.callCount).to.equal(0);
571
630
  });
572
631
 
@@ -585,12 +644,16 @@ describe('Collection Browser', () => {
585
644
  el.baseQuery = 'single-result';
586
645
  await el.updateComplete;
587
646
 
588
- const fetchPromise = el.fetchPage(2);
647
+ // We want to spy exclusively on the first set of results, not the second
648
+ searchService.asyncResponse = false;
649
+ searchService.resultsSpy = () => {};
650
+
589
651
  el.sortParam = { field: 'foo', direction: 'asc' };
590
- await fetchPromise;
652
+ await el.updateComplete;
653
+ await nextTick();
591
654
 
592
655
  // If the different sort param causes the results to be discarded,
593
- // the results array should never be read.
656
+ // the first results array should never be read.
594
657
  expect(resultsSpy.callCount).to.equal(0);
595
658
  });
596
659
 
@@ -607,14 +670,19 @@ describe('Collection Browser', () => {
607
670
  );
608
671
 
609
672
  el.baseQuery = 'with-sort';
673
+ el.sortParam = { field: 'foo', direction: 'asc' };
610
674
  await el.updateComplete;
611
675
 
612
- const fetchPromise = el.fetchPage(2);
676
+ // We want to spy exclusively on the first set of results, not the second
677
+ searchService.asyncResponse = false;
678
+ searchService.resultsSpy = () => {};
679
+
613
680
  el.sortParam = null;
614
- await fetchPromise;
681
+ await el.updateComplete;
682
+ await nextTick();
615
683
 
616
684
  // If the different sort param causes the results to be discarded,
617
- // the results array should never be read.
685
+ // the first results array should never be read.
618
686
  expect(resultsSpy.callCount).to.equal(0);
619
687
  });
620
688
 
@@ -801,6 +869,37 @@ describe('Collection Browser', () => {
801
869
  infiniteScroller.scrollToCell = oldScrollToCell;
802
870
  });
803
871
 
872
+ it('shows mobile facets in mobile view', async () => {
873
+ const searchService = new MockSearchService();
874
+ const el = await fixture<CollectionBrowser>(
875
+ html`<collection-browser
876
+ .searchService=${searchService}
877
+ .mobileBreakpoint=${9999}
878
+ ></collection-browser>`
879
+ );
880
+
881
+ el.baseQuery = 'collection:foo';
882
+ await el.updateComplete;
883
+
884
+ const contentContainer = el.shadowRoot?.querySelector(
885
+ '#content-container'
886
+ ) as HTMLElement;
887
+
888
+ el.handleResize({
889
+ target: contentContainer,
890
+ contentRect: contentContainer.getBoundingClientRect(),
891
+ borderBoxSize: [],
892
+ contentBoxSize: [],
893
+ devicePixelContentBoxSize: [],
894
+ });
895
+ await el.updateComplete;
896
+
897
+ const mobileFacets = el.shadowRoot?.querySelector(
898
+ '#mobile-filter-collapse'
899
+ );
900
+ expect(mobileFacets).to.exist;
901
+ });
902
+
804
903
  it('refreshes when certain properties change - with some analytics event sampling', async () => {
805
904
  const mockAnalyticsHandler = new MockAnalyticsHandler();
806
905
  const searchService = new MockSearchService();
@@ -5,11 +5,12 @@ import {
5
5
  SearchResponse,
6
6
  SearchServiceError,
7
7
  } from '@internetarchive/search-service';
8
+ import { SearchServiceErrorType } from '@internetarchive/search-service/dist/src/search-service-error';
8
9
 
9
- export const mockSuccessSingleResult: Result<
10
+ export const getMockSuccessSingleResult: () => Result<
10
11
  SearchResponse,
11
12
  SearchServiceError
12
- > = {
13
+ > = () => ({
13
14
  success: {
14
15
  request: {
15
16
  clientParameters: {
@@ -39,12 +40,12 @@ export const mockSuccessSingleResult: Result<
39
40
  query_time: 0,
40
41
  },
41
42
  },
42
- };
43
+ });
43
44
 
44
- export const mockSuccessWithYearHistogramAggs: Result<
45
+ export const getMockSuccessWithYearHistogramAggs: () => Result<
45
46
  SearchResponse,
46
47
  SearchServiceError
47
- > = {
48
+ > = () => ({
48
49
  success: {
49
50
  request: {
50
51
  clientParameters: {
@@ -84,12 +85,12 @@ export const mockSuccessWithYearHistogramAggs: Result<
84
85
  query_time: 0,
85
86
  },
86
87
  },
87
- };
88
+ });
88
89
 
89
- export const mockSuccessLoggedInResult: Result<
90
+ export const getMockSuccessLoggedInResult: () => Result<
90
91
  SearchResponse,
91
92
  SearchServiceError
92
- > = {
93
+ > = () => ({
93
94
  success: {
94
95
  request: {
95
96
  clientParameters: {
@@ -121,12 +122,12 @@ export const mockSuccessLoggedInResult: Result<
121
122
  query_time: 0,
122
123
  },
123
124
  },
124
- };
125
+ });
125
126
 
126
- export const mockSuccessNoPreviewResult: Result<
127
+ export const getMockSuccessNoPreviewResult: () => Result<
127
128
  SearchResponse,
128
129
  SearchServiceError
129
- > = {
130
+ > = () => ({
130
131
  success: {
131
132
  request: {
132
133
  clientParameters: {
@@ -156,12 +157,12 @@ export const mockSuccessNoPreviewResult: Result<
156
157
  query_time: 0,
157
158
  },
158
159
  },
159
- };
160
+ });
160
161
 
161
- export const mockSuccessLoggedInAndNoPreviewResult: Result<
162
+ export const getMockSuccessLoggedInAndNoPreviewResult: () => Result<
162
163
  SearchResponse,
163
164
  SearchServiceError
164
- > = {
165
+ > = () => ({
165
166
  success: {
166
167
  request: {
167
168
  clientParameters: {
@@ -191,12 +192,12 @@ export const mockSuccessLoggedInAndNoPreviewResult: Result<
191
192
  query_time: 0,
192
193
  },
193
194
  },
194
- };
195
+ });
195
196
 
196
- export const mockSuccessFirstTitleResult: Result<
197
+ export const getMockSuccessFirstTitleResult: () => Result<
197
198
  SearchResponse,
198
199
  SearchServiceError
199
- > = {
200
+ > = () => ({
200
201
  success: {
201
202
  request: {
202
203
  clientParameters: {
@@ -230,12 +231,12 @@ export const mockSuccessFirstTitleResult: Result<
230
231
  query_time: 0,
231
232
  },
232
233
  },
233
- };
234
+ });
234
235
 
235
- export const mockSuccessFirstCreatorResult: Result<
236
+ export const getMockSuccessFirstCreatorResult: () => Result<
236
237
  SearchResponse,
237
238
  SearchServiceError
238
- > = {
239
+ > = () => ({
239
240
  success: {
240
241
  request: {
241
242
  clientParameters: {
@@ -269,7 +270,7 @@ export const mockSuccessFirstCreatorResult: Result<
269
270
  query_time: 0,
270
271
  },
271
272
  },
272
- };
273
+ });
273
274
 
274
275
  export const getMockSuccessSingleResultWithSort: (
275
276
  resultsSpy: Function
@@ -308,10 +309,10 @@ export const getMockSuccessSingleResultWithSort: (
308
309
  },
309
310
  });
310
311
 
311
- export const mockSuccessMultipleResults: Result<
312
+ export const getMockSuccessMultipleResults: () => Result<
312
313
  SearchResponse,
313
314
  SearchServiceError
314
- > = {
315
+ > = () => ({
315
316
  success: {
316
317
  request: {
317
318
  clientParameters: {
@@ -347,12 +348,12 @@ export const mockSuccessMultipleResults: Result<
347
348
  query_time: 0,
348
349
  },
349
350
  },
350
- };
351
+ });
351
352
 
352
- export const mockSuccessMultiLineDescription: Result<
353
+ export const getMockSuccessMultiLineDescription: () => Result<
353
354
  SearchResponse,
354
355
  SearchServiceError
355
- > = {
356
+ > = () => ({
356
357
  success: {
357
358
  request: {
358
359
  clientParameters: {
@@ -383,4 +384,18 @@ export const mockSuccessMultiLineDescription: Result<
383
384
  query_time: 0,
384
385
  },
385
386
  },
386
- };
387
+ });
388
+
389
+ export const getMockErrorResult: () => Result<
390
+ SearchResponse,
391
+ SearchServiceError
392
+ > = () => ({
393
+ error: new SearchServiceError(SearchServiceErrorType.networkError, 'foo', {
394
+ message: 'bar',
395
+ }),
396
+ });
397
+
398
+ export const getMockMalformedResult: () => Result<
399
+ SearchResponse,
400
+ SearchServiceError
401
+ > = () => ({});