import fs from "fs"; import { getTimeObj, getTimeStr, clamp, parseViscolors, parseIni, normalize, denormalize, segment, moveSelected, spliceIn, getFileExtension, makeCachingFilterFunction } from "./utils"; const fixture = (filename: string) => fs.readFileSync(`./js/__tests__/fixtures/${filename}`, "utf8"); describe("getTimeObj", () => { it("expresses seconds as an object", () => { const actual = getTimeObj(1234); const expected = { minutesFirstDigit: "2", minutesSecondDigit: "0", secondsFirstDigit: "3", secondsSecondDigit: "4" }; expect(actual).toEqual(expected); }); }); describe("getTimeStr", () => { it("expresses seconds as string", () => { const actual = getTimeStr(1234); const expected = "20:34"; expect(actual).toEqual(expected); }); it("pads with only one zero", () => { const actual = getTimeStr(5); const expected = "0:05"; expect(actual).toEqual(expected); }); it("truncates extra minutes", () => { const actual = getTimeStr(540000); const expected = "9000:00"; expect(actual).toEqual(expected); }); }); describe("getFileExtension", () => { it("can get bmp", () => { expect(getFileExtension("foo.bmp")).toBe("bmp"); }); it("can match four char extension", () => { expect(getFileExtension("foo.html")).toBe("html"); }); it("converts to lower case", () => { expect(getFileExtension("foo.BMP")).toBe("bmp"); }); it("returns null if a match is not found", () => { expect(getFileExtension("foo")).toBe(null); }); }); describe("clamp", () => { it("respects the max value", () => { const actual = clamp(101, 0, 100); const expected = 100; expect(actual).toEqual(expected); }); it("respects the min value", () => { const actual = clamp(0, 1, 100); const expected = 1; expect(actual).toEqual(expected); }); it("respects the given value if in range", () => { const actual = clamp(50, 0, 100); const expected = 50; expect(actual).toEqual(expected); }); }); describe("parseViscolors", () => { it("can parse the default viscolors file", () => { const viscolors = fixture("VISCOLOR.TXT"); const actual = parseViscolors(viscolors); const expected = [ "rgb(0,0,0)", "rgb(24,33,41)", "rgb(239,49,16)", "rgb(206,41,16)", "rgb(214,90,0)", "rgb(214,102,0)", "rgb(214,115,0)", "rgb(198,123,8)", "rgb(222,165,24)", "rgb(214,181,33)", "rgb(189,222,41)", "rgb(148,222,33)", "rgb(41,206,16)", "rgb(50,190,16)", "rgb(57,181,16)", "rgb(49,156,8)", "rgb(41,148,0)", "rgb(24,132,8)", "rgb(255,255,255)", "rgb(214,214,222)", "rgb(181,189,189)", "rgb(160,170,175)", "rgb(148,156,165)", "rgb(150,150,150)" ]; expect(actual).toEqual(expected); }); it("can parse a malformed viscolors file", () => { // From https://skins.webamp.org/skin/018ddb394f2bfe49efa70bce27b71cb2/Centra_CSS-102_104-3.wsz/ const viscolors = fixture("CENTRA_VISCOLOR.TXT"); const actual = parseViscolors(viscolors); const expected = [ "rgb(110,150,176)", // 0 "rgb(165,165,165)", // 1 "rgb(55,55,67)", // 2 "rgb(55,55,67)", // 3 "rgb(55,55,67)", // 4 "rgb(55,55,67)", // 5 "rgb(55,55,67)", // 6 "rgb(55,55,67)", // 7 "rgb(55,55,67)", // 8 "rgb(55,55,67)", // 9 "rgb(55,55,67)", // 10 "rgb(55,55,67)", // 11 "rgb(55,55,67)", // 12 "rgb(55,55,67)", // 13 "rgb(55,55,67)", // 14 "rgb(55,55,67)", // 15 "rgb(55,55,67)", // 16 "rgb(55,55,67)", // 17 "rgb(55,55,67)", // 18 "rgb(55,55,67)", // 19 "rgb(55,55,67)", // 20 "rgb(55,55,67)", // 21 "rgb(55,55,67)", // 22 //c 2 "rgb(181,189,189)", // 20 = osc 3 "rgb(148,156,165)", // 21 = osc 4 "rgb(148,156,165)" // 2) = osc 5 (dimmest) ]; expect(actual).toEqual(expected); }); it("does not require commas to separate values", () => { // From https://skins.webamp.org/skin/99c6227d8880e00813a9aa6c4e808c37/valgaav_by_dreamcass-d85bqwp.wsz/ const viscolors = fixture("viscolor_valgaav.txt"); const actual = parseViscolors(viscolors); const expected = [ "rgb(98,111,123)", // color 0 = background "rgb(98,111,123)", // color 1 = dots "rgb(21,21,21)", // 2 = top of spec "rgb(223,176,176)", // 16 "rgb(218,168,168)", // 15 "rgb(211,158,158)", // 14 "rgb(204,147,147)", // 13 "rgb(198,137,137)", // 12 "rgb(191,127,127)", // 11 "rgb(185,117,117)", // 10 "rgb(178,107,107)", // 9 "rgb(172,97,97)", // 8 "rgb(165,86,86)", // 7 "rgb(158,76,76)", // 6 "rgb(152,66,66)", // 5 "rgb(145,56,56)", // 4 "rgb(138,46,46)", // 3 "rgb(138,46,46)", // 17 = bottom of spec "rgb(138,46,46)", // 18 = osc 1 "rgb(158,76,76)", // 19 = osc 2 "rgb(178,107,107)", // 20 = osc 3 "rgb(198,137,137)", // 21 = osc4 "rgb(218,168,168)", // 22 = osc5 "rgb(223,176,176)" // 23 = analyzer peak dots ]; expect(actual).toEqual(expected); }); }); describe("parseIni", () => { it("can parse the default pledit.txt file", () => { const pledit = fixture("PLEDIT.TXT"); const actual = parseIni(pledit); const expected = { text: { normal: "#00FF00", current: "#FFFFFF", normalbg: "#000000", selectedbg: "#0000FF", font: "Arial" } }; expect(actual).toEqual(expected); }); it("can parse TopazAmp's pledit.txt file", () => { const pledit = fixture("PLEDIT_TOPAZ.TXT"); const actual = parseIni(pledit); const expected = { text: { normal: "#319593", current: "#89D8D1", normalbg: "#000000", selectedbg: "#2B4242", font: "Arial", mbbg: "#000000", mbfg: "#89D8D1" } }; expect(actual).toEqual(expected); }); it("allows space around =", () => { const actual = parseIni(` [foo] bar = baz `); const expected = { foo: { bar: "baz" } }; expect(actual).toEqual(expected); }); it("can parse a pledit.txt file with quotes", () => { const pledit = fixture("PLEDIT_WITH_QUOTES.TXT"); const actual = parseIni(pledit); const expected = { text: { normal: "#00FF00", current: "#FFFFFF", normalbg: "#000000", selectedbg: "#0000FF", font: "Ricky's cool font!" } }; expect(actual).toEqual(expected); }); it("allows quotes around values", () => { const actual = parseIni(` [foo] bar = "baz" `); const expected = { foo: { bar: "baz" } }; expect(actual).toEqual(expected); }); }); test("normalize", () => { expect(normalize(1)).toBe(1); expect(normalize(64)).toBe(100); }); test("denormalize", () => { expect(denormalize(1)).toBe(1); expect(denormalize(100)).toBe(64); }); describe("segment", () => { it("can handle min", () => { expect(segment(0, 100, 0, [0, 1, 2])).toBe(0); expect(segment(1, 100, 1, [0, 1, 2])).toBe(0); expect(segment(-1, 100, -1, [0, 1, 2])).toBe(0); }); it("can handle max", () => { //expect(segment(0, 100, 100, [0, 1, 2])).toBe(2); //expect(segment(1, 100, 100, [0, 1, 2])).toBe(2); expect(segment(-1, 100, 100, [0, 1, 2])).toBe(2); }); it("can handle mid", () => { expect(segment(0, 2, 1, [0, 1, 2])).toBe(1); expect(segment(0, 2, 1.5, [0, 1, 2])).toBe(2); expect(segment(1, 3, 2.5, [0, 1, 2])).toBe(2); expect(segment(-1, 2, 0.5, [0, 1, 2])).toBe(1); }); it("can handle various real wold cases", () => { expect(segment(-100, 100, -100, ["left", "center", "right"])).toBe("left"); expect(segment(0, 100, 88, ["left", "center", "right"])).toBe("right"); expect(segment(0, 100, 50, ["left", "center", "right"])).toBe("center"); }); }); describe("moveSelected", () => { it("can drag a single item 1", () => { expect( moveSelected( ["a", "b", "c", "d", "e", "f", "g", "h"], i => new Set([1]).has(i), 1 ) ).toEqual(["a", "c", "b", "d", "e", "f", "g", "h"]); }); it("can drag a single item", () => { expect( moveSelected( ["a", "b", "c", "d", "e", "f", "g", "h"], i => new Set([1]).has(i), 3 ) ).toEqual(["a", "c", "d", "e", "b", "f", "g", "h"]); }); it("can drag consecutive items", () => { expect( moveSelected( ["a", "b", "c", "d", "e", "f", "g", "h"], i => new Set([1, 2]).has(i), 3 ) ).toEqual(["a", "d", "e", "f", "b", "c", "g", "h"]); }); it("works for a simple example", () => { const arr = [true, false, false]; expect(moveSelected(arr, i => arr[i], 1)).toEqual([false, true, false]); }); it("works for a simple negative example", () => { const arr = [false, false, true]; expect(moveSelected(arr, i => arr[i], -1)).toEqual([false, true, false]); }); }); describe("spliceIn", () => { it("is immutable", () => { const original = [1, 2, 3]; const spliced = spliceIn(original, 1, [200]); expect(spliced).not.toBe(original); expect(original).toEqual([1, 2, 3]); }); it("adds values at the given index", () => { const spliced = spliceIn([1, 2, 3], 1, [200]); expect(spliced).toEqual([1, 200, 2, 3]); }); }); describe("makeCachingFilterFunction", () => { test("caches exact queries", () => { const values = ["abc", "b", "c"]; const includes = jest.fn((v, query) => v.includes(query)); const filter = makeCachingFilterFunction(values, includes); expect(filter("c")).toEqual(["abc", "c"]); expect(includes.mock.calls.length).toBe(3); expect(filter("c")).toEqual(["abc", "c"]); expect(includes.mock.calls.length).toBe(3); }); test("caches sub queries", () => { const values = ["a--", "ab-", "abc"]; const includes = jest.fn((v, query) => v.includes(query)); let comparisons = 0; const newComparisons = () => { const recent = includes.mock.calls.length - comparisons; comparisons += recent; return recent; }; const filter = makeCachingFilterFunction(values, includes); // Intial search expect(filter("ab")).toEqual(["ab-", "abc"]); expect(newComparisons()).toBe(3); // Looks at all elements // Second search where original search is a prefix expect(filter("abc")).toEqual(["abc"]); expect(newComparisons()).toBe(2); // Only reconsiders the previous matches // Unique search expect(filter("b")).toEqual(["ab-", "abc"]); // Looks at all elements expect(newComparisons()).toBe(3); // Reconsiders all elements expect(filter("bc")).toEqual(["abc"]); // Only reconsidres the matches that already include `b` expect(newComparisons()).toBe(2); // Go back to the initial serach expect(filter("ab")).toEqual(["ab-", "abc"]); expect(newComparisons()).toBe(0); // Result is cached // A variation on the second search expect(filter("abcd")).toEqual([]); expect(newComparisons()).toBe(1); // Only recondsiders the results of `abc` }); test("big data", () => { const values = [...Array(10000)].map((val, i) => String(i)); const includes = jest.fn((v, query) => v.includes(query)); let comparisons = 0; const newComparisons = () => { const recent = includes.mock.calls.length - comparisons; comparisons += recent; return recent; }; const filter = makeCachingFilterFunction(values, includes); // Intial search expect(filter("").length).toEqual(10000); expect(newComparisons()).toBe(0); // Looks at zero expect(filter("1").length).toEqual(3439); expect(newComparisons()).toBe(10000); // Looks at all elements expect(filter("12").length).toEqual(299); expect(newComparisons()).toBe(3439); expect(filter("123").length).toEqual(20); expect(newComparisons()).toBe(299); expect(filter("1234").length).toEqual(1); expect(newComparisons()).toBe(20); expect(filter("12345").length).toEqual(0); expect(newComparisons()).toBe(1); // A variation on the initial non-empty query expect(filter("11").length).toEqual(280); expect(newComparisons()).toBe(3439); expect(filter("111").length).toEqual(19); expect(newComparisons()).toBe(280); expect(filter("1111").length).toEqual(1); expect(newComparisons()).toBe(19); }); });