Files

100 lines
3.3 KiB
TypeScript

import { EditorSelection } from '@codemirror/state';
import createTestEditor from '../testing/createTestEditor';
import { getSearchQuery, openSearchPanel, SearchQuery, setSearchQuery } from '@codemirror/search';
import { EditorView } from '@codemirror/view';
import searchExtension from './searchExtension';
import createEditorSettings from '../../testing/createEditorSettings';
import { Second } from '@joplin/utils/time';
const setSearchText = (text: string, view: EditorView) => {
const oldQuery = getSearchQuery(view.state);
const query = new SearchQuery({
search: text,
caseSensitive: oldQuery.caseSensitive,
regexp: oldQuery.regexp,
replace: oldQuery.replace,
});
view.dispatch({
effects: [
setSearchQuery.of(query),
],
});
};
const setSearchTextAndWait = async (text: string, view: EditorView) => {
setSearchText(text, view);
await jest.advanceTimersByTimeAsync(Second);
};
const createEditor = async (initialText: string, cursorPosition: number) => {
const view = await createTestEditor(initialText, EditorSelection.cursor(cursorPosition), [], [
searchExtension(()=>{}, createEditorSettings(1)),
]);
openSearchPanel(view);
return view;
};
const getSelectionFrom = (view: EditorView) => {
return view.state.selection.main.from;
};
describe('searchExtension', () => {
beforeEach(() => {
jest.useFakeTimers();
});
test('should auto-scroll to a match when the search changes', async () => {
const view = await createEditor('Line 1\n\nLine 3\nLine 4', 0);
const docText = view.state.doc.toString();
await setSearchTextAndWait('Line 3', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Line 3'));
await setSearchTextAndWait('Line 4', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Line 4'));
});
test('should advance to the next match on change', async () => {
const view = await createEditor('Match\nMatch2\nMatch23', 0);
const docText = view.state.doc.toString();
await setSearchTextAndWait('Match', view);
expect(getSelectionFrom(view)).toBe(0);
await setSearchTextAndWait('Match2', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Match2'));
await setSearchTextAndWait('Match23', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Match23'));
});
test('should preserve auto-match start position until selection is manually changed', async () => {
const view = await createEditor('Match1\nMatch2\nMatch23', 0);
const docText = view.state.doc.toString();
await setSearchTextAndWait('Match2', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Match2'));
await setSearchTextAndWait('Match', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Match'));
await setSearchTextAndWait('Match23', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Match23'));
// Manually setting the selection should change the match start location
view.dispatch({
selection: EditorSelection.single('Match1\n'.length),
});
await setSearchTextAndWait('Match2', view);
expect(getSelectionFrom(view)).toBe(docText.indexOf('Match2'));
});
test('search should wrap to the top when not found after the cursor', async () => {
const view = await createEditor('Before\nOther\nOther 2', 5);
await setSearchTextAndWait('Before', view);
expect(getSelectionFrom(view)).toBe(0);
});
});