Skip to content

Commit e019b4d

Browse files
authored
fix(core): trigger invariant when user doesn't return anything from getItems (#607)
* fix: trigger invariant when user doesn't return anything from getItems * tests: test invariant when not returning from getItems * fix: remove pre-resolve invariant * feat: check that every item is truthy in post-resolve invariant * fix: rmeove unused dependency * feat: add sourceId in error message * feat: use second invariant for undefined items * feat: add link to docs in invariant
1 parent 760f8e7 commit e019b4d

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

packages/autocomplete-core/src/__tests__/getSources.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
runAllMicroTasks,
77
} from '../../../../test/utils';
88
import { createAutocomplete } from '../createAutocomplete';
9+
import * as handlers from '../onInput';
910

1011
describe('getSources', () => {
1112
test('gets calls on input', () => {
@@ -110,4 +111,25 @@ describe('getSources', () => {
110111

111112
expect(onStateChange.mock.calls.pop()[0].state.collections).toHaveLength(2);
112113
});
114+
115+
test('with nothing returned from getItems throws', async () => {
116+
const spy = jest.spyOn(handlers, 'onInput');
117+
118+
const { inputElement } = createPlayground(createAutocomplete, {
119+
getSources() {
120+
return [createSource({ sourceId: 'source1', getItems: () => {} })];
121+
},
122+
});
123+
124+
await expect(() => {
125+
inputElement.focus();
126+
userEvent.type(inputElement, 'a');
127+
128+
return spy.mock.results[0].value;
129+
}).rejects.toThrow(
130+
'[Autocomplete] The `getItems` function from source "source1" must return an array of items but returned undefined.\n\nDid you forget to return items?\n\nSee: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems'
131+
);
132+
133+
spy.mockRestore();
134+
});
113135
});

packages/autocomplete-core/src/resolve.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function isDescription<TItem extends BaseItem>(
2727
function isRequesterDescription<TItem extends BaseItem>(
2828
description: TItem[] | TItem[][] | RequesterDescription<TItem>
2929
): description is RequesterDescription<TItem> {
30-
return Boolean((description as RequesterDescription<TItem>).execute);
30+
return Boolean((description as RequesterDescription<TItem>)?.execute);
3131
}
3232

3333
type PackedDescription<TItem extends BaseItem> = {
@@ -172,9 +172,26 @@ export function postResolve<TItem extends BaseItem>(
172172

173173
invariant(
174174
Array.isArray(items),
175-
`The \`getItems\` function must return an array of items but returned type ${JSON.stringify(
175+
`The \`getItems\` function from source "${
176+
source.sourceId
177+
}" must return an array of items but returned type ${JSON.stringify(
176178
typeof items
177-
)}:\n\n${JSON.stringify(items, null, 2)}`
179+
)}:\n\n${JSON.stringify(items, null, 2)}.
180+
181+
See: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems`
182+
);
183+
184+
invariant(
185+
(items as Array<typeof items>).every(Boolean),
186+
`The \`getItems\` function from source "${
187+
source.sourceId
188+
}" must return an array of items but returned ${JSON.stringify(
189+
undefined
190+
)}.
191+
192+
Did you forget to return items?
193+
194+
See: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems`
178195
);
179196

180197
return {

0 commit comments

Comments
 (0)