Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/__tests__/input/handlers/insert-text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ vi.mock("../../../src/ts/input/input-element", () => ({
const mockState = vi.hoisted(() => ({
activeWordIndex: 0,
correctShiftUsed: true as boolean,
// words that have scrolled off the screen and been removed from the dom
wordsScrolledOff: new Set<number>(),
}));

const nav = vi.hoisted(() => ({
Expand Down Expand Up @@ -94,6 +96,10 @@ vi.mock("../../../src/ts/test/custom-text", () => ({
// peripheral collaborators - none of them feed back into the events we assert
vi.mock("../../../src/ts/test/test-ui", () => ({
afterTestTextInput: vi.fn(),
// words scrolled off the screen are removed from the dom
getWordElement: vi.fn((index: number) =>
mockState.wordsScrolledOff.has(index) ? null : {},
),
pendingWordData: new Map<number, string>(),
}));
vi.mock("../../../src/ts/test/test-logic", () => ({
Expand Down Expand Up @@ -207,6 +213,7 @@ describe("onInsertText - delete on error", () => {
TestWords.reset();
mockState.activeWordIndex = 0;
mockState.correctShiftUsed = true;
mockState.wordsScrolledOff.clear();
setInput("");
replaceConfig({
mode: "words",
Expand Down Expand Up @@ -343,6 +350,20 @@ describe("onInsertText - delete on error", () => {
expect(deletesForWord(0)).toEqual([["deleteContentBackward", 1, ""]]);
});

it("does not regress into a word that scrolled off the screen", async () => {
replaceConfig({ deleteOnError: "letter_hard", stopOnError: "off" });
pushWords("hello", "world");
for (const char of "hello ") await type(char);
mockState.wordsScrolledOff.add(0);

await type("x");

expect(nav.goToPreviousWord).not.toHaveBeenCalled();
expect(mockState.activeWordIndex).toBe(1);
expect(deletesForWord(1)).toEqual([["deleteContentBackward", 1, ""]]);
expect(getInput()).toBe("");
});

it("does not regress on a mistake later in the word", async () => {
replaceConfig({ deleteOnError: "letter_hard", stopOnError: "off" });
pushWords("hello", "world");
Expand Down
1 change: 1 addition & 0 deletions frontend/src/ts/input/handlers/before-insert-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export function onBeforeInsertText(data: string): boolean {
dataIsNotFalsy &&
!Config.blindMode &&
!Config.hideExtraLetters &&
!Config.deleteOnError.includes("hard") &&
inputIsLongerThanOrEqualToWord &&
!goingToNextWord &&
Config.mode !== "zen"
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/ts/input/handlers/insert-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,14 @@ function handleDeleteOnError(now: number): void {
}

//mistake on the first character of the word - the hard modes send you back
if (goBackAWord && inputLength <= 1 && getActiveWordIndex() > 0) {
//but only if the previous word is still in the dom (it might have scrolled
//off), same check as the one a normal backspace does in onBeforeDelete
if (
goBackAWord &&
inputLength <= 1 &&
getActiveWordIndex() > 0 &&
TestUI.getWordElement(getActiveWordIndex() - 1) !== null
) {
//pretend its a normal backspace, not insertText
const inputType: DeleteInputType = deleteWholeWord
? "deleteWordBackward"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "monkeytype",
"version": "26.28.0",
"version": "26.32.0",
"private": true,
"license": "GPL-3.0",
"type": "module",
Expand Down
Loading