From 61377c8e7dbbc8072dd6bedca299a4ccf51836d4 Mon Sep 17 00:00:00 2001 From: Alex MacArthur Date: Tue, 31 Aug 2021 22:25:26 -0500 Subject: [PATCH] Fix bug caused by passing move() value that's out of range. --- __tests__/chainable-methods/move.test.js | 47 +++++++++++++++++++ .../repositionCursor.test.js.snap | 2 - .../helpers/calculateCursorSteps.test.js | 25 ++++++++++ __tests__/helpers/repositionCursor.test.js | 6 --- package.json | 2 +- src/TypeIt.ts | 8 +++- src/helpers/repositionCursor.ts | 8 +--- src/helpers/updateCursorPosition.ts | 12 +++++ 8 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 src/helpers/updateCursorPosition.ts diff --git a/__tests__/chainable-methods/move.test.js b/__tests__/chainable-methods/move.test.js index 676b8355..457e4c24 100644 --- a/__tests__/chainable-methods/move.test.js +++ b/__tests__/chainable-methods/move.test.js @@ -1,5 +1,6 @@ import TypeIt from "../../src/TypeIt"; import * as wait from "../../src/helpers/wait"; +import * as repositionCursor from "../../src/helpers/repositionCursor"; beforeEach(() => { jest.clearAllMocks(); @@ -68,3 +69,49 @@ describe("timeouts fire correctly", () => { .go(); }); }); + +describe("moves only within range", () => { + let repositionCursorSpy; + + beforeEach(() => { + setHTML`
+ +
`; + + repositionCursorSpy = jest.spyOn(repositionCursor, "default"); + }); + + it("bottom end of range", (done) => { + new TypeIt("#element", { + speed: 0, + afterComplete: () => { + expect(repositionCursorSpy.mock.calls).toEqual([ + [expect.anything(), expect.anything(), expect.anything(), 0], + [expect.anything(), expect.anything(), expect.anything(), 0], + ]); + done(); + }, + }) + .type("Hi!") + .move(2) // Number of steps is out of range of printed characters. + .go(); + }); + + it("top end of range", (done) => { + new TypeIt("#element", { + speed: 0, + afterComplete: () => { + expect(repositionCursorSpy.mock.calls).toEqual([ + [expect.anything(), expect.anything(), expect.anything(), 1], + [expect.anything(), expect.anything(), expect.anything(), 2], + [expect.anything(), expect.anything(), expect.anything(), 3], + [expect.anything(), expect.anything(), expect.anything(), 3], + ]); + done(); + }, + }) + .type("Hi!") + .move(-4) // Number of steps is out of range of printed characters. + .go(); + }); +}); diff --git a/__tests__/helpers/__snapshots__/repositionCursor.test.js.snap b/__tests__/helpers/__snapshots__/repositionCursor.test.js.snap index cded61b6..69a67b50 100644 --- a/__tests__/helpers/__snapshots__/repositionCursor.test.js.snap +++ b/__tests__/helpers/__snapshots__/repositionCursor.test.js.snap @@ -6,6 +6,4 @@ exports[`Moves cursor three back and two forward. 1`] = `"1234 exports[`Moves cursor three steps back. 1`] = `"12|345"`; -exports[`Stops moving when at beginning of string. 1`] = `"|12345"`; - exports[`Stops moving when at end of string. 1`] = `"12345|"`; diff --git a/__tests__/helpers/calculateCursorSteps.test.js b/__tests__/helpers/calculateCursorSteps.test.js index f10acc71..c1310b99 100644 --- a/__tests__/helpers/calculateCursorSteps.test.js +++ b/__tests__/helpers/calculateCursorSteps.test.js @@ -192,3 +192,28 @@ describe("string has nested HTML", () => { expect(result).toEqual(19); }); }); + +describe("arg is outside bounds", () => { + it("returns zero when overshooting back", async () => { + const el = document.querySelector("#el"); + + await new Promise((resolve) => { + new TypeIt(el, { + strings: "Hi, Bob!", + speed: 0, + afterComplete: () => { + return resolve(); + }, + }).go(); + }); + + const result = calculateCursorSteps({ + el, + move: null, + cursorPos: 8, + to: "end", + }); + + expect(result).toEqual(-8); + }); +}); diff --git a/__tests__/helpers/repositionCursor.test.js b/__tests__/helpers/repositionCursor.test.js index 50517ab2..7ce642b1 100644 --- a/__tests__/helpers/repositionCursor.test.js +++ b/__tests__/helpers/repositionCursor.test.js @@ -38,12 +38,6 @@ test("Moves cursor three back and two forward.", () => { expect(document.body.innerHTML).toMatchSnapshot(); }); -test("Stops moving when at beginning of string.", () => { - repositionCursor(element, allCharacters, cursor, 100); - - expect(document.body.innerHTML).toMatchSnapshot(); -}); - test("Stops moving when at end of string.", () => { repositionCursor(element, allCharacters, cursor, -100); diff --git a/package.json b/package.json index 7f4b501c..3bebde66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typeit", - "version": "8.0.1", + "version": "8.0.2", "description": "The most versatile animated typing utility on the planet.", "author": "Alex MacArthur (https://macarthur.me)", "license": "GPL-3.0", diff --git a/src/TypeIt.ts b/src/TypeIt.ts index 18d29db4..d8c649fb 100644 --- a/src/TypeIt.ts +++ b/src/TypeIt.ts @@ -19,6 +19,7 @@ import handleFunctionalArg from "./helpers/handleFunctionalArg"; import isNumber from "./helpers/isNumber"; import insertIntoElement from "./helpers/insertIntoElement"; import isInput from "./helpers/isInput"; +import updateCursorPosition from "./helpers/updateCursorPosition"; import merge from "./helpers/merge"; import removeNode from "./helpers/removeNode"; import removeEmptyElements from "./helpers/removeEmptyElements"; @@ -296,7 +297,12 @@ export default function TypeIt( }); let moveCursor = () => { - _cursorPosition += numberOfSteps < 0 ? -1 : 1; + _cursorPosition = updateCursorPosition( + numberOfSteps < 0 ? -1 : 1, + _cursorPosition, + _getAllChars() + ); + repositionCursor(_element, _getAllChars(), _cursor, _cursorPosition); }; diff --git a/src/helpers/repositionCursor.ts b/src/helpers/repositionCursor.ts index 70fafc23..52bf4a22 100644 --- a/src/helpers/repositionCursor.ts +++ b/src/helpers/repositionCursor.ts @@ -2,13 +2,9 @@ export default ( element: Node, allChars: any[], cursor: Node, - cursorPosition: number + newCursorPosition: number ): void => { - // Guarantee that the new cursor position is never greater than - // the number of characters we're dealing with. - let characterIndex = Math.min(cursorPosition, allChars.length); - - let nodeToInsertBefore = allChars[characterIndex - 1]; + let nodeToInsertBefore = allChars[newCursorPosition - 1]; element = nodeToInsertBefore?.parentNode || element; diff --git a/src/helpers/updateCursorPosition.ts b/src/helpers/updateCursorPosition.ts new file mode 100644 index 00000000..8d755898 --- /dev/null +++ b/src/helpers/updateCursorPosition.ts @@ -0,0 +1,12 @@ +const updateCursorPosition = ( + steps: number, + cursorPosition: number, + printedCharacters: Element[] +) => { + return Math.min( + Math.max(cursorPosition + steps, 0), + printedCharacters.length + ); +}; + +export default updateCursorPosition;