import { describe, it, expect, afterAll, vi } from "vitest"; import * as Misc from "../../src/utils/misc"; import { ObjectId } from "mongodb"; describe("Misc Utils", () => { afterAll(() => { vi.useRealTimers(); }); describe("matchesAPattern", () => { const testCases = [ { pattern: "eng.*", cases: ["english", "aenglish", "en", "eng"], expected: [true, false, false, true], }, { pattern: "\\d+", cases: ["b", "2", "331", "1a"], expected: [false, true, true, false], }, { pattern: "(hi|hello)", cases: ["hello", "hi", "hillo", "hi hello"], expected: [true, true, false, false], }, { pattern: ".+", cases: ["a2", "b2", "c1", ""], expected: [true, true, true, false], }, ]; it.each(testCases)( "matchesAPattern with $pattern", ({ pattern, cases, expected }) => { cases.forEach((caseValue, index) => { expect(Misc.matchesAPattern(caseValue, pattern)).toBe( expected[index], ); }); }, ); }); describe("kogascore", () => { const testCases = [ { wpm: 214.8, acc: 93.04, timestamp: 1653586489000, expectedScore: 1214800930423111, }, { wpm: 214.8, acc: 93.04, timestamp: 1653601763000, expectedScore: 1214800930407837, }, { wpm: 199.37, acc: 97.69, timestamp: 1653588809000, expectedScore: 1199370976920791, }, { wpm: 196.2, acc: 96.07, timestamp: 1653591901000, expectedScore: 1196200960717699, }, { wpm: 196.205, acc: 96.075, timestamp: 1653591901000, expectedScore: 1196210960817699, }, { // this one is particularly important - in JS 154.39 * 100 is equal to 15438.999999999998 // thanks floating point errors! wpm: 154.39, acc: 96.14, timestamp: 1740333827000, expectedScore: 1154390961421373, }, ]; it.each(testCases)( "kogascore with wpm:$wpm, acc:$acc, timestamp:$timestamp = $expectedScore", ({ wpm, acc, timestamp, expectedScore }) => { expect(Misc.kogascore(wpm, acc, timestamp)).toBe(expectedScore); }, ); }); describe("identity", () => { const testCases = [ { input: "", expected: "string", }, { input: {}, expected: "object", }, { input: 0, expected: "number", }, { input: null, expected: "null", }, { input: undefined, expected: "undefined", }, ]; it.each(testCases)( "identity with $input = $expected", ({ input, expected }) => { expect(Misc.identity(input)).toBe(expected); }, ); }); describe("flattenObjectDeep", () => { const testCases = [ { obj: { a: { b: { c: 1, }, }, d: 2, e: [], }, expected: { "a.b.c": 1, d: 2, e: [], }, }, { obj: { a: { b: { c: 1, }, }, d: { e: { f: 2, g: 3, }, }, }, expected: { "a.b.c": 1, "d.e.f": 2, "d.e.g": 3, }, }, { obj: { a: { b: { c: 1, d: { e: 2, f: 3, g: {}, }, }, }, }, expected: { "a.b.c": 1, "a.b.d.e": 2, "a.b.d.f": 3, "a.b.d.g": {}, }, }, { obj: {}, expected: {}, }, ]; it.each(testCases)( "flattenObjectDeep with $obj = $expected", ({ obj, expected }) => { expect(Misc.flattenObjectDeep(obj)).toEqual(expected); }, ); }); it("sanitizeString", () => { const testCases = [ { input: "h̶̼͔̭͈̏́̀́͋͜ͅe̵̺̞̦̫̫͔̋́̅̅̃̀͝͝ļ̶̬̯͚͇̺͍̞̫̟͖͋̓͛̆̒̓͜ĺ̴̗̘͇̬̆͂͌̈͊͝͝ỡ̴̡̦̩̠̞̐̃͆̚͠͝", expected: "hello", }, { input: "hello", expected: "hello", }, { input: "hel lo", expected: "hel lo", }, { input: " hel lo ", expected: "hel lo", }, { input: "", expected: "", }, { input: " \n\n\n", expected: "", }, { input: undefined, expected: undefined, }, ]; testCases.forEach(({ input, expected }) => { expect(Misc.sanitizeString(input)).toEqual(expected); }); }); it("getOrdinalNumberString", () => { const testCases = [ { input: 0, output: "0th", }, { input: 1, output: "1st", }, { input: 2, output: "2nd", }, { input: 3, output: "3rd", }, { input: 4, output: "4th", }, { input: 10, output: "10th", }, { input: 11, output: "11th", }, { input: 12, output: "12th", }, { input: 13, output: "13th", }, { input: 100, output: "100th", }, { input: 101, output: "101st", }, { input: 102, output: "102nd", }, { input: 103, output: "103rd", }, { input: 104, output: "104th", }, { input: 93589423, output: "93589423rd", }, ]; testCases.forEach(({ input, output }) => { expect(Misc.getOrdinalNumberString(input)).toEqual(output); }); }); it("formatSeconds", () => { const testCases = [ { seconds: 5, expected: "5 seconds", }, { seconds: 65, expected: "1.08 minutes", }, { seconds: Misc.HOUR_IN_SECONDS, expected: "1 hour", }, { seconds: Misc.DAY_IN_SECONDS, expected: "1 day", }, { seconds: Misc.WEEK_IN_SECONDS, expected: "1 week", }, { seconds: Misc.YEAR_IN_SECONDS, expected: "1 year", }, { seconds: 2 * Misc.YEAR_IN_SECONDS, expected: "2 years", }, { seconds: 4 * Misc.YEAR_IN_SECONDS, expected: "4 years", }, { seconds: 3 * Misc.WEEK_IN_SECONDS, expected: "3 weeks", }, { seconds: Misc.MONTH_IN_SECONDS * 4, expected: "4 months", }, { seconds: Misc.MONTH_IN_SECONDS * 11, expected: "11 months", }, ]; testCases.forEach(({ seconds, expected }) => { expect(Misc.formatSeconds(seconds)).toBe(expected); }); }); describe("replaceObjectId", () => { it("replaces objecId with string", () => { const fromDatabase = { _id: new ObjectId(), test: "test", number: 1, }; expect(Misc.replaceObjectId(fromDatabase)).toStrictEqual({ _id: fromDatabase._id.toHexString(), test: "test", number: 1, }); }); it("ignores null values", () => { expect(Misc.replaceObjectId(null)).toBeNull(); }); }); describe("replaceObjectIds", () => { it("replaces objecIds with string", () => { const fromDatabase = { _id: new ObjectId(), test: "test", number: 1, }; const fromDatabase2 = { _id: new ObjectId(), test: "bob", number: 2, }; expect( Misc.replaceObjectIds([fromDatabase, fromDatabase2]), ).toStrictEqual([ { _id: fromDatabase._id.toHexString(), test: "test", number: 1, }, { _id: fromDatabase2._id.toHexString(), test: "bob", number: 2, }, ]); }); it("handles undefined", () => { expect(Misc.replaceObjectIds(undefined as any)).toBeUndefined(); }); }); describe("omit()", () => { it("should omit a single key", () => { const input = { a: 1, b: 2, c: 3 }; const result = Misc.omit(input, ["b"]); expect(result).toEqual({ a: 1, c: 3 }); }); it("should omit multiple keys", () => { const input = { a: 1, b: 2, c: 3, d: 4 }; const result = Misc.omit(input, ["a", "d"]); expect(result).toEqual({ b: 2, c: 3 }); }); it("should return the same object if no keys are omitted", () => { const input = { x: 1, y: 2 }; const result = Misc.omit(input, []); expect(result).toEqual({ x: 1, y: 2 }); }); it("should not mutate the original object", () => { const input = { foo: "bar", baz: "qux" }; const copy = { ...input }; Misc.omit(input, ["baz"]); expect(input).toEqual(copy); }); it("should ignore keys that do not exist", () => { const input = { a: 1, b: 2 }; const result = Misc.omit(input, "c" as any); // allow a non-existing key expect(result).toEqual({ a: 1, b: 2 }); }); it("should work with different value types", () => { const input = { str: "hello", num: 123, bool: true, obj: { x: 1 }, arr: [1, 2, 3], }; const result = Misc.omit(input, ["bool", "arr"]); expect(result).toEqual({ str: "hello", num: 123, obj: { x: 1 }, }); }); }); describe("isPlainObject", () => { it("should return true for plain objects", () => { expect(Misc.isPlainObject({})).toBe(true); expect(Misc.isPlainObject({ a: 1, b: 2 })).toBe(true); expect(Misc.isPlainObject(Object.create(Object.prototype))).toBe(true); }); it("should return false for arrays", () => { expect(Misc.isPlainObject([])).toBe(false); expect(Misc.isPlainObject([1, 2, 3])).toBe(false); }); it("should return false for null", () => { expect(Misc.isPlainObject(null)).toBe(false); }); it("should return false for primitives", () => { expect(Misc.isPlainObject(123)).toBe(false); expect(Misc.isPlainObject("string")).toBe(false); expect(Misc.isPlainObject(true)).toBe(false); expect(Misc.isPlainObject(undefined)).toBe(false); expect(Misc.isPlainObject(Symbol("sym"))).toBe(false); }); it("should return false for objects with different prototypes", () => { // oxlint-disable-next-line no-extraneous-class class MyClass {} expect(Misc.isPlainObject(new MyClass())).toBe(false); expect(Misc.isPlainObject(Object.create(null))).toBe(false); expect(Misc.isPlainObject(new Date())).toBe(false); expect(Misc.isPlainObject(new Map())).toBe(false); expect(Misc.isPlainObject(new Set())).toBe(false); }); it("should return false for functions", () => { // oxlint-disable-next-line no-empty-function expect(Misc.isPlainObject(function () {})).toBe(false); // oxlint-disable-next-line no-empty-function expect(Misc.isPlainObject(() => {})).toBe(false); }); }); });