@maxpike/vue
Version:
Vue VariantJS: Fully configurable Vue 3 components styled with TailwindCSS
1,435 lines (1,189 loc) • 38.1 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { nextTick, ref } from 'vue';
import { FetchOptionsFn, MinimumInputLengthTextProp, PreFetchOptionsFn } from '../../types';
import useFetchsOptions from '../../use/useFetchsOptions';
import { useSetup } from './useSetup';
describe('useFetchsOptions', () => {
const localValue = ref<any>(undefined);
const options = ref<string[] | undefined>(['A', 'B']);
const textAttribute = ref<string | undefined>(undefined);
const valueAttribute = ref<string | undefined>(undefined);
const normalize = ref<boolean>(true);
const searchQuery = ref<string | undefined>(undefined);
const fetchFn = ref<FetchOptionsFn | undefined>(undefined);
const prefetchFn = ref<PreFetchOptionsFn | boolean>(false);
const fetchDelay = ref<number | undefined>(undefined);
const fetchMinimumInputLength = ref<number | undefined>(undefined);
const fetchMinimumInputLengthText = ref<MinimumInputLengthTextProp>((minimumInputLength: number): string => `Please enter ${minimumInputLength} or more characters`);
beforeEach(() => {
options.value = ['A', 'B'];
textAttribute.value = undefined;
valueAttribute.value = undefined;
normalize.value = true;
searchQuery.value = undefined;
fetchFn.value = undefined;
fetchDelay.value = undefined;
fetchMinimumInputLength.value = undefined;
fetchMinimumInputLengthText.value = (minimumInputLength: number): string => `Please enter ${minimumInputLength} or more characters`;
});
describe('no fetch function', () => {
it('returns normalized options', () => {
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([
{ raw: 'A', text: 'A', value: 'A' },
{ raw: 'B', text: 'B', value: 'B' },
]);
});
});
it('returns empty array of normalized options if options became undefined', () => {
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
options.value = undefined;
expect(normalizedOptions.value).toEqual([]);
});
});
it('returns flattened options', () => {
useSetup(() => {
const { flattenedOptions } = useFetchsOptions(
localValue,
ref([
{ text: 'A', value: 'A' },
{
text: 'B', value: 'B', children: ['C'],
},
]),
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(flattenedOptions.value).toEqual(
[
{ value: 'A', text: 'A', raw: { text: 'A', value: 'A' } },
{ value: 'C', text: 'C', raw: 'C' },
],
);
});
});
it('handles undefined options', () => {
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
ref(undefined),
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
});
});
it('accepts a custom `textAttribute`', () => {
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
ref([
{ label: 'Letter A', value: 'A' },
{ label: 'Letter B', value: 'B' },
]),
ref('label'),
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([
{ raw: { label: 'Letter A', value: 'A' }, text: 'Letter A', value: 'A' },
{ raw: { label: 'Letter B', value: 'B' }, text: 'Letter B', value: 'B' },
]);
});
});
it('accepts a custom `valueAttribute`', () => {
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
ref([
{ text: 'A', identifier: 'a' },
{ text: 'B', identifier: 'b' },
]),
textAttribute,
ref('identifier'),
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([
{ raw: { text: 'A', identifier: 'a' }, text: 'A', value: 'a' },
{ raw: { text: 'B', identifier: 'b' }, text: 'B', value: 'b' },
]);
});
});
it('doesnt normalize the options if normalize is `false`', () => {
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
ref(false),
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual(options.value);
});
});
describe('with search query', () => {
it('filters by the search query', () => {
options.value = ['Option A', 'Option B', 'Option B2'];
searchQuery.value = 'b ';
useSetup(() => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([
{ raw: 'Option B', text: 'Option B', value: 'Option B' },
{ raw: 'Option B2', text: 'Option B2', value: 'Option B2' },
]);
});
});
});
});
describe('with fetch function', () => {
beforeEach(() => {
options.value = [];
fetchFn.value = () => new Promise((resolve) => resolve({
results: ['A', 'B'],
}));
fetchDelay.value = 0;
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('should emit an error event if the response doesnt have results', () => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
wrong: 'sss',
} as any);
});
useSetup(() => {
const { fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
fetchOptions();
}, {}, {
onFetchOptionsError: (error: any) => {
expect(error).toBeInstanceOf(Error);
expect(error.toString()).toBe('Error: Options response must be an object with `results` property.');
},
});
});
it('should emit an error event if the response results are in an invalid format', () => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
results: 'invalid format',
} as any);
});
useSetup(() => {
const { fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
fetchOptions();
}, {}, {
onFetchOptionsError: (error: any) => {
expect(error).toBeInstanceOf(Error);
expect(error.toString()).toBe('Error: Response.results must be an array or object, got string');
},
});
});
it('returns normalized options', () => {
useSetup(async () => {
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: 'A', text: 'A', value: 'A' },
{ raw: 'B', text: 'B', value: 'B' },
]);
});
});
it('determines that is fetching options is promise is busy', () => {
useSetup(async () => {
jest.useFakeTimers();
fetchFn.value = () => new Promise((resolve) => {
setTimeout(() => {
resolve({
results: ['A', 'B'],
});
}, 10);
});
const { fetchingOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(fetchingOptions.value).toBe(false);
fetchOptions();
await nextTick();
expect(fetchingOptions.value).toBe(true);
jest.advanceTimersByTime(9);
await nextTick();
expect(fetchingOptions.value).toBe(true);
jest.advanceTimersByTime(1);
await nextTick();
expect(fetchingOptions.value).toBe(false);
jest.useRealTimers();
});
});
it('determines if the fetched options have more pages', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: true,
});
});
const { fetchedOptionsHaveMorePages, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
fetchOptions();
await nextTick();
expect(fetchedOptionsHaveMorePages.value).toBe(true);
});
});
it('determines if the fetched options doesnt have more pages', () => {
useSetup(() => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
});
const { fetchedOptionsHaveMorePages, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
fetchOptions();
expect(fetchedOptionsHaveMorePages.value).toBe(false);
});
});
it('determines if the options were fetched', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
});
const { optionsWereFetched, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(optionsWereFetched.value).toBe(false);
fetchOptions();
await nextTick();
expect(optionsWereFetched.value).toBe(true);
});
});
it('resets the optionsWereFetched flag if search query changes', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
});
const { optionsWereFetched, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(optionsWereFetched.value).toBe(false);
fetchOptions();
await nextTick();
expect(optionsWereFetched.value).toBe(true);
searchQuery.value = 'other';
await nextTick();
expect(optionsWereFetched.value).toBe(false);
fetchOptions();
await nextTick();
expect(optionsWereFetched.value).toBe(true);
});
});
it('doesnt resets the optionsWereFetched flag if search query doesnt have enough characters', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
});
fetchMinimumInputLength.value = 3;
const { optionsWereFetched, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(optionsWereFetched.value).toBe(false);
fetchOptions();
await nextTick();
expect(optionsWereFetched.value).toBe(true);
searchQuery.value = 'te';
await nextTick();
expect(optionsWereFetched.value).toBe(true);
searchQuery.value = 'tes';
await nextTick();
expect(optionsWereFetched.value).toBe(false);
});
});
it('doesnt resets the optionsWereFetched flag if no fetchFn', () => {
useSetup(() => {
fetchFn.value = undefined;
const { optionsWereFetched, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(optionsWereFetched.value).toBe(false);
fetchOptions();
expect(optionsWereFetched.value).toBe(false);
});
});
it('handles undefined options', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => resolve({
results: undefined!,
}));
const { fetchOptions, normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: 'A', text: 'A', value: 'A' },
{ raw: 'B', text: 'B', value: 'B' },
]);
});
});
it('accepts a custom `textAttribute`', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => resolve({
results: [
{ label: 'Letter A', value: 'A' },
{ label: 'Letter B', value: 'B' },
],
}));
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
ref('label'),
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: { label: 'Letter A', value: 'A' }, text: 'Letter A', value: 'A' },
{ raw: { label: 'Letter B', value: 'B' }, text: 'Letter B', value: 'B' },
]);
});
});
it('accepts a custom `valueAttribute`', () => {
useSetup(async () => {
fetchFn.value = () => new Promise((resolve) => resolve({
results: [
{ text: 'A', identifier: 'a' },
{ text: 'B', identifier: 'b' },
],
}));
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
ref('identifier'),
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: { text: 'A', identifier: 'a' }, text: 'A', value: 'a' },
{ raw: { text: 'B', identifier: 'b' }, text: 'B', value: 'b' },
]);
});
});
it('doesnt normalize the options if normalize is `false`', () => {
useSetup(async () => {
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
ref(false),
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual(options.value);
});
});
describe('with a custom delay', () => {
beforeEach(() => {
fetchDelay.value = 200;
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('fetchs after 200ms', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
searchQuery.value = 'test ';
useSetup(async () => {
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
jest.advanceTimersByTime(199);
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
jest.advanceTimersByTime(1);
await nextTick();
expect(fetchFunctionMock).toHaveBeenCalled();
});
});
it('throtles the search', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
searchQuery.value = 'test ';
useSetup(async () => {
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
jest.advanceTimersByTime(199);
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
// Since its throttled it will reset the counter
fetchOptions();
jest.advanceTimersByTime(199);
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
jest.advanceTimersByTime(1);
await nextTick();
expect(fetchFunctionMock).toHaveBeenCalled();
});
});
});
describe('with a minimum input length', () => {
beforeEach(() => {
fetchMinimumInputLength.value = 3;
});
it('doesnt fetch if no query', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
useSetup(async () => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
searchQuery.value = '';
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
});
});
it('determines that needsMoreCharsToFetch if query is shorter that the min input length', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
searchQuery.value = 'te';
useSetup(() => {
const { needsMoreCharsToFetch } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(needsMoreCharsToFetch.value).toBe(true);
});
});
it('determines that doesnt needsMoreCharsToFetch if not fetchFn', () => {
fetchFn.value = undefined;
searchQuery.value = 'test';
useSetup(() => {
const { needsMoreCharsToFetch } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(needsMoreCharsToFetch.value).toBe(false);
});
});
it('determines that doesnt needsMoreCharsToFetch if query is exactly the min input length', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
searchQuery.value = 'tes';
useSetup(() => {
const { needsMoreCharsToFetch } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(needsMoreCharsToFetch.value).toBe(false);
});
});
it('doesnt fetch if the query is shorter than the min input length', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
useSetup(async () => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
searchQuery.value = 'te';
await nextTick();
expect(fetchFunctionMock).not.toHaveBeenCalled();
});
});
it('fetchs if the query is exactly the min input length', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
useSetup(async () => {
const { normalizedOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
searchQuery.value = 'tes';
await nextTick();
expect(fetchFunctionMock).toHaveBeenCalled();
});
});
it('fetchs if the query is larger that the min input length', () => {
const fetchFunctionMock = jest.fn().mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
fetchFn.value = fetchFunctionMock;
searchQuery.value = 'te';
useSetup(async () => {
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(fetchFunctionMock).toHaveBeenCalled();
});
});
it('builds the fetch minimum input length text', () => {
useSetup(() => {
const { needsMoreCharsMessage } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(needsMoreCharsMessage.value).toBe('Please enter 3 or more characters');
});
});
it('accepts a string as the minimum input length text', () => {
fetchMinimumInputLengthText.value = 'test';
useSetup(() => {
const { needsMoreCharsMessage } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(needsMoreCharsMessage.value).toBe('test');
});
});
it('pass the minimum input length text and the query to the fetchMinimumInputLengthText function ', () => {
const fetchMinimumInputLengthTextMock = jest.fn().mockImplementation(() => 'test');
fetchMinimumInputLengthText.value = fetchMinimumInputLengthTextMock;
searchQuery.value = 'te';
useSetup(() => {
const { needsMoreCharsMessage } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(needsMoreCharsMessage.value).toBe('test');
expect(fetchMinimumInputLengthTextMock).toHaveBeenCalledWith(3, 'te');
});
});
});
describe('with search query', () => {
it('filters by the search query', () => {
const fetchFunctionMock = jest.fn();
fetchFn.value = fetchFunctionMock.mockImplementation(() => new Promise((resolve) => {
resolve({
results: ['A', 'B'],
hasMorePages: false,
});
}));
searchQuery.value = 'test ';
useSetup(async () => {
const { normalizedOptions, fetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
fetchOptions();
await nextTick();
expect(fetchFunctionMock).toHaveBeenCalledWith('test ');
});
});
});
});
describe('with prefetch function', () => {
beforeEach(() => {
options.value = [];
prefetchFn.value = () => new Promise((resolve) => resolve(
['A', 'B'],
));
});
it('should emit an error event if the results are in an invalid format', () => {
prefetchFn.value = () => new Promise((resolve) => {
resolve('wrong' as any);
});
useSetup(() => {
const { prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
prefetchOptions();
}, {}, {
onFetchOptionsError: (error: any) => {
expect(error).toBeInstanceOf(Error);
expect(error.toString()).toBe('Error: Response must be an array or object, got string');
},
});
});
it('returns normalized options', () => {
useSetup(async () => {
const { normalizedOptions, prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
prefetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: 'A', text: 'A', value: 'A' },
{ raw: 'B', text: 'B', value: 'B' },
]);
});
});
it('determines that is fetching options if promise is busy', () => {
useSetup(async () => {
jest.useFakeTimers();
prefetchFn.value = () => new Promise((resolve) => {
setTimeout(() => {
resolve(['A', 'B']);
}, 10);
});
const { fetchingOptions, prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(fetchingOptions.value).toBe(false);
prefetchOptions();
await nextTick();
expect(fetchingOptions.value).toBe(true);
jest.advanceTimersByTime(9);
await nextTick();
expect(fetchingOptions.value).toBe(true);
jest.advanceTimersByTime(1);
await nextTick();
expect(fetchingOptions.value).toBe(false);
jest.useRealTimers();
});
});
it('determines if the options were fetched', () => {
useSetup(async () => {
prefetchFn.value = () => new Promise((resolve) => {
resolve(['A', 'B']);
});
const { optionsWereFetched, prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(optionsWereFetched.value).toBe(false);
prefetchOptions();
await nextTick();
expect(optionsWereFetched.value).toBe(true);
});
});
it('doesnt resets the optionsWereFetched flag if no fetchFn', () => {
useSetup(() => {
prefetchFn.value = false;
const { optionsWereFetched, prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(optionsWereFetched.value).toBe(false);
prefetchOptions();
expect(optionsWereFetched.value).toBe(false);
});
});
it('accepts a custom `textAttribute`', () => {
useSetup(async () => {
prefetchFn.value = () => new Promise((resolve) => resolve([
{ label: 'Letter A', value: 'A' },
{ label: 'Letter B', value: 'B' },
]));
const { normalizedOptions, prefetchOptions } = useFetchsOptions(
localValue,
options,
ref('label'),
valueAttribute,
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
prefetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: { label: 'Letter A', value: 'A' }, text: 'Letter A', value: 'A' },
{ raw: { label: 'Letter B', value: 'B' }, text: 'Letter B', value: 'B' },
]);
});
});
it('accepts a custom `valueAttribute`', () => {
useSetup(async () => {
prefetchFn.value = () => new Promise((resolve) => resolve([
{ text: 'A', identifier: 'a' },
{ text: 'B', identifier: 'b' },
]));
const { normalizedOptions, prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
ref('identifier'),
normalize,
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
prefetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual([
{ raw: { text: 'A', identifier: 'a' }, text: 'A', value: 'a' },
{ raw: { text: 'B', identifier: 'b' }, text: 'B', value: 'b' },
]);
});
});
it('doesnt normalize the options if normalize is `false`', () => {
useSetup(async () => {
const { normalizedOptions, prefetchOptions } = useFetchsOptions(
localValue,
options,
textAttribute,
valueAttribute,
ref(false),
searchQuery,
fetchFn,
prefetchFn,
fetchDelay,
fetchMinimumInputLength,
fetchMinimumInputLengthText,
);
expect(normalizedOptions.value).toEqual([]);
prefetchOptions();
await nextTick();
expect(normalizedOptions.value).toEqual(options.value);
});
});
});
});