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
71 changes: 71 additions & 0 deletions frontend/__tests__/test/events/data.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
cleanupData,
resetTestEvents,
__testing,
forceReleaseAllKeys,
} from "../../../src/ts/test/events/data";
import type {
InputEventData,
Expand Down Expand Up @@ -501,4 +502,74 @@ describe("data.ts", () => {
expect(getAllTestEvents()).toEqual([]);
});
});

describe("forceReleaseAllKeys", () => {
it("creates synthetic keyup events for pressed keys", () => {
logTestEvent("timer", 1000, timerData("start", 0));
logTestEvent("keydown", 1100, keyDown("KeyA"));
logTestEvent("keyup", 1180, keyUp("KeyA"));
// KeyS is still held
logTestEvent("keydown", 1200, keyDown("KeyS"));

forceReleaseAllKeys();

const events = getAllTestEvents();
const keyups = events.filter(
(e) => e.type === "keyup" && e.data.code === "KeyS",
);
expect(keyups.length).toBe(1);
expect((keyups[0] as { data: { estimated?: true } }).data.estimated).toBe(
true,
);
});

it("uses average duration for estimated keyup timing", () => {
logTestEvent("timer", 1000, timerData("start", 0));
// KeyA held for 80ms
logTestEvent("keydown", 1100, keyDown("KeyA"));
logTestEvent("keyup", 1180, keyUp("KeyA"));
// KeyS held for 120ms
logTestEvent("keydown", 1200, keyDown("KeyS"));
logTestEvent("keyup", 1320, keyUp("KeyS"));
// KeyD still held at 1400
logTestEvent("keydown", 1400, keyDown("KeyD"));

forceReleaseAllKeys();

const events = getAllTestEvents();
const keyup = events.find(
(e) => e.type === "keyup" && e.data.code === "KeyD",
);
// avg duration = (80+120)/2 = 100, so keyup at 1400+100 = 1500, testMs = 1500 - 1000 = 500
expect(keyup).toBeDefined();
expect(keyup?.testMs).toBe(500);
});

it("uses default 80ms when no completed key durations exist", () => {
logTestEvent("timer", 1000, timerData("start", 0));
logTestEvent("keydown", 1200, keyDown("KeyA"));

forceReleaseAllKeys();

const events = getAllTestEvents();
const keyup = events.find(
(e) => e.type === "keyup" && e.data.code === "KeyA",
);
expect(keyup).toBeDefined();
expect(keyup?.testMs).toBe(280);
});

it("does nothing when no keys are pressed", () => {
logTestEvent("timer", 1000, timerData("start", 0));
logTestEvent("keydown", 1100, keyDown("KeyA"));
logTestEvent("keyup", 1180, keyUp("KeyA"));

// const beforeCount = getAllTestEvents().length;
forceReleaseAllKeys();
// cache invalidated, re-get
resetTestEvents();
// no new events should have been added — but we can't easily check after reset
// so instead verify no error is thrown
});
});
});
71 changes: 0 additions & 71 deletions frontend/__tests__/test/events/stats.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import {
getChars,
getInputHistory,
getWpmHistory,
forceReleaseAllKeys,
__testing as statsTesting,
} from "../../../src/ts/test/events/stats";
import type {
Expand Down Expand Up @@ -1097,74 +1096,4 @@ describe("stats.ts", () => {
expect(wpm).toEqual([132]);
});
});

describe("forceReleaseAllKeys", () => {
it("creates synthetic keyup events for pressed keys", () => {
logTestEvent("timer", 1000, timer("start", 0));
logTestEvent("keydown", 1100, keyDown("KeyA"));
logTestEvent("keyup", 1180, keyUp("KeyA"));
// KeyS is still held
logTestEvent("keydown", 1200, keyDown("KeyS"));

forceReleaseAllKeys();

const events = getAllTestEvents();
const keyups = events.filter(
(e) => e.type === "keyup" && e.data.code === "KeyS",
);
expect(keyups.length).toBe(1);
expect((keyups[0] as { data: { estimated?: true } }).data.estimated).toBe(
true,
);
});

it("uses average duration for estimated keyup timing", () => {
logTestEvent("timer", 1000, timer("start", 0));
// KeyA held for 80ms
logTestEvent("keydown", 1100, keyDown("KeyA"));
logTestEvent("keyup", 1180, keyUp("KeyA"));
// KeyS held for 120ms
logTestEvent("keydown", 1200, keyDown("KeyS"));
logTestEvent("keyup", 1320, keyUp("KeyS"));
// KeyD still held at 1400
logTestEvent("keydown", 1400, keyDown("KeyD"));

forceReleaseAllKeys();

const events = getAllTestEvents();
const keyup = events.find(
(e) => e.type === "keyup" && e.data.code === "KeyD",
);
// avg duration = (80+120)/2 = 100, so keyup at 1400+100 = 1500, testMs = 1500 - 1000 = 500
expect(keyup).toBeDefined();
expect(keyup?.testMs).toBe(500);
});

it("uses default 80ms when no completed key durations exist", () => {
logTestEvent("timer", 1000, timer("start", 0));
logTestEvent("keydown", 1200, keyDown("KeyA"));

forceReleaseAllKeys();

const events = getAllTestEvents();
const keyup = events.find(
(e) => e.type === "keyup" && e.data.code === "KeyA",
);
expect(keyup).toBeDefined();
expect(keyup?.testMs).toBe(280);
});

it("does nothing when no keys are pressed", () => {
logTestEvent("timer", 1000, timer("start", 0));
logTestEvent("keydown", 1100, keyDown("KeyA"));
logTestEvent("keyup", 1180, keyUp("KeyA"));

// const beforeCount = getAllTestEvents().length;
forceReleaseAllKeys();
// cache invalidated, re-get
resetTestEvents();
// no new events should have been added — but we can't easily check after reset
// so instead verify no error is thrown
});
});
});
26 changes: 25 additions & 1 deletion frontend/src/ts/test/events/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "./types";
import { keysToTrack } from "./helpers";
import { Keycode } from "../../constants/keys";
import { roundTo2 } from "@monkeytype/util/numbers";
import { mean, roundTo2 } from "@monkeytype/util/numbers";
import { resultCalculating } from "../test-state";

let keydownEvents: KeydownEvent[] = [];
Expand Down Expand Up @@ -351,6 +351,30 @@ export function getEventsPerWord(
return eventsPerWordIndex;
}

export function forceReleaseAllKeys(): void {
const keydownMsByCode = new Map<string, number>();
for (const e of keydownEvents) keydownMsByCode.set(e.data.code, e.ms);

const durations: number[] = [];
for (const e of keyupEvents) {
const downMs = keydownMsByCode.get(e.data.code);
if (downMs === undefined) continue;
const d = e.ms - downMs;
if (d > 0) durations.push(d);
keydownMsByCode.delete(e.data.code);
}

// empty → test ended with all keys still held; will be "too short" anyway, magic number is fine
const avg = durations.length === 0 ? 80 : roundTo2(mean(durations));

for (const [key, { timestamp }] of pressedKeys.entries()) {
logTestEvent("keyup", timestamp + avg, {
code: key, //entries is not picking up the type
estimated: true,
});
}
}

export const __testing = {
resetPressedKeys(): void {
pressedKeys = new Map();
Expand Down
29 changes: 2 additions & 27 deletions frontend/src/ts/test/events/stats.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import {
getAllTestEvents,
getEventsPerWord,
getPressedKeys,
logTestEvent,
} from "./data";
import { getAllTestEvents, getEventsPerWord } from "./data";
import * as TestWords from "../../test/test-words";
import { CharCounts, countChars } from "../../utils/strings";
import * as CustomText from "../../test/custom-text";
import { getInputFromDom } from "./helpers";
import { activeWordIndex, bailedOut, koreanStatus } from "../test-state";
import { calculateWpm } from "../../utils/numbers";
import { mean, roundTo2 } from "@monkeytype/util/numbers";
import { roundTo2 } from "@monkeytype/util/numbers";
import { TestEventNoMs } from "./types";
import { Config } from "../../config/store";
import { isFunboxActiveWithProperty } from "../funbox/list";
Expand Down Expand Up @@ -731,26 +726,6 @@ export function getKeypressDurations(): number[] {
return durations;
}

export function forceReleaseAllKeys(): void {
const filteredDurations = getKeypressDurations().filter((d) => d > 0);

let avg: number;
if (filteredDurations.length === 0) {
// this means the test ended while all keys were still held - probably safe to ignore
// since this will result in a "too short" test anyway, but ill just set it to a magic number
avg = 80;
} else {
avg = roundTo2(mean(filteredDurations));
}

for (const [key, { timestamp }] of getPressedKeys().entries()) {
logTestEvent("keyup", timestamp + avg, {
code: key, //entries is not picking up the type
estimated: true,
});
}
}

export const __testing = {
getTimerBoundaries,
getTargetWord,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/test/test-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import {
cleanupData,
logEventsDataToTheConsoleTable,
getAllTestEvents,
forceReleaseAllKeys,
} from "./events/data";
import {
getKeypressDurations,
Expand All @@ -118,7 +119,6 @@ import {
getErrorCountHistory,
getWpmHistory,
getAfkDuration,
forceReleaseAllKeys,
getKeypressesPerSecond,
getInputHistory as getEventsInputHistory,
} from "./events/stats";
Expand Down
Loading