fix: use selected typing speed unit on personal best popup (fehmer) (#5070)

* fix: Use selected typing speed unit on personal best popup

* refactor

* refactor

* test coverage

* use Format in more places

* Make config mockable

* dependency injection

* wip

* fix

* test

* touch
This commit is contained in:
Christian Fehmer 2024-02-19 14:46:02 +01:00 committed by GitHub
parent 4d5085d351
commit bac837d823
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 416 additions and 302 deletions

View file

@ -0,0 +1,201 @@
import { Formatting } from "../../src/ts/utils/format";
import * as MockConfig from "../../src/ts/config";
import DefaultConfig from "../../src/ts/constants/default-config";
describe("format.ts", () => {
afterEach(() => {
jest.resetAllMocks();
});
describe("typingsSpeed", () => {
it("should format with typing speed and decimalPlaces from configuration", () => {
//wpm, no decimals
const wpmNoDecimals = getInstance({
typingSpeedUnit: "wpm",
alwaysShowDecimalPlaces: false,
});
expect(wpmNoDecimals.typingSpeed(12.5)).toEqual("13");
expect(wpmNoDecimals.typingSpeed(0)).toEqual("0");
//cpm, no decimals
const cpmNoDecimals = getInstance({
typingSpeedUnit: "cpm",
alwaysShowDecimalPlaces: false,
});
expect(cpmNoDecimals.typingSpeed(12.5)).toEqual("63");
expect(cpmNoDecimals.typingSpeed(0)).toEqual("0");
//wpm, with decimals
const wpmWithDecimals = getInstance({
typingSpeedUnit: "wpm",
alwaysShowDecimalPlaces: true,
});
expect(wpmWithDecimals.typingSpeed(12.5)).toEqual("12.50");
expect(wpmWithDecimals.typingSpeed(0)).toEqual("0.00");
//cpm, with decimals
const cpmWithDecimals = getInstance({
typingSpeedUnit: "cpm",
alwaysShowDecimalPlaces: true,
});
expect(cpmWithDecimals.typingSpeed(12.5)).toEqual("62.50");
expect(cpmWithDecimals.typingSpeed(0)).toEqual("0.00");
});
it("should format with fallback", () => {
//default fallback
const format = getInstance();
expect(format.typingSpeed(null)).toEqual("-");
expect(format.typingSpeed(undefined)).toEqual("-");
//provided fallback
expect(format.typingSpeed(null, { fallback: "none" })).toEqual("none");
expect(format.typingSpeed(null, { fallback: "" })).toEqual("");
expect(format.typingSpeed(undefined, { fallback: "none" })).toEqual(
"none"
);
expect(format.typingSpeed(undefined, { fallback: "" })).toEqual("");
expect(format.typingSpeed(undefined, { fallback: undefined })).toEqual(
""
);
});
it("should format with decimals", () => {
//force with decimals
const wpmNoDecimals = getInstance({
typingSpeedUnit: "wpm",
alwaysShowDecimalPlaces: false,
});
expect(
wpmNoDecimals.typingSpeed(100, { showDecimalPlaces: true })
).toEqual("100.00");
//force without decimals
const wpmWithDecimals = getInstance({
typingSpeedUnit: "wpm",
alwaysShowDecimalPlaces: true,
});
expect(
wpmWithDecimals.typingSpeed(100, { showDecimalPlaces: false })
).toEqual("100");
});
it("should format with suffix", () => {
const format = getInstance({
typingSpeedUnit: "wpm",
alwaysShowDecimalPlaces: false,
});
expect(format.typingSpeed(100, { suffix: " raw" })).toEqual("100 raw");
expect(format.typingSpeed(100, { suffix: undefined })).toEqual("100");
expect(format.typingSpeed(0, { suffix: " raw" })).toEqual("0 raw");
expect(format.typingSpeed(null, { suffix: " raw" })).toEqual("-");
expect(format.typingSpeed(undefined, { suffix: " raw" })).toEqual("-");
});
});
describe("percentage", () => {
it("should format with decimalPlaces from configuration", () => {
//no decimals
const noDecimals = getInstance({ alwaysShowDecimalPlaces: false });
expect(noDecimals.percentage(12.5)).toEqual("13%");
expect(noDecimals.percentage(0)).toEqual("0%");
//with decimals
const withDecimals = getInstance({ alwaysShowDecimalPlaces: true });
expect(withDecimals.percentage(12.5)).toEqual("12.50%");
expect(withDecimals.percentage(0)).toEqual("0.00%");
});
it("should format with fallback", () => {
//default fallback
const format = getInstance();
expect(format.percentage(null)).toEqual("-");
expect(format.percentage(undefined)).toEqual("-");
//provided fallback
expect(format.percentage(null, { fallback: "none" })).toEqual("none");
expect(format.percentage(null, { fallback: "" })).toEqual("");
expect(format.percentage(undefined, { fallback: "none" })).toEqual(
"none"
);
expect(format.percentage(undefined, { fallback: "" })).toEqual("");
expect(format.percentage(undefined, { fallback: undefined })).toEqual("");
});
it("should format with decimals", () => {
//force with decimals
const noDecimals = getInstance({ alwaysShowDecimalPlaces: false });
expect(noDecimals.percentage(100, { showDecimalPlaces: true })).toEqual(
"100.00%"
);
//force without decimals
const withDecimals = getInstance({ alwaysShowDecimalPlaces: true });
expect(
withDecimals.percentage(100, { showDecimalPlaces: false })
).toEqual("100%");
});
it("should format with suffix", () => {
const format = getInstance({ alwaysShowDecimalPlaces: false });
expect(format.percentage(100, { suffix: " raw" })).toEqual("100% raw");
expect(format.percentage(100, { suffix: undefined })).toEqual("100%");
expect(format.percentage(0, { suffix: " raw" })).toEqual("0% raw");
expect(format.percentage(null, { suffix: " raw" })).toEqual("-");
expect(format.percentage(undefined, { suffix: " raw" })).toEqual("-");
});
});
describe("decimals", () => {
it("should format with decimalPlaces from configuration", () => {
//no decimals
const noDecimals = getInstance({ alwaysShowDecimalPlaces: false });
expect(noDecimals.decimals(12.5)).toEqual("13");
expect(noDecimals.decimals(0)).toEqual("0");
//with decimals
const withDecimals = getInstance({ alwaysShowDecimalPlaces: true });
expect(withDecimals.decimals(12.5)).toEqual("12.50");
expect(withDecimals.decimals(0)).toEqual("0.00");
});
it("should format with fallback", () => {
//default fallback
const format = getInstance();
expect(format.decimals(null)).toEqual("-");
expect(format.decimals(undefined)).toEqual("-");
//provided fallback
expect(format.decimals(null, { fallback: "none" })).toEqual("none");
expect(format.decimals(null, { fallback: "" })).toEqual("");
expect(format.decimals(undefined, { fallback: "none" })).toEqual("none");
expect(format.decimals(undefined, { fallback: "" })).toEqual("");
expect(format.decimals(undefined, { fallback: undefined })).toEqual("");
});
it("should format with decimals", () => {
//force with decimals
const noDecimals = getInstance({ alwaysShowDecimalPlaces: false });
expect(noDecimals.decimals(100, { showDecimalPlaces: true })).toEqual(
"100.00"
);
//force without decimals
const withDecimals = getInstance({ alwaysShowDecimalPlaces: true });
expect(withDecimals.decimals(100, { showDecimalPlaces: false })).toEqual(
"100"
);
});
it("should format with suffix", () => {
const format = getInstance({ alwaysShowDecimalPlaces: false });
expect(format.decimals(100, { suffix: " raw" })).toEqual("100 raw");
expect(format.decimals(100, { suffix: undefined })).toEqual("100");
expect(format.decimals(0, { suffix: " raw" })).toEqual("0 raw");
expect(format.decimals(null, { suffix: " raw" })).toEqual("-");
expect(format.decimals(undefined, { suffix: " raw" })).toEqual("-");
});
});
});
function getInstance(config?: Partial<SharedTypes.Config>): Formatting {
const target: SharedTypes.Config = { ...DefaultConfig, ...config };
return new Formatting(target);
}

View file

@ -1,7 +1,6 @@
import Config from "../config";
import format from "date-fns/format";
import * as Misc from "../utils/misc";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import dateFormat from "date-fns/format";
import Format from "../utils/format";
function clearTables(isProfile: boolean): void {
const source = isProfile ? "Profile" : "Account";
@ -140,7 +139,6 @@ function buildPbHtml(
let dateText = "";
const modeString = `${mode2} ${mode === "time" ? "seconds" : "words"}`;
const speedUnit = Config.typingSpeedUnit;
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
try {
const pbData = (pbs[mode][mode2] ?? []).sort((a, b) => b.wpm - a.wpm)[0];
@ -148,62 +146,28 @@ function buildPbHtml(
const date = new Date(pbData.timestamp);
if (pbData.timestamp) {
dateText = format(date, "dd MMM yyyy");
dateText = dateFormat(date, "dd MMM yyyy");
}
let speedString: number | string = typingSpeedUnit.fromWpm(pbData.wpm);
if (Config.alwaysShowDecimalPlaces) {
speedString = Misc.roundTo2(speedString).toFixed(2);
} else {
speedString = Math.round(speedString);
}
speedString += ` ${speedUnit}`;
let rawString: number | string = typingSpeedUnit.fromWpm(pbData.raw);
if (Config.alwaysShowDecimalPlaces) {
rawString = Misc.roundTo2(rawString).toFixed(2);
} else {
rawString = Math.round(rawString);
}
rawString += ` raw`;
let accString: number | string = pbData.acc;
if (accString === undefined) {
accString = "-";
} else {
if (Config.alwaysShowDecimalPlaces) {
accString = Misc.roundTo2(accString).toFixed(2);
} else {
accString = Math.floor(accString);
}
}
accString += ` acc`;
let conString: number | string = pbData.consistency;
if (conString === undefined) {
conString = "-";
} else {
if (Config.alwaysShowDecimalPlaces) {
conString = Misc.roundTo2(conString).toFixed(2);
} else {
conString = Math.round(conString);
}
}
conString += ` con`;
retval = `<div class="quick">
<div class="test">${modeString}</div>
<div class="wpm">${Math.round(typingSpeedUnit.fromWpm(pbData.wpm))}</div>
<div class="acc">${
pbData.acc === undefined ? "-" : Math.floor(pbData.acc) + "%"
}</div>
<div class="wpm">${Format.typingSpeed(pbData.wpm, {
showDecimalPlaces: false,
})}</div>
<div class="acc">${Format.percentage(pbData.acc, {
showDecimalPlaces: false,
})}</div>
</div>
<div class="fullTest">
<div>${modeString}</div>
<div>${speedString}</div>
<div>${rawString}</div>
<div>${accString}</div>
<div>${conString}</div>
<div>${Format.typingSpeed(pbData.wpm, {
suffix: ` ${speedUnit}`,
})}</div>
<div>${Format.typingSpeed(pbData.raw, { suffix: " raw" })}</div>
<div>${Format.percentage(pbData.acc, { suffix: " acc" })}</div>
<div>${Format.percentage(pbData.consistency, {
suffix: " con",
})}</div>
<div>${dateText}</div>
</div>`;
} catch (e) {

View file

@ -2,7 +2,6 @@ import Ape from "../ape";
import * as DB from "../db";
import Config from "../config";
import * as Misc from "../utils/misc";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import * as Notifications from "./notifications";
import format from "date-fns/format";
import { isAuthenticated } from "../firebase";
@ -11,6 +10,7 @@ import { getHTMLById as getBadgeHTMLbyId } from "../controllers/badge-controller
import * as ConnectionState from "../states/connection";
import * as Skeleton from "../popups/skeleton";
import { debounce } from "throttle-debounce";
import Format from "../utils/format";
import SlimSelect from "slim-select";
const wrapperId = "leaderboardsWrapper";
@ -165,7 +165,6 @@ function updateFooter(lb: LbKey): void {
return;
}
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
if (DB.getSnapshot()?.lbOptOut === true) {
$(`#leaderboardsWrapper table.${side} tfoot`).html(`
<tr>
@ -211,12 +210,18 @@ function updateFooter(lb: LbKey): void {
<tr>
<td>${lbRank.rank}</td>
<td><span class="top">You</span>${toppercent ? toppercent : ""}</td>
<td class="alignRight">${typingSpeedUnit.fromWpm(entry.wpm).toFixed(2)}<br>
<div class="sub">${entry.acc.toFixed(2)}%</div></td>
<td class="alignRight">${typingSpeedUnit.fromWpm(entry.raw).toFixed(2)}<br>
<div class="sub">${
entry.consistency === undefined ? "-" : entry.consistency.toFixed(2) + "%"
}</div></td>
<td class="alignRight">${Format.typingSpeed(entry.wpm, {
showDecimalPlaces: true,
})}<br>
<div class="sub">${Format.percentage(entry.acc, {
showDecimalPlaces: true,
})}%</div></td>
<td class="alignRight">${Format.typingSpeed(entry.raw, {
showDecimalPlaces: true,
})}<br>
<div class="sub">${Format.percentage(entry.consistency, {
showDecimalPlaces: true,
})}</div></td>
<td class="alignRight">${format(date, "dd MMM yyyy")}<br>
<div class='sub'>${format(date, "HH:mm")}</div></td>
</tr>
@ -296,8 +301,6 @@ async function fillTable(lb: LbKey): Promise<void> {
"<tr><td colspan='7'>No results found</td></tr>"
);
}
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
const loggedInUserName = DB.getSnapshot()?.name;
let html = "";
@ -336,12 +339,18 @@ async function fillTable(lb: LbKey): Promise<void> {
${entry.badgeId ? getBadgeHTMLbyId(entry.badgeId) : ""}
</div>
</td>
<td class="alignRight">${typingSpeedUnit.fromWpm(entry.wpm).toFixed(2)}<br>
<div class="sub">${entry.acc.toFixed(2)}%</div></td>
<td class="alignRight">${typingSpeedUnit.fromWpm(entry.raw).toFixed(2)}<br>
<div class="sub">${
entry.consistency === undefined ? "-" : entry.consistency.toFixed(2) + "%"
}</div></td>
<td class="alignRight">${Format.typingSpeed(entry.wpm, {
showDecimalPlaces: true,
})}<br>
<div class="sub">${Format.percentage(entry.acc, {
showDecimalPlaces: true,
})}</div></td>
<td class="alignRight">${Format.typingSpeed(entry.raw, {
showDecimalPlaces: true,
})}<br>
<div class="sub">${Format.percentage(entry.consistency, {
showDecimalPlaces: true,
})}</div></td>
<td class="alignRight">${format(date, "dd MMM yyyy")}<br>
<div class='sub'>${format(date, "HH:mm")}</div></td>
</tr>

View file

@ -7,8 +7,8 @@ import * as TestWords from "../test/test-words";
import * as ConfigEvent from "../observables/config-event";
import { isAuthenticated } from "../firebase";
import * as CustomTextState from "../states/custom-text-name";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import { getLanguageDisplayString, roundTo2 } from "../utils/misc";
import { getLanguageDisplayString } from "../utils/misc";
import Format from "../utils/format";
ConfigEvent.subscribe((eventKey) => {
if (
@ -123,14 +123,11 @@ export async function update(): Promise<void> {
Config.paceCaret !== "off" ||
(Config.repeatedPace && TestState.isPaceRepeat)
) {
let speed = "";
try {
speed = ` (${roundTo2(
getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(
PaceCaret.settings?.wpm ?? 0
)
)} ${Config.typingSpeedUnit})`;
} catch {}
const speed = Format.typingSpeed(PaceCaret.settings?.wpm ?? 0, {
showDecimalPlaces: true,
suffix: ` ${Config.typingSpeedUnit}`,
});
$(".pageTest #testModesNotice").append(
`<div class="textButton" commands="paceCaretMode"><i class="fas fa-tachometer-alt"></i>${
Config.paceCaret === "average"
@ -142,7 +139,7 @@ export async function update(): Promise<void> {
: Config.paceCaret === "daily"
? "daily"
: "custom"
} pace${speed}</div>`
} pace ${speed}</div>`
);
}
@ -157,14 +154,11 @@ export async function update(): Promise<void> {
if (isAuthenticated() && avgWPM > 0) {
const avgWPMText = ["speed", "both"].includes(Config.showAverage)
? getTypingSpeedUnit(Config.typingSpeedUnit).convertWithUnitSuffix(
avgWPM,
Config.alwaysShowDecimalPlaces
)
? Format.typingSpeed(avgWPM, { suffix: ` ${Config.typingSpeedUnit}` })
: "";
const avgAccText = ["acc", "both"].includes(Config.showAverage)
? `${avgAcc}% acc`
? Format.percentage(avgAcc, { suffix: " acc" })
: "";
const text = `${avgWPMText} ${avgAccText}`.trim();
@ -177,11 +171,10 @@ export async function update(): Promise<void> {
if (Config.minWpm !== "off") {
$(".pageTest #testModesNotice").append(
`<div class="textButton" commands="minWpm"><i class="fas fa-bomb"></i>min ${roundTo2(
getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(
Config.minWpmCustomSpeed
)
)} ${Config.typingSpeedUnit}</div>`
`<div class="textButton" commands="minWpm"><i class="fas fa-bomb"></i>min ${Format.typingSpeed(
Config.minWpmCustomSpeed,
{ showDecimalPlaces: true, suffix: ` ${Config.typingSpeedUnit}` }
)}</div>`
);
}
@ -193,10 +186,9 @@ export async function update(): Promise<void> {
if (Config.minBurst !== "off") {
$(".pageTest #testModesNotice").append(
`<div class="textButton" commands="minBurst"><i class="fas fa-bomb"></i>min ${roundTo2(
getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(
Config.minBurstCustomSpeed
)
`<div class="textButton" commands="minBurst"><i class="fas fa-bomb"></i>min ${Format.typingSpeed(
Config.minBurstCustomSpeed,
{ showDecimalPlaces: true }
)} ${Config.typingSpeedUnit} burst ${
Config.minBurst === "flex" ? "(flex)" : ""
}</div>`

View file

@ -23,6 +23,7 @@ import * as ActivePage from "../states/active-page";
import { Auth } from "../firebase";
import * as Loader from "../elements/loader";
import * as ResultBatches from "../elements/result-batches";
import Format from "../utils/format";
let filterDebug = false;
//toggle filterdebug
@ -37,7 +38,6 @@ let filteredResults: SharedTypes.Result<SharedTypes.Config.Mode>[] = [];
let visibleTableLines = 0;
function loadMoreLines(lineIndex?: number): void {
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
if (filteredResults === undefined || filteredResults.length === 0) return;
let newVisibleLines;
if (lineIndex && lineIndex > visibleTableLines) {
@ -53,16 +53,6 @@ function loadMoreLines(lineIndex?: number): void {
diff = "normal";
}
let raw;
try {
raw = typingSpeedUnit.fromWpm(result.rawWpm).toFixed(2);
if (raw === undefined) {
raw = "-";
}
} catch (e) {
raw = "-";
}
let icons = `<span aria-label="${result.language?.replace(
"_",
" "
@ -139,12 +129,6 @@ function loadMoreLines(lineIndex?: number): void {
}
}
let consistency = "-";
if (result.consistency) {
consistency = result.consistency.toFixed(2) + "%";
}
let pb = "";
if (result.isPb) {
pb = '<i class="fas fa-fw fa-crown"></i>';
@ -158,10 +142,12 @@ function loadMoreLines(lineIndex?: number): void {
$(".pageAccount .history table tbody").append(`
<tr class="resultRow" id="result-${i}">
<td>${pb}</td>
<td>${typingSpeedUnit.fromWpm(result.wpm).toFixed(2)}</td>
<td>${raw}</td>
<td>${result.acc.toFixed(2)}%</td>
<td>${consistency}</td>
<td>${Format.typingSpeed(result.wpm, { showDecimalPlaces: true })}</td>
<td>${Format.typingSpeed(result.rawWpm, { showDecimalPlaces: true })}</td>
<td>${Format.percentage(result.acc, { showDecimalPlaces: true })}</td>
<td>${Format.percentage(result.consistency, {
showDecimalPlaces: true,
})}</td>
<td>${charStats}</td>
<td>${result.mode} ${result.mode2}</td>
<td class="infoIcons">${icons}</td>
@ -860,144 +846,58 @@ async function fillContent(): Promise<void> {
Misc.secondsToString(Math.round(totalSecondsFiltered), true, true)
);
let highestSpeed: number | string = typingSpeedUnit.fromWpm(topWpm);
if (Config.alwaysShowDecimalPlaces) {
highestSpeed = Misc.roundTo2(highestSpeed).toFixed(2);
} else {
highestSpeed = Math.round(highestSpeed);
}
const speedUnit = Config.typingSpeedUnit;
$(".pageAccount .highestWpm .title").text(`highest ${speedUnit}`);
$(".pageAccount .highestWpm .val").text(highestSpeed);
let averageSpeed: number | string = typingSpeedUnit.fromWpm(totalWpm);
if (Config.alwaysShowDecimalPlaces) {
averageSpeed = Misc.roundTo2(averageSpeed / testCount).toFixed(2);
} else {
averageSpeed = Math.round(averageSpeed / testCount);
}
$(".pageAccount .highestWpm .val").text(Format.typingSpeed(topWpm));
$(".pageAccount .averageWpm .title").text(`average ${speedUnit}`);
$(".pageAccount .averageWpm .val").text(averageSpeed);
let averageSpeedLast10: number | string =
typingSpeedUnit.fromWpm(wpmLast10total);
if (Config.alwaysShowDecimalPlaces) {
averageSpeedLast10 = Misc.roundTo2(averageSpeedLast10 / last10).toFixed(2);
} else {
averageSpeedLast10 = Math.round(averageSpeedLast10 / last10);
}
$(".pageAccount .averageWpm .val").text(
Format.typingSpeed(totalWpm / testCount)
);
$(".pageAccount .averageWpm10 .title").text(
`average ${speedUnit} (last 10 tests)`
);
$(".pageAccount .averageWpm10 .val").text(averageSpeedLast10);
let highestRawSpeed: number | string = typingSpeedUnit.fromWpm(rawWpm.max);
if (Config.alwaysShowDecimalPlaces) {
highestRawSpeed = Misc.roundTo2(highestRawSpeed).toFixed(2);
} else {
highestRawSpeed = Math.round(highestRawSpeed);
}
$(".pageAccount .averageWpm10 .val").text(
Format.typingSpeed(wpmLast10total / last10)
);
$(".pageAccount .highestRaw .title").text(`highest raw ${speedUnit}`);
$(".pageAccount .highestRaw .val").text(highestRawSpeed);
let averageRawSpeed: number | string = typingSpeedUnit.fromWpm(rawWpm.total);
if (Config.alwaysShowDecimalPlaces) {
averageRawSpeed = Misc.roundTo2(averageRawSpeed / rawWpm.count).toFixed(2);
} else {
averageRawSpeed = Math.round(averageRawSpeed / rawWpm.count);
}
$(".pageAccount .highestRaw .val").text(Format.typingSpeed(rawWpm.max));
$(".pageAccount .averageRaw .title").text(`average raw ${speedUnit}`);
$(".pageAccount .averageRaw .val").text(averageRawSpeed);
let averageRawSpeedLast10: number | string = typingSpeedUnit.fromWpm(
rawWpm.last10Total
$(".pageAccount .averageRaw .val").text(
Format.typingSpeed(rawWpm.total / rawWpm.count)
);
if (Config.alwaysShowDecimalPlaces) {
averageRawSpeedLast10 = Misc.roundTo2(
averageRawSpeedLast10 / rawWpm.last10Count
).toFixed(2);
} else {
averageRawSpeedLast10 = Math.round(
averageRawSpeedLast10 / rawWpm.last10Count
);
}
$(".pageAccount .averageRaw10 .title").text(
`average raw ${speedUnit} (last 10 tests)`
);
$(".pageAccount .averageRaw10 .val").text(averageRawSpeedLast10);
$(".pageAccount .averageRaw10 .val").text(
Format.typingSpeed(rawWpm.last10Total / rawWpm.last10Count)
);
$(".pageAccount .highestWpm .mode").html(topMode);
$(".pageAccount .testsTaken .val").text(testCount);
let highestAcc: string | number = topAcc;
if (Config.alwaysShowDecimalPlaces) {
highestAcc = Misc.roundTo2(highestAcc).toFixed(2);
} else {
highestAcc = Math.floor(highestAcc);
}
$(".pageAccount .highestAcc .val").text(highestAcc + "%");
let averageAcc: number | string = totalAcc;
if (Config.alwaysShowDecimalPlaces) {
averageAcc = Misc.roundTo2(averageAcc / testCount);
} else {
averageAcc = Math.floor(averageAcc / testCount);
}
$(".pageAccount .avgAcc .val").text(averageAcc + "%");
let averageAccLast10: number | string = totalAcc10;
if (Config.alwaysShowDecimalPlaces) {
averageAccLast10 = Misc.roundTo2(averageAccLast10 / last10);
} else {
averageAccLast10 = Math.floor(averageAccLast10 / last10);
}
$(".pageAccount .avgAcc10 .val").text(averageAccLast10 + "%");
$(".pageAccount .highestAcc .val").text(Format.percentage(topAcc));
$(".pageAccount .avgAcc .val").text(Format.percentage(totalAcc / testCount));
$(".pageAccount .avgAcc10 .val").text(Format.percentage(totalAcc10 / last10));
if (totalCons === 0 || totalCons === undefined) {
$(".pageAccount .avgCons .val").text("-");
$(".pageAccount .avgCons10 .val").text("-");
} else {
let highestCons: number | string = topCons;
if (Config.alwaysShowDecimalPlaces) {
highestCons = Misc.roundTo2(highestCons).toFixed(2);
} else {
highestCons = Math.round(highestCons);
}
$(".pageAccount .highestCons .val").text(Format.percentage(topCons));
$(".pageAccount .highestCons .val").text(highestCons + "%");
$(".pageAccount .avgCons .val").text(
Format.percentage(totalCons / consCount)
);
let averageCons: number | string = totalCons;
if (Config.alwaysShowDecimalPlaces) {
averageCons = Misc.roundTo2(averageCons / consCount).toFixed(2);
} else {
averageCons = Math.round(averageCons / consCount);
}
$(".pageAccount .avgCons .val").text(averageCons + "%");
let averageConsLast10: number | string = totalCons10;
if (Config.alwaysShowDecimalPlaces) {
averageConsLast10 = Misc.roundTo2(
averageConsLast10 / Math.min(last10, consCount)
).toFixed(2);
} else {
averageConsLast10 = Math.round(
averageConsLast10 / Math.min(last10, consCount)
);
}
$(".pageAccount .avgCons10 .val").text(averageConsLast10 + "%");
$(".pageAccount .avgCons10 .val").text(
Format.percentage(totalCons10 / Math.min(last10, consCount))
);
}
$(".pageAccount .testsStarted .val").text(`${testCount + testRestarts}`);
@ -1020,7 +920,7 @@ async function fillContent(): Promise<void> {
const plus = wpmChangePerHour > 0 ? "+" : "";
$(".pageAccount .group.chart .below .text").text(
`Speed change per hour spent typing: ${
plus + Misc.roundTo2(typingSpeedUnit.fromWpm(wpmChangePerHour))
plus + Format.typingSpeed(wpmChangePerHour, { showDecimalPlaces: true })
} ${Config.typingSpeedUnit}`
);
}

View file

@ -2,6 +2,8 @@ import * as DB from "../db";
import format from "date-fns/format";
import * as Skeleton from "./skeleton";
import { getLanguageDisplayString, isPopupVisible } from "../utils/misc";
import Config from "../config";
import Format from "../utils/format";
type PersonalBest = {
mode2: SharedTypes.Config.Mode2<SharedTypes.Config.Mode>;
@ -12,6 +14,9 @@ const wrapperId = "pbTablesPopupWrapper";
function update(mode: SharedTypes.Config.Mode): void {
$("#pbTablesPopup table tbody").empty();
$($("#pbTablesPopup table thead tr td")[0] as HTMLElement).text(mode);
$($("#pbTablesPopup table thead tr td span.unit")[0] as HTMLElement).text(
Config.typingSpeedUnit
);
const snapshot = DB.getSnapshot();
if (!snapshot) return;
@ -56,16 +61,14 @@ function update(mode: SharedTypes.Config.Mode): void {
<tr>
<td>${mode2memory === pb.mode2 ? "" : pb.mode2}</td>
<td>
${pb.wpm.toFixed(2)}
${Format.typingSpeed(pb.wpm)}
<br />
<span class="sub">${pb.acc ? pb.acc + "%" : "-"}</span>
<span class="sub">${Format.percentage(pb.acc)}</span>
</td>
<td>
${pb.raw ? pb.raw : "-"}
${Format.typingSpeed(pb.raw)}
<br />
<span class="sub">${
pb.consistency ? pb.consistency + "%" : "-"
}</span>
<span class="sub">${Format.percentage(pb.consistency)}</span>
</td>
<td>${pb.difficulty}</td>
<td>${pb.language ? getLanguageDisplayString(pb.language) : "-"}</td>

View file

@ -1,15 +1,14 @@
import Config from "../config";
import * as TestState from "../test/test-state";
import * as ConfigEvent from "../observables/config-event";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import Format from "../utils/format";
export async function update(burst: number): Promise<void> {
if (!Config.showLiveBurst) return;
burst = Math.round(getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(burst));
const burstText = Format.typingSpeed(burst, { showDecimalPlaces: false });
(document.querySelector("#miniTimerAndLiveWpm .burst") as Element).innerHTML =
burst.toString();
(document.querySelector("#liveBurst") as Element).innerHTML =
burst.toString();
burstText;
(document.querySelector("#liveBurst") as Element).innerHTML = burstText;
}
let state = false;

View file

@ -1,7 +1,7 @@
import Config from "../config";
import * as TestState from "../test/test-state";
import * as ConfigEvent from "../observables/config-event";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import Format from "../utils/format";
const liveWpmElement = document.querySelector("#liveWpm") as Element;
const miniLiveWpmElement = document.querySelector(
@ -13,11 +13,9 @@ export function update(wpm: number, raw: number): void {
if (Config.blindMode) {
number = raw;
}
number = Math.round(
getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(number)
);
miniLiveWpmElement.innerHTML = number.toString();
liveWpmElement.innerHTML = number.toString();
const numberText = Format.typingSpeed(number, { showDecimalPlaces: false });
miniLiveWpmElement.innerHTML = numberText;
liveWpmElement.innerHTML = numberText;
}
let state = false;

View file

@ -1,3 +1,4 @@
//TODO: use Format
import { Chart, type PluginChartOptions } from "chart.js";
import Config from "../config";
import * as AdController from "../controllers/ad-controller";
@ -25,6 +26,7 @@ import * as Focus from "./focus";
import * as CustomText from "./custom-text";
import * as CustomTextState from "./../states/custom-text-name";
import * as Funbox from "./funbox/funbox";
import Format from "../utils/format";
import confetti from "canvas-confetti";
import type { AnnotationOptions } from "chartjs-plugin-annotation";
@ -213,24 +215,23 @@ export async function updateGraphPBLine(): Promise<void> {
function updateWpmAndAcc(): void {
let inf = false;
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
if (result.wpm >= 1000) {
inf = true;
}
if (Config.alwaysShowDecimalPlaces) {
$("#result .stats .wpm .top .text").text(Config.typingSpeedUnit);
if (inf) {
$("#result .stats .wpm .bottom").text("Infinite");
} else {
$("#result .stats .wpm .bottom").text(
Misc.roundTo2(typingSpeedUnit.fromWpm(result.wpm)).toFixed(2)
);
}
$("#result .stats .raw .bottom").text(
Misc.roundTo2(typingSpeedUnit.fromWpm(result.rawWpm)).toFixed(2)
);
$("#result .stats .wpm .top .text").text(Config.typingSpeedUnit);
if (inf) {
$("#result .stats .wpm .bottom").text("Infinite");
} else {
$("#result .stats .wpm .bottom").text(Format.typingSpeed(result.wpm));
}
$("#result .stats .raw .bottom").text(Format.typingSpeed(result.rawWpm));
$("#result .stats .acc .bottom").text(
result.acc === 100 ? "100%" : Format.percentage(result.acc)
);
if (Config.alwaysShowDecimalPlaces) {
if (Config.typingSpeedUnit != "wpm") {
$("#result .stats .wpm .bottom").attr(
"aria-label",
@ -245,9 +246,6 @@ function updateWpmAndAcc(): void {
$("#result .stats .raw .bottom").removeAttr("aria-label");
}
$("#result .stats .acc .bottom").text(
result.acc === 100 ? "100%" : Misc.roundTo2(result.acc).toFixed(2) + "%"
);
let time = Misc.roundTo2(result.testDuration).toFixed(2) + "s";
if (result.testDuration > 61) {
time = Misc.secondsToString(Misc.roundTo2(result.testDuration));
@ -261,53 +259,47 @@ function updateWpmAndAcc(): void {
);
} else {
//not showing decimal places
let wpmHover = typingSpeedUnit.convertWithUnitSuffix(result.wpm, true);
let rawWpmHover = typingSpeedUnit.convertWithUnitSuffix(
result.rawWpm,
true
);
const decimalsAndSuffix = {
showDecimalPlaces: true,
suffix: ` ${Config.typingSpeedUnit}`,
};
let wpmHover = Format.typingSpeed(result.wpm, decimalsAndSuffix);
let rawWpmHover = Format.typingSpeed(result.rawWpm, decimalsAndSuffix);
if (Config.typingSpeedUnit != "wpm") {
wpmHover += " (" + result.wpm.toFixed(2) + " wpm)";
rawWpmHover += " (" + result.rawWpm.toFixed(2) + " wpm)";
}
$("#result .stats .wpm .top .text").text(Config.typingSpeedUnit);
$("#result .stats .wpm .bottom").attr("aria-label", wpmHover);
if (inf) {
$("#result .stats .wpm .bottom").text("Infinite");
} else {
$("#result .stats .wpm .bottom").text(
Math.round(typingSpeedUnit.fromWpm(result.wpm))
);
}
$("#result .stats .raw .bottom").text(
Math.round(typingSpeedUnit.fromWpm(result.rawWpm))
);
$("#result .stats .raw .bottom").attr("aria-label", rawWpmHover);
$("#result .stats .acc .bottom").text(Math.floor(result.acc) + "%");
$("#result .stats .acc .bottom").attr(
"aria-label",
`${result.acc === 100 ? "100" : Misc.roundTo2(result.acc).toFixed(2)}% (${
TestInput.accuracy.correct
} correct / ${TestInput.accuracy.incorrect} incorrect)`
`${
result.acc === 100
? "100"
: Format.percentage(result.acc, { showDecimalPlaces: true })
} (${TestInput.accuracy.correct} correct / ${
TestInput.accuracy.incorrect
} incorrect)`
);
}
}
function updateConsistency(): void {
$("#result .stats .consistency .bottom").text(
Format.percentage(result.consistency)
);
if (Config.alwaysShowDecimalPlaces) {
$("#result .stats .consistency .bottom").text(
Misc.roundTo2(result.consistency).toFixed(2) + "%"
);
$("#result .stats .consistency .bottom").attr(
"aria-label",
`${result.keyConsistency.toFixed(2)}% key`
Format.percentage(result.keyConsistency, {
showDecimalPlaces: true,
suffix: " key",
})
);
} else {
$("#result .stats .consistency .bottom").text(
Math.round(result.consistency) + "%"
);
$("#result .stats .consistency .bottom").attr(
"aria-label",
`${result.consistency}% (${result.keyConsistency}% key)`
@ -327,6 +319,7 @@ function updateTime(): void {
"aria-label",
`${result.afkDuration}s afk ${afkSecondsPercent}%`
);
if (Config.alwaysShowDecimalPlaces) {
let time = Misc.roundTo2(result.testDuration).toFixed(2) + "s";
if (result.testDuration > 61) {
@ -416,11 +409,10 @@ export async function updateCrown(): Promise<void> {
Config.lazyMode,
Config.funbox
);
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
pbDiff = Math.abs(result.wpm - lpb);
$("#result .stats .wpm .crown").attr(
"aria-label",
"+" + Misc.roundTo2(typingSpeedUnit.fromWpm(pbDiff))
"+" + Format.typingSpeed(pbDiff, { showDecimalPlaces: true })
);
}

View file

@ -22,6 +22,7 @@ import { debounce } from "throttle-debounce";
import * as ResultWordHighlight from "../elements/result-word-highlight";
import * as ActivePage from "../states/active-page";
import html2canvas from "html2canvas";
import Format from "../utils/format";
const debouncedZipfCheck = debounce(250, async () => {
const supports = await Misc.checkIfLanguageSupportsZipf(Config.language);
@ -1299,9 +1300,8 @@ $(".pageTest #resultWordsHistory").on("mouseenter", ".words .word", (e) => {
.replace(/>/g, "&gt")}
</div>
<div class="speed">
${Math.round(
getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(burst)
)}${Config.typingSpeedUnit}
${Format.typingSpeed(burst, { showDecimalPlaces: false })}
${Config.typingSpeedUnit}
</div>
</div>`
);

View file

@ -474,7 +474,6 @@ declare namespace MonkeyTypes {
type TypingSpeedUnitSettings = {
fromWpm: (number: number) => number;
toWpm: (number: number) => number;
convertWithUnitSuffix: (number: number, withDecimals: boolean) => string;
fullUnitString: string;
histogramDataBucketSize: number;
historyStepSize: number;

View file

@ -0,0 +1,67 @@
import * as Misc from "./misc";
import Config from "../config";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
export type FormatOptions = {
showDecimalPlaces?: boolean;
suffix?: string;
fallback?: string;
};
const FORMAT_DEFAULT_OPTIONS: FormatOptions = {
suffix: "",
fallback: "-",
showDecimalPlaces: undefined,
};
export class Formatting {
constructor(private config: SharedTypes.Config) {}
typingSpeed(
wpm: number | null | undefined,
formatOptions: FormatOptions = FORMAT_DEFAULT_OPTIONS
): string {
const options = { ...FORMAT_DEFAULT_OPTIONS, ...formatOptions };
if (wpm === undefined || wpm === null) return options.fallback ?? "";
const result = getTypingSpeedUnit(this.config.typingSpeedUnit).fromWpm(wpm);
return this.number(result, options);
}
percentage(
percentage: number | null | undefined,
formatOptions: FormatOptions = {}
): string {
const options = { ...FORMAT_DEFAULT_OPTIONS, ...formatOptions };
options.suffix = "%" + (options.suffix ?? "");
return this.number(percentage, options);
}
decimals(
value: number | null | undefined,
formatOptions: FormatOptions = {}
): string {
const options = { ...FORMAT_DEFAULT_OPTIONS, ...formatOptions };
return this.number(value, options);
}
private number(
value: number | null | undefined,
formatOptions: FormatOptions
): string {
if (value === undefined || value === null)
return formatOptions.fallback ?? "";
const suffix = formatOptions.suffix ?? "";
if (
formatOptions.showDecimalPlaces !== undefined
? formatOptions.showDecimalPlaces
: this.config.alwaysShowDecimalPlaces
) {
return Misc.roundTo2(value).toFixed(2) + suffix;
}
return Math.round(value).toString() + suffix;
}
}
export default new Formatting(Config);

View file

@ -1,5 +1,3 @@
import { roundTo2 } from "../utils/misc";
class Unit implements MonkeyTypes.TypingSpeedUnitSettings {
unit: SharedTypes.Config.TypingSpeedUnit;
convertFactor: number;
@ -28,14 +26,6 @@ class Unit implements MonkeyTypes.TypingSpeedUnitSettings {
toWpm(val: number): number {
return val / this.convertFactor;
}
convertWithUnitSuffix(wpm: number, withDecimals: boolean): string {
if (withDecimals) {
return roundTo2(this.fromWpm(wpm)).toFixed(2) + " " + this.unit;
} else {
return Math.round(this.fromWpm(wpm)) + " " + this.unit;
}
}
}
const typingSpeedUnits: Record<SharedTypes.Config.TypingSpeedUnit, Unit> = {

View file

@ -244,7 +244,7 @@
<tr>
<td width="1%">words</td>
<td>
wpm
<span class="unit">wpm</span>
<br />
<span class="sub">accuracy</span>
</td>