From ab78f3f50d8ec9a2fa8b3794ceb9a9b1b02fb3a6 Mon Sep 17 00:00:00 2001 From: Philipp Fritsche Date: Thu, 12 May 2022 12:31:21 +0200 Subject: [PATCH] fix: guard against selection without range (#953) --- src/utils/focus/selection.ts | 5 +-- tests/utils/focus/selection.ts | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/utils/focus/selection.ts b/src/utils/focus/selection.ts index c09282c2..2149309d 100644 --- a/src/utils/focus/selection.ts +++ b/src/utils/focus/selection.ts @@ -217,14 +217,13 @@ export function moveSelection(node: Element, direction: -1 | 1) { } else { const selection = node.ownerDocument.getSelection() - /* istanbul ignore if */ - if (!selection) { + if (!selection?.focusNode) { return } if (selection.isCollapsed) { const nextPosition = getNextCursorPosition( - selection.focusNode as Node, + selection.focusNode, selection.focusOffset, direction, ) diff --git a/tests/utils/focus/selection.ts b/tests/utils/focus/selection.ts index 6c09b617..16c68e6b 100644 --- a/tests/utils/focus/selection.ts +++ b/tests/utils/focus/selection.ts @@ -4,6 +4,7 @@ import { setSelection, setSelectionRange, modifySelection, + moveSelection, } from '#src/utils' import {setup} from '#testHelpers' @@ -183,3 +184,78 @@ describe('update selection when moving focus into element with own selection imp expect(document.getSelection()).toHaveProperty('focusOffset', 0) }) }) + +describe('move selection', () => { + test('do nothing without a selection range', () => { + const {element} = setup(`
`) + document.getSelection()?.removeAllRanges() + + moveSelection(element, 1) + + expect(document.getSelection()).toHaveProperty('rangeCount', 0) + }) + + test('move to next cursor position', () => { + const {element} = setup(`
foo
`, { + selection: {focusNode: 'div/text()', focusOffset: 1}, + }) + + moveSelection(element, 1) + + expect(document.getSelection()).toHaveProperty( + 'focusNode', + element.firstChild, + ) + expect(document.getSelection()).toHaveProperty('focusOffset', 2) + }) + + test('move to next cursor position', () => { + const {element} = setup(`
foo
`, { + selection: {focusNode: 'div/text()', focusOffset: 1}, + }) + + moveSelection(element, 1) + + expect(document.getSelection()).toHaveProperty( + 'focusNode', + element.firstChild, + ) + expect(document.getSelection()).toHaveProperty('focusOffset', 2) + expect(document.getSelection()).toHaveProperty('isCollapsed', true) + }) + + test('collapse range', () => { + const {element} = setup(`
foo
`, { + selection: {focusNode: 'div/text()', anchorOffset: 1, focusOffset: 2}, + }) + + moveSelection(element, 1) + + expect(document.getSelection()).toHaveProperty( + 'focusNode', + element.firstChild, + ) + expect(document.getSelection()).toHaveProperty('focusOffset', 2) + expect(document.getSelection()).toHaveProperty('isCollapsed', true) + }) + + test('move cursor in input', () => { + const {element} = setup(``) + + moveSelection(element, 1) + + expect(element).toHaveProperty('selectionStart', 1) + expect(element).toHaveProperty('selectionEnd', 1) + }) + + test('collapse range in input', () => { + const {element} = setup(``, { + selection: {anchorOffset: 1, focusOffset: 2}, + }) + + moveSelection(element, 1) + + expect(element).toHaveProperty('selectionStart', 2) + expect(element).toHaveProperty('selectionEnd', 2) + }) +})