diff --git a/frontend/src/scripts/test/test-input.js b/frontend/src/scripts/test/test-input.ts similarity index 56% rename from frontend/src/scripts/test/test-input.js rename to frontend/src/scripts/test/test-input.ts index 65f14bb91..ab3f4c240 100644 --- a/frontend/src/scripts/test/test-input.js +++ b/frontend/src/scripts/test/test-input.ts @@ -1,52 +1,77 @@ import * as TestWords from "./test-words"; +type Keypress = { + count: number; + errors: number; + words: string[]; + afk: boolean; +}; + +type KeypressTimings = { + spacing: { + current: number; + array: number[] | "toolong"; + }; + duration: { + current: number; + array: number[] | "toolong"; + }; +}; + class Input { + current: string; + history: string[]; + historyLength: number; + length: number; constructor() { this.current = ""; this.history = []; + this.historyLength = 0; this.length = 0; } - reset() { + reset(): void { this.current = ""; this.history = []; this.length = 0; } - resetHistory() { + resetHistory(): void { this.history = []; this.length = 0; } - setCurrent(val) { + setCurrent(val: string): void { this.current = val; this.length = this.current.length; } - appendCurrent(val) { + appendCurrent(val: string): void { this.current += val; this.length = this.current.length; } - resetCurrent() { + resetCurrent(): void { this.current = ""; } - getCurrent() { + getCurrent(): string { return this.current; } - pushHistory() { + pushHistory(): void { this.history.push(this.current); this.historyLength = this.history.length; this.resetCurrent(); } - popHistory() { - return this.history.pop(); + popHistory(): string { + const ret = this.history.pop() ?? ""; + this.historyLength = this.history.length; + return ret; } - getHistory(i) { + getHistory(i: number): string | string[] { if (i === undefined) { return this.history; } else { @@ -54,69 +79,73 @@ class Input { } } - getHistoryLast() { + getHistoryLast(): string { return this.history[this.history.length - 1]; } } class Corrected { + current: string; + history: string[]; constructor() { this.current = ""; this.history = []; } - setCurrent(val) { + setCurrent(val: string): void { this.current = val; } - appendCurrent(val) { + appendCurrent(val: string): void { this.current += val; } - resetCurrent() { + resetCurrent(): void { this.current = ""; } - resetHistory() { + resetHistory(): void { this.history = []; } - reset() { + reset(): void { this.resetCurrent(); this.resetHistory(); } - getHistory(i) { + getHistory(i: number): string { return this.history[i]; } - popHistory() { - return this.history.pop(); + popHistory(): string { + return this.history.pop() ?? ""; } - pushHistory() { + pushHistory(): void { this.history.push(this.current); this.current = ""; } } -export let input = new Input(); -export let corrected = new Corrected(); +export const input = new Input(); +export const corrected = new Corrected(); -export let keypressPerSecond = []; -export let currentKeypress = { +export let keypressPerSecond: Keypress[] = []; +export let currentKeypress: Keypress = { count: 0, errors: 0, words: [], afk: true, }; -export let lastKeypress; +export let lastKeypress: number; export let currentBurstStart = 0; -export let missedWords = {}; +export let missedWords: { + [word: string]: number; +} = {}; export let accuracy = { correct: 0, incorrect: 0, }; -export let keypressTimings = { +export let keypressTimings: KeypressTimings = { spacing: { current: -1, array: [], @@ -126,45 +155,45 @@ export let keypressTimings = { array: [], }, }; -export let wpmHistory = []; -export let rawHistory = []; -export let burstHistory = []; +export let wpmHistory: number[] = []; +export let rawHistory: number[] = []; +export let burstHistory: number[] = []; export let bailout = false; -export function setBailout(tf) { +export function setBailout(tf: boolean): void { bailout = tf; } export let spacingDebug = false; -export function enableSpacingDebug() { +export function enableSpacingDebug(): void { spacingDebug = true; console.clear(); } -export function updateLastKeypress() { +export function updateLastKeypress(): void { lastKeypress = performance.now(); } -export function incrementKeypressCount() { +export function incrementKeypressCount(): void { currentKeypress.count++; } -export function setKeypressNotAfk() { +export function setKeypressNotAfk(): void { currentKeypress.afk = false; } -export function incrementKeypressErrors() { +export function incrementKeypressErrors(): void { currentKeypress.errors++; } -export function pushKeypressWord(word) { +export function pushKeypressWord(word: string): void { currentKeypress.words.push(word); } -export function setBurstStart(time) { +export function setBurstStart(time: number): void { currentBurstStart = time; } -export function pushKeypressesToHistory() { +export function pushKeypressesToHistory(): void { keypressPerSecond.push(currentKeypress); currentKeypress = { count: 0, @@ -174,7 +203,7 @@ export function pushKeypressesToHistory() { }; } -export function incrementAccuracy(correctincorrect) { +export function incrementAccuracy(correctincorrect: boolean): void { if (correctincorrect) { accuracy.correct++; } else { @@ -182,30 +211,30 @@ export function incrementAccuracy(correctincorrect) { } } -export function setKeypressTimingsTooLong() { +export function setKeypressTimingsTooLong(): void { keypressTimings.spacing.array = "toolong"; keypressTimings.duration.array = "toolong"; } -export function pushKeypressDuration(val) { - keypressTimings.duration.array.push(val); +export function pushKeypressDuration(val: number): void { + (keypressTimings.duration.array as number[]).push(val); } -export function setKeypressDuration(val) { +export function setKeypressDuration(val: number): void { keypressTimings.duration.current = val; } -function pushKeypressSpacing(val) { - keypressTimings.spacing.array.push(val); +function pushKeypressSpacing(val: number): void { + (keypressTimings.spacing.array as number[]).push(val); } -function setKeypressSpacing(val) { +function setKeypressSpacing(val: number): void { keypressTimings.spacing.current = val; } -export function recordKeypressSpacing() { - let now = performance.now(); - let diff = Math.abs(keypressTimings.spacing.current - now); +export function recordKeypressSpacing(): void { + const now = performance.now(); + const diff = Math.abs(keypressTimings.spacing.current - now); if (keypressTimings.spacing.current !== -1) { pushKeypressSpacing(diff); if (spacingDebug) @@ -235,7 +264,7 @@ export function recordKeypressSpacing() { ); } -export function resetKeypressTimings() { +export function resetKeypressTimings(): void { keypressTimings = { spacing: { current: performance.now(), @@ -249,7 +278,7 @@ export function resetKeypressTimings() { if (spacingDebug) console.clear(); } -export function pushMissedWord(word) { +export function pushMissedWord(word: string): void { if (!Object.keys(missedWords).includes(word)) { missedWords[word] = 1; } else { @@ -257,15 +286,15 @@ export function pushMissedWord(word) { } } -export function pushToWpmHistory(word) { - wpmHistory.push(word); +export function pushToWpmHistory(wpm: number): void { + wpmHistory.push(wpm); } -export function pushToRawHistory(word) { - rawHistory.push(word); +export function pushToRawHistory(raw: number): void { + rawHistory.push(raw); } -export function pushBurstToHistory(speed) { +export function pushBurstToHistory(speed: number): void { if (burstHistory[TestWords.words.currentIndex] === undefined) { burstHistory.push(speed); } else { @@ -274,7 +303,7 @@ export function pushBurstToHistory(speed) { } } -export function restart() { +export function restart(): void { wpmHistory = []; rawHistory = []; burstHistory = []; diff --git a/frontend/src/scripts/test/test-stats.js b/frontend/src/scripts/test/test-stats.ts similarity index 68% rename from frontend/src/scripts/test/test-stats.js rename to frontend/src/scripts/test/test-stats.ts index 19fd2447f..92d4f689e 100644 --- a/frontend/src/scripts/test/test-stats.js +++ b/frontend/src/scripts/test/test-stats.ts @@ -3,19 +3,95 @@ import * as Misc from "../misc"; import * as TestInput from "./test-input"; import * as TestWords from "./test-words"; +type CharCount = { + spaces: number; + correctWordChars: number; + allCorrectChars: number; + incorrectChars: number; + extraChars: number; + missedChars: number; + correctSpaces: number; +}; + +type Keypress = { + count: number; + errors: number; + words: string[]; + afk: boolean; +}; + +type KeypressTimings = { + spacing: { + current: number; + array: number[] | "toolong"; + }; + duration: { + current: number; + array: number[] | "toolong"; + }; +}; + +type DebugStats = { + start: number; + end: number; + wpmHistory: number[]; + rawHistory: number[]; + burstHistory: number[]; + keypressPerSecond: Keypress[]; + currentKeypress: { + count: number; + errors: number; + words: string[]; + afk: boolean; + }; + lastKeypress: number; + currentBurstStart: number; + lastSecondNotRound: boolean; + missedWords: { + [word: string]: number; + }; + accuracy: { + correct: number; + incorrect: number; + }; + keypressTimings: KeypressTimings; + keySpacingStats?: { + average: number; + sd: number; + }; + keyDurationStats?: { + average: number; + sd: number; + }; +}; + +type Stats = { + wpm: number; + wpmRaw: number; + acc: number; + correctChars: number; + incorrectChars: number; + missedChars: number; + extraChars: number; + allChars: number; + time: number; + spaces: number; + correctSpaces: number; +}; + export let invalid = false; -export let start, end; -export let start2, end2; +export let start: number, end: number; +export let start2: number, end2: number; export let lastSecondNotRound = false; export let lastTestWpm = 0; -export function setLastTestWpm(wpm) { +export function setLastTestWpm(wpm: number): void { lastTestWpm = wpm; } -export function getStats() { - let ret = { +export function getStats(): DebugStats { + const ret: DebugStats = { start, end, wpmHistory: TestInput.wpmHistory, @@ -34,10 +110,10 @@ export function getStats() { try { ret.keySpacingStats = { average: - TestInput.keypressTimings.spacing.array.reduce( + (TestInput.keypressTimings.spacing.array as number[]).reduce( (previous, current) => (current += previous) ) / TestInput.keypressTimings.spacing.array.length, - sd: Misc.stdDev(TestInput.keypressTimings.spacing.array), + sd: Misc.stdDev(TestInput.keypressTimings.spacing.array as number[]), }; } catch (e) { // @@ -45,10 +121,10 @@ export function getStats() { try { ret.keyDurationStats = { average: - TestInput.keypressTimings.duration.array.reduce( + (TestInput.keypressTimings.duration.array as number[]).reduce( (previous, current) => (current += previous) ) / TestInput.keypressTimings.duration.array.length, - sd: Misc.stdDev(TestInput.keypressTimings.duration.array), + sd: Misc.stdDev(TestInput.keypressTimings.duration.array as number[]), }; } catch (e) { // @@ -57,7 +133,7 @@ export function getStats() { return ret; } -export function restart() { +export function restart(): void { start = 0; end = 0; invalid = false; @@ -67,26 +143,26 @@ export function restart() { export let restartCount = 0; export let incompleteSeconds = 0; -export function incrementRestartCount() { +export function incrementRestartCount(): void { restartCount++; } -export function incrementIncompleteSeconds(val) { +export function incrementIncompleteSeconds(val: number): void { incompleteSeconds += val; } -export function resetIncomplete() { +export function resetIncomplete(): void { restartCount = 0; incompleteSeconds = 0; } -export function setInvalid() { +export function setInvalid(): void { invalid = true; } -export function calculateTestSeconds(now) { +export function calculateTestSeconds(now?: number): number { if (now === undefined) { - let endAfkSeconds = (end - TestInput.lastKeypress) / 1000; + const endAfkSeconds = (end - TestInput.lastKeypress) / 1000; if ((Config.mode == "zen" || TestInput.bailout) && endAfkSeconds < 7) { return (TestInput.lastKeypress - start) / 1000; } else { @@ -97,13 +173,13 @@ export function calculateTestSeconds(now) { } } -export function calculateWpmAndRaw() { +export function calculateWpmAndRaw(): { wpm: number; raw: number } { let chars = 0; let correctWordChars = 0; let spaces = 0; //check input history for (let i = 0; i < TestInput.input.history.length; i++) { - let word = + const word = Config.mode == "zen" ? TestInput.input.getHistory(i) : TestWords.words.get(i); @@ -113,7 +189,7 @@ export function calculateWpmAndRaw() { correctWordChars += word.length; if ( i < TestInput.input.history.length - 1 && - Misc.getLastChar(TestInput.input.getHistory(i)) !== "\n" + Misc.getLastChar(TestInput.input.getHistory(i) as string) !== "\n" ) { spaces++; } @@ -121,12 +197,12 @@ export function calculateWpmAndRaw() { chars += TestInput.input.getHistory(i).length; } if (TestInput.input.current !== "") { - let word = + const word = Config.mode == "zen" ? TestInput.input.current : TestWords.words.getCurrent(); //check whats currently typed - let toAdd = { + const toAdd = { correct: 0, incorrect: 0, missed: 0, @@ -156,26 +232,28 @@ export function calculateWpmAndRaw() { spaces = 0; } chars += TestInput.input.current.length; - let testSeconds = calculateTestSeconds(performance.now()); - let wpm = Math.round(((correctWordChars + spaces) * (60 / testSeconds)) / 5); - let raw = Math.round(((chars + spaces) * (60 / testSeconds)) / 5); + const testSeconds = calculateTestSeconds(performance.now()); + const wpm = Math.round( + ((correctWordChars + spaces) * (60 / testSeconds)) / 5 + ); + const raw = Math.round(((chars + spaces) * (60 / testSeconds)) / 5); return { wpm: wpm, raw: raw, }; } -export function setEnd(e) { +export function setEnd(e: number): void { end = e; end2 = Date.now(); } -export function setStart(s) { +export function setStart(s: number): void { start = s; start2 = Date.now(); } -export function calculateAfkSeconds(testSeconds) { +export function calculateAfkSeconds(testSeconds: number): number { let extraAfk = 0; if (testSeconds !== undefined) { if (Config.mode === "time") { @@ -191,16 +269,16 @@ export function calculateAfkSeconds(testSeconds) { // `gonna add extra ${extraAfk} seconds of afk because of no keypress data` // ); } - let ret = TestInput.keypressPerSecond.filter((x) => x.afk).length; + const ret = TestInput.keypressPerSecond.filter((x) => x.afk).length; return ret + extraAfk; } -export function setLastSecondNotRound() { +export function setLastSecondNotRound(): void { lastSecondNotRound = true; } -export function calculateBurst() { - let timeToWrite = (performance.now() - TestInput.currentBurstStart) / 1000; +export function calculateBurst(): number { + const timeToWrite = (performance.now() - TestInput.currentBurstStart) / 1000; let wordLength; if (Config.mode === "zen") { wordLength = TestInput.input.current.length; @@ -210,27 +288,27 @@ export function calculateBurst() { } else { wordLength = TestWords.words.getCurrent().length; } - let speed = Misc.roundTo2((wordLength * (60 / timeToWrite)) / 5); + const speed = Misc.roundTo2((wordLength * (60 / timeToWrite)) / 5); return Math.round(speed); } -export function calculateAccuracy() { - let acc = +export function calculateAccuracy(): number { + const acc = (TestInput.accuracy.correct / (TestInput.accuracy.correct + TestInput.accuracy.incorrect)) * 100; return isNaN(acc) ? 100 : acc; } -export function removeAfkData() { - let testSeconds = calculateTestSeconds(); +export function removeAfkData(): void { + const testSeconds = calculateTestSeconds(); TestInput.keypressPerSecond.splice(testSeconds); - TestInput.keypressTimings.duration.array.splice(testSeconds); - TestInput.keypressTimings.spacing.array.splice(testSeconds); + (TestInput.keypressTimings.duration.array as number[]).splice(testSeconds); + (TestInput.keypressTimings.spacing.array as number[]).splice(testSeconds); TestInput.wpmHistory.splice(testSeconds); } -function countChars() { +function countChars(): CharCount { let correctWordChars = 0; let correctChars = 0; let incorrectChars = 0; @@ -239,7 +317,7 @@ function countChars() { let spaces = 0; let correctspaces = 0; for (let i = 0; i < TestInput.input.history.length; i++) { - let word = + const word = Config.mode == "zen" ? TestInput.input.getHistory(i) : TestWords.words.get(i); @@ -253,7 +331,7 @@ function countChars() { correctChars += word.length; if ( i < TestInput.input.history.length - 1 && - Misc.getLastChar(TestInput.input.getHistory(i)) !== "\n" + Misc.getLastChar(TestInput.input.getHistory(i) as string) !== "\n" ) { correctspaces++; } @@ -274,7 +352,7 @@ function countChars() { } } else { //not enough chars - let toAdd = { + const toAdd = { correct: 0, incorrect: 0, missed: 0, @@ -321,18 +399,18 @@ function countChars() { }; } -export function calculateStats() { +export function calculateStats(): Stats { let testSeconds = calculateTestSeconds(); console.log((end2 - start2) / 1000); console.log(testSeconds); if (Config.mode != "custom") { testSeconds = Misc.roundTo2(testSeconds); } - let chars = countChars(); - let wpm = Misc.roundTo2( + const chars = countChars(); + const wpm = Misc.roundTo2( ((chars.correctWordChars + chars.correctSpaces) * (60 / testSeconds)) / 5 ); - let wpmraw = Misc.roundTo2( + const wpmraw = Misc.roundTo2( ((chars.allCorrectChars + chars.spaces + chars.incorrectChars + @@ -340,7 +418,7 @@ export function calculateStats() { (60 / testSeconds)) / 5 ); - let acc = Misc.roundTo2(calculateAccuracy()); + const acc = Misc.roundTo2(calculateAccuracy()); return { wpm: isNaN(wpm) ? 0 : wpm, wpmRaw: isNaN(wpmraw) ? 0 : wpmraw,