Skip to content
Draft
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
2 changes: 1 addition & 1 deletion frontend/__tests__/test/events/data.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function timerData(
if (event === "step") {
return { event, timer, drift: 0 };
}
return { event, timer };
return { event, timer, date: 0 };
}

describe("data.ts", () => {
Expand Down
281 changes: 280 additions & 1 deletion frontend/__tests__/test/events/stats.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
getChars,
getWpmHistory,
forceReleaseAllKeys,
getCorrectedWords,
__testing as statsTesting,
} from "../../../src/ts/test/events/stats";
import type {
Expand Down Expand Up @@ -112,7 +113,7 @@ function timer(
if (event === "step") {
return { event, timer: timerVal, drift: 0 };
}
return { event, timer: timerVal };
return { event, timer: timerVal, date: 0 };
}

// Helper: sets up a basic test with timer start, steps at 1s intervals,
Expand Down Expand Up @@ -677,6 +678,284 @@ describe("stats.ts", () => {
});
});

describe("getCorrectedWords", () => {
it("returns input as-is when no corrections made", () => {
logTestEvent("timer", 1000, timer("start", 0));
logTestEvent(
"input",
1100,
input({ charIndex: 0, wordIndex: 0, data: "t" }),
);
logTestEvent(
"input",
1150,
input({ charIndex: 1, wordIndex: 0, data: "e" }),
);
logTestEvent(
"input",
1200,
input({ charIndex: 2, wordIndex: 0, data: "s" }),
);
logTestEvent(
"input",
1250,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);

expect(getCorrectedWords()).toEqual(["test"]);
});

it("returns last deleted char per position (xact -> fact)", () => {
logTestEvent("timer", 1000, timer("start", 0));
// type "xact"
logTestEvent(
"input",
1100,
input({ charIndex: 0, wordIndex: 0, data: "x" }),
);
logTestEvent(
"input",
1150,
input({ charIndex: 1, wordIndex: 0, data: "a" }),
);
logTestEvent(
"input",
1200,
input({ charIndex: 2, wordIndex: 0, data: "c" }),
);
logTestEvent(
"input",
1250,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);
// delete all
logTestEvent("input", 1300, {
charIndex: 3,
wordIndex: 0,
inputType: "deleteContentBackward",
} as InputEventData);
logTestEvent("input", 1350, {
charIndex: 2,
wordIndex: 0,
inputType: "deleteContentBackward",
} as InputEventData);
logTestEvent("input", 1400, {
charIndex: 1,
wordIndex: 0,
inputType: "deleteContentBackward",
} as InputEventData);
logTestEvent("input", 1450, {
charIndex: 0,
wordIndex: 0,
inputType: "deleteContentBackward",
} as InputEventData);
// type "fact"
logTestEvent(
"input",
1500,
input({ charIndex: 0, wordIndex: 0, data: "f" }),
);
logTestEvent(
"input",
1550,
input({ charIndex: 1, wordIndex: 0, data: "a" }),
);
logTestEvent(
"input",
1600,
input({ charIndex: 2, wordIndex: 0, data: "c" }),
);
logTestEvent(
"input",
1650,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);

expect(getCorrectedWords()).toEqual(["xact"]);
});

it("returns last deleted char per position across multiple corrections (xest -> west -> test)", () => {
logTestEvent("timer", 1000, timer("start", 0));
// type "xest"
logTestEvent(
"input",
1100,
input({ charIndex: 0, wordIndex: 0, data: "x" }),
);
logTestEvent(
"input",
1150,
input({ charIndex: 1, wordIndex: 0, data: "e" }),
);
logTestEvent(
"input",
1200,
input({ charIndex: 2, wordIndex: 0, data: "s" }),
);
logTestEvent(
"input",
1250,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);
// delete all
logTestEvent("input", 1300, {
charIndex: 3,
wordIndex: 0,
inputType: "deleteWordBackward",
} as InputEventData);
// type "west"
logTestEvent(
"input",
1400,
input({ charIndex: 0, wordIndex: 0, data: "w" }),
);
logTestEvent(
"input",
1450,
input({ charIndex: 1, wordIndex: 0, data: "e" }),
);
logTestEvent(
"input",
1500,
input({ charIndex: 2, wordIndex: 0, data: "s" }),
);
logTestEvent(
"input",
1550,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);
// delete all
logTestEvent("input", 1600, {
charIndex: 3,
wordIndex: 0,
inputType: "deleteWordBackward",
} as InputEventData);
// type "test"
logTestEvent(
"input",
1700,
input({ charIndex: 0, wordIndex: 0, data: "t" }),
);
logTestEvent(
"input",
1750,
input({ charIndex: 1, wordIndex: 0, data: "e" }),
);
logTestEvent(
"input",
1800,
input({ charIndex: 2, wordIndex: 0, data: "s" }),
);
logTestEvent(
"input",
1850,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);

expect(getCorrectedWords()).toEqual(["west"]);
});

it("handles partial correction (tset -> delete last 2 -> st)", () => {
logTestEvent("timer", 1000, timer("start", 0));
// type "tset"
logTestEvent(
"input",
1100,
input({ charIndex: 0, wordIndex: 0, data: "t" }),
);
logTestEvent(
"input",
1150,
input({ charIndex: 1, wordIndex: 0, data: "s" }),
);
logTestEvent(
"input",
1200,
input({ charIndex: 2, wordIndex: 0, data: "e" }),
);
logTestEvent(
"input",
1250,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);
// delete last 2
logTestEvent("input", 1300, {
charIndex: 3,
wordIndex: 0,
inputType: "deleteContentBackward",
} as InputEventData);
logTestEvent("input", 1350, {
charIndex: 2,
wordIndex: 0,
inputType: "deleteContentBackward",
} as InputEventData);
// type "st"
logTestEvent(
"input",
1400,
input({ charIndex: 2, wordIndex: 0, data: "s" }),
);
logTestEvent(
"input",
1450,
input({ charIndex: 3, wordIndex: 0, data: "t" }),
);

// pos 0: "t" never deleted, pos 1: "s" never deleted, pos 2: "e" deleted, pos 3: "t" deleted
expect(getCorrectedWords()).toEqual(["tset"]);
});

it("handles multiple words", () => {
logTestEvent("timer", 1000, timer("start", 0));
// word 0: type "ab" correctly
logTestEvent(
"input",
1100,
input({ charIndex: 0, wordIndex: 0, data: "a" }),
);
logTestEvent(
"input",
1150,
input({ charIndex: 1, wordIndex: 0, data: "b" }),
);
// word 1: type "xy", delete both, type "zw"
logTestEvent(
"input",
1200,
input({ charIndex: 0, wordIndex: 1, data: "x" }),
);
logTestEvent(
"input",
1250,
input({ charIndex: 1, wordIndex: 1, data: "y" }),
);
logTestEvent("input", 1300, {
charIndex: 1,
wordIndex: 1,
inputType: "deleteContentBackward",
} as InputEventData);
logTestEvent("input", 1350, {
charIndex: 1,
wordIndex: 1,
inputType: "deleteContentBackward",
} as InputEventData);
logTestEvent(
"input",
1400,
input({ charIndex: 0, wordIndex: 1, data: "z" }),
);
logTestEvent(
"input",
1450,
input({ charIndex: 1, wordIndex: 1, data: "w" }),
);

const result = getCorrectedWords();
expect(result[0]).toEqual("ab");
expect(result[1]).toEqual("xy");
});
});

describe("forceReleaseAllKeys", () => {
it("creates synthetic keyup events for pressed keys", () => {
logTestEvent("timer", 1000, timer("start", 0));
Expand Down
35 changes: 17 additions & 18 deletions frontend/src/ts/commandline/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ import { randomizeTheme } from "../controllers/theme-controller";
import { showModal } from "../states/modals";
import {
showErrorNotification,
showSuccessNotification,
clearAllNotifications,
} from "../states/notifications";
import * as VideoAdPopup from "../popups/video-ad-popup";
import * as TestStats from "../test/test-stats";
import { Command, CommandsSubgroup } from "./types";
import { buildCommandForConfigKey } from "./util";
import { CommandlineConfigMetadataObject } from "./commandline-metadata";
Expand Down Expand Up @@ -288,22 +286,23 @@ export const commands: CommandsSubgroup = {
alert(await caches.keys());
},
},
{
id: "copyResultStats",
display: "Copy result stats",
icon: "fa-cog",
visible: false,
exec: async (): Promise<void> => {
navigator.clipboard
.writeText(JSON.stringify(TestStats.getStats()))
.then(() => {
showSuccessNotification("Copied to clipboard");
})
.catch((e: unknown) => {
showErrorNotification("Failed to copy to clipboard", { error: e });
});
},
},
// todo: bring back?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new Todo was discovered. If it is not a priority right now,consider marking it for later attention.
todo: bring back?

// {
// id: "copyResultStats",
// display: "Copy result stats",
// icon: "fa-cog",
// visible: false,
// exec: async (): Promise<void> => {
// navigator.clipboard
// .writeText(JSON.stringify(TestStats.getStats()))
// .then(() => {
// showSuccessNotification("Copied to clipboard");
// })
// .catch((e: unknown) => {
// showErrorNotification("Failed to copy to clipboard", { error: e });
// });
// },
// },
{
id: "fpsCounter",
display: "FPS counter...",
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/ts/commandline/lists/result-screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import {
showErrorNotification,
showSuccessNotification,
} from "../../states/notifications";
import * as TestInput from "../../test/test-input";
import * as TestState from "../../test/test-state";
import * as TestWords from "../../test/test-words";
import { Config } from "../../config/store";
import * as PractiseWords from "../../test/practise-words";
import { Command, CommandsSubgroup } from "../types";
import * as TestScreenshot from "../../test/test-screenshot";
import { getInputHistory } from "../../test/events/stats";

const practiceSubgroup: CommandsSubgroup = {
title: "Practice words...",
Expand Down Expand Up @@ -141,8 +141,8 @@ const commands: Command[] = [
exec: (): void => {
const words = (
Config.mode === "zen"
? TestInput.input.getHistory()
: TestWords.words.list.slice(0, TestInput.input.getHistory().length)
? getInputHistory()
: TestWords.words.list.slice(0, getInputHistory().length)
).join(" ");

navigator.clipboard.writeText(words).then(
Expand Down
Loading
Loading