mirror of
https://github.com/philomena-dev/philomena.git
synced 2025-03-23 20:27:14 +01:00
Add integration tests for server-side completions cancellation with Escape
This commit is contained in:
parent
96d32d863d
commit
57781a1aa0
4 changed files with 95 additions and 55 deletions
|
@ -6,7 +6,7 @@ import { fireEvent } from '@testing-library/dom';
|
|||
import { assertNotNull } from '../../utils/assert';
|
||||
import { TextInputElement } from '../input';
|
||||
import store from '../../utils/store';
|
||||
import { GetTagSuggestionsResponse } from 'autocomplete/client';
|
||||
import { GetTagSuggestionsResponse, TagSuggestion } from 'autocomplete/client';
|
||||
|
||||
/**
|
||||
* A reusable test environment for autocompletion tests. Note that it does no
|
||||
|
@ -44,27 +44,30 @@ export class TestContext {
|
|||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
if (url.searchParams.get('term')?.toLowerCase() !== 'mar') {
|
||||
const suggestions: GetTagSuggestionsResponse = { suggestions: [] };
|
||||
return JSON.stringify(suggestions);
|
||||
}
|
||||
const term = url.searchParams.get('term');
|
||||
|
||||
const termLower = assertNotNull(term).toLowerCase();
|
||||
|
||||
const fakeSuggestions: TagSuggestion[] = [
|
||||
{
|
||||
alias: 'marvelous',
|
||||
canonical: 'beautiful',
|
||||
images: 30,
|
||||
},
|
||||
{
|
||||
canonical: 'mare',
|
||||
images: 20,
|
||||
},
|
||||
{
|
||||
canonical: 'market',
|
||||
images: 10,
|
||||
},
|
||||
];
|
||||
|
||||
const suggestions: GetTagSuggestionsResponse = {
|
||||
suggestions: [
|
||||
{
|
||||
alias: 'marvelous',
|
||||
canonical: 'beautiful',
|
||||
images: 30,
|
||||
},
|
||||
{
|
||||
canonical: 'mare',
|
||||
images: 20,
|
||||
},
|
||||
{
|
||||
canonical: 'market',
|
||||
images: 10,
|
||||
},
|
||||
],
|
||||
suggestions: fakeSuggestions.filter(suggestion =>
|
||||
(suggestion.alias || suggestion.canonical).startsWith(termLower),
|
||||
),
|
||||
};
|
||||
|
||||
return JSON.stringify(suggestions);
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import { init } from './context';
|
||||
|
||||
it('ignores the autocompletion results if Escape was pressed', async () => {
|
||||
const ctx = await init();
|
||||
|
||||
await Promise.all([ctx.setInput('mar'), ctx.keyDown('Escape')]);
|
||||
|
||||
// The input must be empty because the user typed `mar` and pressed `Escape` right after that
|
||||
ctx.expectUi().toMatchInlineSnapshot(`
|
||||
{
|
||||
"input": "mar<>",
|
||||
"suggestions": [],
|
||||
}
|
||||
`);
|
||||
|
||||
// First request for the local autocomplete index.
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
await ctx.setInput('mar');
|
||||
|
||||
ctx.expectUi().toMatchInlineSnapshot(`
|
||||
{
|
||||
"input": "mar<>",
|
||||
"suggestions": [
|
||||
"marvelous → beautiful 30",
|
||||
"mare 20",
|
||||
"market 10",
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
||||
// Second request for the server-side suggestions.
|
||||
expect(fetch).toHaveBeenCalledTimes(2);
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
import { init } from '../context';
|
||||
|
||||
it('ignores the autocompletion results if Escape was pressed', async () => {
|
||||
const ctx = await init();
|
||||
|
||||
// First request for the local autocomplete index was done
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
await Promise.all([ctx.setInput('mar'), ctx.keyDown('Escape')]);
|
||||
|
||||
// The input must be empty because the user typed `mar` and pressed `Escape` right after that
|
||||
ctx.expectUi().toMatchInlineSnapshot(`
|
||||
{
|
||||
"input": "mar<>",
|
||||
"suggestions": [],
|
||||
}
|
||||
`);
|
||||
|
||||
// No new requests must've been sent because the input was debounced early
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
await ctx.setInput('mar');
|
||||
|
||||
ctx.expectUi().toMatchInlineSnapshot(`
|
||||
{
|
||||
"input": "mar<>",
|
||||
"suggestions": [
|
||||
"marvelous → beautiful 30",
|
||||
"mare 20",
|
||||
"market 10",
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
||||
// Second request for the server-side suggestions.
|
||||
expect(fetch).toHaveBeenCalledTimes(2);
|
||||
|
||||
ctx.setInput('mare');
|
||||
|
||||
// After 300 milliseconds the debounce threshold is over, and the server-side
|
||||
// completions request is issued.
|
||||
vi.advanceTimersByTime(300);
|
||||
|
||||
await ctx.keyDown('Escape');
|
||||
|
||||
expect(fetch).toHaveBeenCalledTimes(3);
|
||||
|
||||
ctx.expectUi().toMatchInlineSnapshot(`
|
||||
{
|
||||
"input": "mare<>",
|
||||
"suggestions": [],
|
||||
}
|
||||
`);
|
||||
|
||||
ctx.setInput('mare');
|
||||
|
||||
// Make sure that the user gets the results immediately without any debouncing (0 ms)
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
ctx.expectUi().toMatchInlineSnapshot(`
|
||||
{
|
||||
"input": "mare<>",
|
||||
"suggestions": [
|
||||
"mare 20",
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
||||
// The results must come from the cache, so no new fetch calls must have been made
|
||||
expect(fetch).toHaveBeenCalledTimes(3);
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import { init } from './context';
|
||||
import { init } from '../context';
|
||||
|
||||
it('requests server-side autocomplete if local autocomplete returns no results', async () => {
|
||||
const ctx = await init();
|
Loading…
Add table
Reference in a new issue