mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
Tests: Covering SuggestionsPopup
with tests
This commit is contained in:
parent
ab43c42e53
commit
914aa75a8e
1 changed files with 159 additions and 1 deletions
|
@ -1,8 +1,16 @@
|
||||||
import { fetchMock } from '../../../test/fetch-mock.ts';
|
import { fetchMock } from '../../../test/fetch-mock.ts';
|
||||||
import { fetchLocalAutocomplete, fetchSuggestions, purgeSuggestionsCache } from '../suggestions.ts';
|
import {
|
||||||
|
fetchLocalAutocomplete,
|
||||||
|
fetchSuggestions,
|
||||||
|
purgeSuggestionsCache,
|
||||||
|
SuggestionsPopup,
|
||||||
|
TermSuggestion,
|
||||||
|
} from '../suggestions.ts';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { LocalAutocompleter } from '../local-autocompleter.ts';
|
import { LocalAutocompleter } from '../local-autocompleter.ts';
|
||||||
|
import { afterEach } from 'vitest';
|
||||||
|
import { fireEvent } from '@testing-library/dom';
|
||||||
|
|
||||||
const mockedSuggestionsEndpoint = '/endpoint?term=';
|
const mockedSuggestionsEndpoint = '/endpoint?term=';
|
||||||
const mockedSuggestionsResponse = [
|
const mockedSuggestionsResponse = [
|
||||||
|
@ -13,8 +21,26 @@ const mockedSuggestionsResponse = [
|
||||||
{ label: 'artist:moe (1)', value: 'artist:moe' },
|
{ label: 'artist:moe (1)', value: 'artist:moe' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function mockBaseSuggestionsPopup(includeMockedSuggestions: boolean = false): [SuggestionsPopup, HTMLInputElement] {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
const popup = new SuggestionsPopup();
|
||||||
|
|
||||||
|
document.body.append(input);
|
||||||
|
popup.showForField(input);
|
||||||
|
|
||||||
|
if (includeMockedSuggestions) {
|
||||||
|
popup.renderSuggestions(mockedSuggestionsResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [popup, input];
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedItemClassName = 'autocomplete__item--selected';
|
||||||
|
|
||||||
describe('Suggestions', () => {
|
describe('Suggestions', () => {
|
||||||
let mockedAutocompleteBuffer: ArrayBuffer;
|
let mockedAutocompleteBuffer: ArrayBuffer;
|
||||||
|
let popup: SuggestionsPopup | undefined;
|
||||||
|
let input: HTMLInputElement | undefined;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
fetchMock.enableMocks();
|
fetchMock.enableMocks();
|
||||||
|
@ -33,6 +59,136 @@ describe('Suggestions', () => {
|
||||||
fetchMock.resetMocks();
|
fetchMock.resetMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (input) {
|
||||||
|
input.remove();
|
||||||
|
input = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popup) {
|
||||||
|
popup.hide();
|
||||||
|
popup = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SuggestionsPopup', () => {
|
||||||
|
it('should create the popup container', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup();
|
||||||
|
|
||||||
|
expect(document.querySelector('.autocomplete')).toBeInstanceOf(HTMLElement);
|
||||||
|
expect(popup.isActive).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be removed when hidden', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup();
|
||||||
|
|
||||||
|
popup.hide();
|
||||||
|
|
||||||
|
expect(document.querySelector('.autocomplete')).not.toBeInstanceOf(HTMLElement);
|
||||||
|
expect(popup.isActive).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render suggestions', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
expect(document.querySelectorAll('.autocomplete__item').length).toBe(mockedSuggestionsResponse.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initially select first element when selectNext called', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
popup.selectNext();
|
||||||
|
|
||||||
|
expect(document.querySelector('.autocomplete__item:first-child')).toHaveClass(selectedItemClassName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initially select last element when selectPrevious called', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
popup.selectPrevious();
|
||||||
|
|
||||||
|
expect(document.querySelector('.autocomplete__item:last-child')).toHaveClass(selectedItemClassName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select and de-select items when hovering items over', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
const firstItem = document.querySelector('.autocomplete__item:first-child');
|
||||||
|
const lastItem = document.querySelector('.autocomplete__item:last-child');
|
||||||
|
|
||||||
|
if (firstItem) {
|
||||||
|
fireEvent.mouseOver(firstItem);
|
||||||
|
fireEvent.mouseMove(firstItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(firstItem).toHaveClass(selectedItemClassName);
|
||||||
|
|
||||||
|
if (lastItem) {
|
||||||
|
fireEvent.mouseOver(lastItem);
|
||||||
|
fireEvent.mouseMove(lastItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(firstItem).not.toHaveClass(selectedItemClassName);
|
||||||
|
expect(lastItem).toHaveClass(selectedItemClassName);
|
||||||
|
|
||||||
|
if (lastItem) {
|
||||||
|
fireEvent.mouseOut(lastItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(lastItem).not.toHaveClass(selectedItemClassName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow switching between mouse and selection', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
const secondItem = document.querySelector('.autocomplete__item:nth-child(2)');
|
||||||
|
const thirdItem = document.querySelector('.autocomplete__item:nth-child(3)');
|
||||||
|
|
||||||
|
if (secondItem) {
|
||||||
|
fireEvent.mouseOver(secondItem);
|
||||||
|
fireEvent.mouseMove(secondItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(secondItem).toHaveClass(selectedItemClassName);
|
||||||
|
|
||||||
|
popup.selectNext();
|
||||||
|
|
||||||
|
expect(secondItem).not.toHaveClass(selectedItemClassName);
|
||||||
|
expect(thirdItem).toHaveClass(selectedItemClassName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return selected item value', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
expect(popup.selectedTerm).toBe(null);
|
||||||
|
|
||||||
|
popup.selectNext();
|
||||||
|
|
||||||
|
expect(popup.selectedTerm).toBe(mockedSuggestionsResponse[0].value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit an event when item was clicked with mouse', () => {
|
||||||
|
[popup, input] = mockBaseSuggestionsPopup(true);
|
||||||
|
|
||||||
|
let clickEvent: CustomEvent<TermSuggestion> | undefined;
|
||||||
|
|
||||||
|
const itemSelectedHandler = vi.fn((event: CustomEvent<TermSuggestion>) => {
|
||||||
|
clickEvent = event;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.onItemSelected(itemSelectedHandler);
|
||||||
|
|
||||||
|
const firstItem = document.querySelector('.autocomplete__item');
|
||||||
|
|
||||||
|
if (firstItem) {
|
||||||
|
fireEvent.click(firstItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(itemSelectedHandler).toBeCalledTimes(1);
|
||||||
|
expect(clickEvent?.detail).toEqual(mockedSuggestionsResponse[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('fetchSuggestions', () => {
|
describe('fetchSuggestions', () => {
|
||||||
it('should only call fetch once per single term', () => {
|
it('should only call fetch once per single term', () => {
|
||||||
fetchSuggestions(mockedSuggestionsEndpoint, 'art');
|
fetchSuggestions(mockedSuggestionsEndpoint, 'art');
|
||||||
|
@ -102,6 +258,8 @@ describe('Suggestions', () => {
|
||||||
|
|
||||||
describe('fetchLocalAutocomplete', () => {
|
describe('fetchLocalAutocomplete', () => {
|
||||||
it('should request binary with date-related cache key', () => {
|
it('should request binary with date-related cache key', () => {
|
||||||
|
fetchMock.mockResolvedValue(new Response(mockedAutocompleteBuffer, { status: 200 }));
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const cacheKey = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDate()}`;
|
const cacheKey = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDate()}`;
|
||||||
const expectedEndpoint = `/autocomplete/compiled?vsn=2&key=${cacheKey}`;
|
const expectedEndpoint = `/autocomplete/compiled?vsn=2&key=${cacheKey}`;
|
||||||
|
|
Loading…
Reference in a new issue