Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8a6fcdb
yeet wpm history, raw history, burst history, old burst calculation
Miodec May 30, 2026
ee43b07
burst improve perf
Miodec May 30, 2026
4f6f87c
yeet keypress timings
Miodec May 30, 2026
99a7953
yeet keypresscounthistory
Miodec May 30, 2026
c2bb661
yeet afk history
Miodec May 30, 2026
1dae6e4
missing after last yeet
Miodec May 30, 2026
1e01096
yeet error history
Miodec May 30, 2026
5d3bd07
fix word highlight
Miodec May 30, 2026
709e2ba
fix
Miodec May 30, 2026
1c61693
yeet comparison
Miodec May 30, 2026
2a45341
fix
Miodec May 30, 2026
fd39854
yeet last second not round
Miodec May 30, 2026
e61c110
yeet stats
Miodec May 30, 2026
7fd6d17
yeet acc
Miodec May 30, 2026
48a17b4
yeet test seconds
Miodec May 30, 2026
66bdfe3
fully yeet stats file
Miodec May 30, 2026
fa4e20b
yeet acc
Miodec May 30, 2026
2cf2bf8
yeet
Miodec May 30, 2026
297775f
fixes
Miodec May 30, 2026
c9fa677
yeet missed words
Miodec May 30, 2026
442ddb4
yeet correcred
Miodec May 30, 2026
36b5251
input history
Miodec May 30, 2026
9883c9b
yeet input history
Miodec May 30, 2026
e1a3540
yeet
Miodec May 30, 2026
358e88c
move korean state
Miodec May 30, 2026
b3f3df0
early return
Miodec Jun 1, 2026
e85e149
boom
Miodec Jun 1, 2026
4cbde49
yeet replay
Miodec Jun 1, 2026
b08aeb8
pass now from input to timer start
Miodec Jun 1, 2026
bb555f3
bring command back
Miodec Jun 1, 2026
04bb380
Merge branch 'test-events-phase-1.5' into test-events-phase2
Miodec Jun 1, 2026
94b893d
Merge branch 'test-events-phase-1.5' into test-events-phase2
Miodec Jun 1, 2026
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
15 changes: 11 additions & 4 deletions frontend/src/ts/commandline/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import * as getErrorMessage from "../utils/error";
import * as JSONData from "../utils/json-data";
import { randomizeTheme } from "../controllers/theme-controller";
import { showModal } from "../states/modals";
import * as TestWords from "../test/test-words";
import {
showErrorNotification,
showSuccessNotification,
clearAllNotifications,
showSuccessNotification,
} 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 All @@ -40,6 +40,7 @@ import {
showFpsCounter,
} from "../components/layout/overlays/FpsCounter";
import { applyConfigFromJson } from "../config/lifecycle";
import { getAllTestEvents } from "../test/events/data";

const challengesPromise = JSONData.getChallengeList();
challengesPromise
Expand Down Expand Up @@ -290,12 +291,18 @@ export const commands: CommandsSubgroup = {
},
{
id: "copyResultStats",
display: "Copy result stats",
display: "Copy result data",
alias: "stats events",
icon: "fa-cog",
visible: false,
exec: async (): Promise<void> => {
navigator.clipboard
.writeText(JSON.stringify(TestStats.getStats()))
.writeText(
JSON.stringify({
events: getAllTestEvents(),
words: TestWords.words,
}),
)
.then(() => {
showSuccessNotification("Copied to clipboard");
})
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