Skip to content

Commit

Permalink
fix: use selected typing speed unit on personal best popup (fehmer) (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
fehmer authored Feb 19, 2024
1 parent 4d5085d commit bac837d
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 302 deletions.
201 changes: 201 additions & 0 deletions frontend/__tests__/utils/format.spec.ts
Original file line number Diff line number Diff line change
@@ -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);
}
70 changes: 17 additions & 53 deletions frontend/src/ts/account/pb-tables.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -140,70 +139,35 @@ 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];

if (pbData === undefined) throw new Error("No PB data found");

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) {
Expand Down
41 changes: 25 additions & 16 deletions frontend/src/ts/elements/leaderboards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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>
Expand Down
Loading

0 comments on commit bac837d

Please sign in to comment.