import { IGNORE_CASE_EQUALITY_COMPARER } from "@/utils/comparison/string-equality-comparer"; import { isMap, isReadOnlyMap, isMultiMap, ArrayMap, MultiMap } from "@/utils/collections/map"; const readOnlyMapLike = { keys: () => {}, values: () => {}, entries: () => {}, get: () => {}, has: () => {}, [Symbol.iterator]: () => {}, }; const mapLike = { ...readOnlyMapLike, set: () => {}, delete: () => {}, }; const multiMapLike = { ...mapLike, append: () => {}, }; describe("isMap", () => { test("returns true for Map instances", () => { expect(isMap(new Map())).toBe(true); }); test("returns true for Map-like objects", () => { expect(isMap(mapLike)).toBe(true); }); test("returns false for non-Map-like objects", () => { expect(isMap({})).toBe(false); }); test("returns false for null and undefined", () => { expect(isMap(null)).toBe(false); expect(isMap(undefined)).toBe(false); }); }); describe("isReadOnlyMap", () => { test("returns true for Map instances", () => { expect(isReadOnlyMap(new Map())).toBe(true); }); test("returns true for ReadOnlyMap-like objects", () => { expect(isReadOnlyMap(readOnlyMapLike)).toBe(true); }); test("returns false for non-ReadOnlyMap-like objects", () => { expect(isReadOnlyMap({})).toBe(false); }); test("returns false for null and undefined", () => { expect(isReadOnlyMap(null)).toBe(false); expect(isReadOnlyMap(undefined)).toBe(false); }); }); describe("isMultiMap", () => { test("returns true for MultiMap instances", () => { expect(isMultiMap(new MultiMap())).toBe(true); }); test("returns true for MultiMap-like objects", () => { expect(isMultiMap(multiMapLike)).toBe(true); }); test("returns false for non-MultiMap-like objects", () => { expect(isMultiMap({})).toBe(false); }); test("returns false for null and undefined", () => { expect(isMultiMap(null)).toBe(false); expect(isMultiMap(undefined)).toBe(false); }); }); describe("ArrayMap", () => { describe("constructor", () => { test("creates an empty map when no parameters are provided", () => { const map = new ArrayMap(); expect(map.size).toBe(0); }); test("creates a map from an iterable of entries", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); expect(map.size).toBe(2); expect(map.get(1)).toBe("one"); expect(map.get(2)).toBe("two"); }); test("creates a map from an iterable of entries and a custom comparer", () => { const map = new ArrayMap([["one", 1], ["two", 2]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.size).toBe(2); expect(map.get("ONE")).toBe(1); expect(map.get("TWO")).toBe(2); }); test("creates a map from an iterable of entries, and eliminates duplicates", () => { const map = new ArrayMap([[1, "zero"], [2, "two"], [1, "one"]]); expect(map.size).toBe(2); expect(map.get(1)).toBe("one"); expect(map.get(2)).toBe("two"); }); test("creates a map from an iterable of entries and a custom comparer, and eliminates duplicates", () => { const map = new ArrayMap([["ONE", -1], ["two", 2], ["one", 1]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.size).toBe(2); expect(map.get("ONE")).toBe(1); expect(map.get("TWO")).toBe(2); }); }); describe("get", () => { test("returns value associated with the specified key", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); expect(map.get(1)).toBe("one"); }); test("respects custom comparer when retrieving value by key", () => { const map = new ArrayMap([["one", 1]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.get("one")).toBe(1); expect(map.get("One")).toBe(1); expect(map.get("ONE")).toBe(1); }); test("returns undefined if the key is not found", () => { const map = new ArrayMap(); expect(map.get(1)).toBeUndefined(); }); }); describe("set", () => { test("adds a new key-value pair if the key is not present", () => { const map = new ArrayMap(); map.set(1, "one"); expect(map.get(1)).toBe("one"); expect(map.size).toBe(1); }); test("respects custom comparer when setting value by key", () => { const map = new ArrayMap([["one", 1]], IGNORE_CASE_EQUALITY_COMPARER); map.set("Two", 2); expect(map.get("two")).toBe(2); map.set("TWO", 3); expect(map.get("two")).toBe(3); }); test("updates the value if the key is already present", () => { const map = new ArrayMap([[1, "one"]]); map.set(1, "updated"); expect(map.get(1)).toBe("updated"); expect(map.size).toBe(1); }); }); describe("has", () => { test("returns true if the key is present", () => { const map = new ArrayMap([[1, "one"]]); expect(map.has(1)).toBe(true); }); test("respects custom comparer when checking for key presence", () => { const map = new ArrayMap([["one", 1]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.has("one")).toBe(true); expect(map.has("One")).toBe(true); expect(map.has("ONE")).toBe(true); }); test("returns false if the key is not present", () => { const map = new ArrayMap(); expect(map.has(1)).toBe(false); }); }); describe("delete", () => { test("removes the entry with the specified key", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); expect(map.delete(1)).toBe(true); expect(map.has(1)).toBe(false); expect(map.size).toBe(1); }); test("respects custom comparer when deleting by key", () => { const map = new ArrayMap([["one", 1]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.delete("One")).toBe(true); expect(map.has("one")).toBe(false); expect(map.delete("ONE")).toBe(false); }); test("returns false if the key is not present", () => { const map = new ArrayMap(); expect(map.delete(1)).toBe(false); }); }); describe("clear", () => { test("removes all entries", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); map.clear(); expect(map.size).toBe(0); expect(map.get(1)).toBeUndefined(); expect(map.has(1)).toBe(false); }); }); describe("keys", () => { test("returns an iterator over the keys", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); const keys = Array.from(map.keys()); expect(keys).toEqual([1, 2]); }); }); describe("values", () => { test("returns an iterator over the values", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); const values = Array.from(map.values()); expect(values).toEqual(["one", "two"]); }); }); describe("entries", () => { test("returns an iterator over the entries", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); const entries = Array.from(map.entries()); expect(entries).toEqual([[1, "one"], [2, "two"]]); }); }); describe("forEach", () => { test("calls the specified callback function for each entry", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); const callback = jest.fn(); map.forEach(callback); expect(callback).toHaveBeenCalledTimes(2); expect(callback).toHaveBeenCalledWith("one", 1, map); expect(callback).toHaveBeenCalledWith("two", 2, map); }); test("binds the callback function to the provided thisArg", () => { const map = new ArrayMap([[1, "one"]]); const thisArg = {}; map.forEach(function(this: typeof thisArg) { expect(this).toBe(thisArg); }, thisArg); }); }); describe("[Symbol.iterator]", () => { test("returns an iterator over the entries", () => { const map = new ArrayMap([[1, "one"], [2, "two"]]); const entries = Array.from(map[Symbol.iterator]()); expect(entries).toEqual([[1, "one"], [2, "two"]]); }); }); describe("[Symbol.toStringTag]", () => { test("returns 'Map'", () => { const map = new ArrayMap(); expect(map[Symbol.toStringTag]).toBe("Map"); }); }); }); describe("MultiMap", () => { describe("constructor", () => { test("creates an empty map when no parameters are provided", () => { const map = new MultiMap(); expect(map.size).toBe(0); }); test("creates a map from an iterable of entries", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); expect(map.size).toBe(2); expect(map.getFirst(1)).toBe("one"); expect(map.getFirst(2)).toBe("two"); }); test("creates a map from an iterable of entries and a custom comparer", () => { const map = new MultiMap([["one", [1]], ["two", [2]]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.size).toBe(2); expect(map.getFirst("ONE")).toBe(1); expect(map.getFirst("TWO")).toBe(2); }); test("creates a map from an iterable of entries, and eliminates duplicates", () => { const map = new MultiMap([[1, ["zero"]], [2, ["two"]], [1, ["one"]]]); expect(map.size).toBe(2); expect(map.getFirst(1)).toBe("one"); expect(map.getFirst(2)).toBe("two"); }); test("creates a map from an iterable of entries and a custom comparer, and eliminates duplicates", () => { const map = new MultiMap([["ONE", [-1]], ["two", [2]], ["one", [1]]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.size).toBe(2); expect(map.getFirst("ONE")).toBe(1); expect(map.getFirst("TWO")).toBe(2); }); }); describe("get", () => { test("returns value associated with the specified key", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); expect(map.get(1)).toEqual(["one"]); }); test("respects custom comparer when retrieving value by key", () => { const map = new MultiMap([["one", [1]]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.get("one")).toEqual([1]); expect(map.get("One")).toEqual([1]); expect(map.get("ONE")).toEqual([1]); }); test("returns undefined if the key is not found", () => { const map = new MultiMap(); expect(map.get(1)).toBeUndefined(); }); }); describe("getFirst", () => { test("returns the first value for a given key", () => { const map = new MultiMap([[1, ["one", "One", "ONE"]]]); expect(map.getFirst(1)).toBe("one"); }); test("respects custom comparer when retrieving the first value by key", () => { const map = new MultiMap([["one", [1, -1]]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.getFirst("one")).toEqual(1); expect(map.getFirst("One")).toEqual(1); expect(map.getFirst("ONE")).toEqual(1); }); test("returns undefined if the key is not found", () => { const map = new MultiMap(); expect(map.getFirst(1)).toBeUndefined(); }); }); describe("set", () => { test("adds a new key-value pair if the key is not present", () => { const map = new MultiMap(); map.set(1, ["one"]); expect(map.getFirst(1)).toBe("one"); expect(map.size).toBe(1); }); test("updates the value if the key is already present", () => { const map = new MultiMap([[1, ["one"]]]); map.set(1, ["updated"]); expect(map.getFirst(1)).toBe("updated"); expect(map.size).toBe(1); }); test("sets a single value for a given key", () => { const map = new MultiMap(); map.set("one", 1); expect(map.get("one")).toEqual([1]); }); test("respects custom comparer when setting value by key", () => { const map = new MultiMap([["one", [1]]], IGNORE_CASE_EQUALITY_COMPARER); map.set("Two", [2]); expect(map.getFirst("two")).toBe(2); map.set("TWO", 3); expect(map.getFirst("two")).toBe(3); }); }); describe("append", () => { test("appends a single value to existing values for a given key", () => { const map = new MultiMap([["one", [1]]]); map.append("one", -1); expect(map.get("one")).toEqual([1, -1]); }); test("appends multiple values to existing values for a given key", () => { const map = new MultiMap([[1, ["one"]]]); map.append(1, ["One", "ONE"]); expect(map.get(1)).toEqual(["one", "One", "ONE"]); }); test("respects custom comparer when appending values by key", () => { const map = new MultiMap([["one", [1]]], IGNORE_CASE_EQUALITY_COMPARER); map.append("One", -1); map.append("ONE", [1, -1]); expect(map.get("one")).toEqual([1, -1, 1, -1]); }); }); describe("has", () => { test("returns true if the key is present", () => { const map = new MultiMap([[1, ["one"]]]); expect(map.has(1)).toBe(true); }); test("respects custom comparer when checking for key presence", () => { const map = new MultiMap([["one", [1]]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.has("one")).toBe(true); expect(map.has("One")).toBe(true); expect(map.has("ONE")).toBe(true); }); test("returns false if the key is not present", () => { const map = new MultiMap(); expect(map.has(1)).toBe(false); }); }); describe("delete", () => { test("removes the entry with the specified key", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); expect(map.delete(1)).toBe(true); expect(map.has(1)).toBe(false); expect(map.size).toBe(1); }); test("deletes a specific value for a given key", () => { const map = new MultiMap([[1, ["one", "One", "ONE"]]]); expect(map.delete(1, "One")).toBe(true); expect(map.get(1)).toEqual(["one", "ONE"]); }); test("deletes a specific value for a given key using a custom comparer", () => { const map = new MultiMap([[1, ["one", "not one"]]]); expect(map.delete(1, "ONE", IGNORE_CASE_EQUALITY_COMPARER)).toBe(true); expect(map.get(1)).toEqual(["not one"]); }); test("respects custom comparer when deleting by key", () => { const map = new MultiMap([["one", [1]], ["two", [2, -2]]], IGNORE_CASE_EQUALITY_COMPARER); expect(map.delete("One")).toBe(true); expect(map.has("one")).toBe(false); expect(map.delete("ONE")).toBe(false); expect(map.delete("TWO", -2)).toBe(true); expect(map.has("Two")).toBe(true); expect(map.get("two")).toEqual([2]); }); test("returns false if the key is not present", () => { const map = new MultiMap(); expect(map.delete(1)).toBe(false); }); }); describe("clear", () => { test("removes all entries", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); map.clear(); expect(map.size).toBe(0); expect(map.get(1)).toBeUndefined(); expect(map.has(1)).toBe(false); }); }); describe("keys", () => { test("returns an iterator over the keys", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); const keys = Array.from(map.keys()); expect(keys).toEqual([1, 2]); }); }); describe("values", () => { test("returns an iterator over the values", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); const values = Array.from(map.values()); expect(values).toEqual([["one"], ["two"]]); }); }); describe("flatValues", () => { test("returns an iterator over all values", () => { const map = new MultiMap([[1, ["one", "One"]], [2, ["two", "Two"]]]); const values = Array.from(map.flatValues()); expect(values).toEqual(["one", "One", "two", "Two"]); }); }); describe("entries", () => { test("returns an iterator over the entries", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); const entries = Array.from(map.entries()); expect(entries).toEqual([[1, ["one"]], [2, ["two"]]]); }); }); describe("flatEntries", () => { test("returns an iterable of key-value pairs, with each key associated with a single value", () => { const map = new MultiMap([[1, ["one", "One"]], [2, ["two", "Two"]]]); const entries = Array.from(map.flatEntries()); expect(entries).toEqual([[1, "one"], [1, "One"], [2, "two"], [2, "Two"]]); }); }); describe("forEach", () => { test("calls the specified callback function for each entry", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); const callback = jest.fn(); map.forEach(callback); expect(callback).toHaveBeenCalledTimes(2); expect(callback).toHaveBeenCalledWith(["one"], 1, map); expect(callback).toHaveBeenCalledWith(["two"], 2, map); }); test("binds the callback function to the provided thisArg", () => { const map = new MultiMap([[1, ["one"]]]); const thisArg = {}; map.forEach(function(this: typeof thisArg) { expect(this).toBe(thisArg); }, thisArg); }); }); describe("forEachFlat", () => { test("calls the callback function for each standalone value coupled with its key", () => { const map = new MultiMap([[1, ["one", "One"]], [2, ["two", "Two"]]]); const callbackFn = jest.fn(); map.forEachFlat(callbackFn); expect(callbackFn).toHaveBeenCalledTimes(4); expect(callbackFn).toHaveBeenNthCalledWith(1, "one", 1, map); expect(callbackFn).toHaveBeenNthCalledWith(2, "One", 1, map); expect(callbackFn).toHaveBeenNthCalledWith(3, "two", 2, map); expect(callbackFn).toHaveBeenNthCalledWith(4, "Two", 2, map); }); test("binds the callback function to the provided thisArg", () => { const map = new MultiMap([[1, ["one"]]]); const thisArg = {}; map.forEachFlat(function(this: typeof thisArg) { expect(this).toBe(thisArg); }, thisArg); }); }); describe("[Symbol.iterator]", () => { test("returns an iterator over the entries", () => { const map = new MultiMap([[1, ["one"]], [2, ["two"]]]); const entries = Array.from(map[Symbol.iterator]()); expect(entries).toEqual([[1, ["one"]], [2, ["two"]]]); }); }); describe("[Symbol.toStringTag]", () => { test("returns 'Map'", () => { const map = new MultiMap(); expect(map[Symbol.toStringTag]).toBe("Map"); }); }); });