diff --git a/tests/unit/utils/net/query-string.spec.ts b/tests/unit/utils/net/query-string.spec.ts new file mode 100644 index 0000000..8d96f92 --- /dev/null +++ b/tests/unit/utils/net/query-string.spec.ts @@ -0,0 +1,470 @@ +import { QueryString, isQueryString, isURLSearchParams } from "@/utils/net/query-string"; + +describe("QueryString", () => { + describe("constructor", () => { + test("creates a new QueryString instance", () => { + const queryString = new QueryString(); + + expect(queryString.size).toBe(0); + }); + + test("creates a new QueryString instance from a query string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("creates a new QueryString instance from a query string that starts with '?'", () => { + const params = "?key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("creates a new QueryString instance from an iterable", () => { + const params = [["key1", "value1"], ["key2", "value2"]] as Iterable<[string, string]>; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("creates a new QueryString instance from a record", () => { + const params = { key1: "value1", key2: "value2" }; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("filters out null and undefined parameters from an iterable", () => { + const params = [["key1", "value1"], ["key2", null], ["key3", undefined]] as Iterable<[string, string]>; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.has("key2")).toBe(false); + expect(queryString.has("key3")).toBe(false); + }); + + test("filters out null and undefined parameters from a record", () => { + const params = { key1: "value1", key2: null, key3: undefined }; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.has("key2")).toBe(false); + expect(queryString.has("key3")).toBe(false); + }); + + test("stringifies all values in an iterable", () => { + const params = [["key1", "value1"], ["key2", 42], ["key3", true]] as Iterable<[string, unknown]>; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("42"); + expect(queryString.get("key3")).toBe("true"); + }); + + test("stringifies all values in a record", () => { + const params = { key1: "value1", key2: 42, key3: true }; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("42"); + expect(queryString.get("key3")).toBe("true"); + }); + }); + + describe("parse", () => { + test("creates a new QueryString instance from a query string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = QueryString.parse(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("creates a new QueryString instance from a query string that starts with '?'", () => { + const params = "?key1=value1&key2=value2"; + + const queryString = QueryString.parse(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + }); + + describe("size", () => { + test("returns the number of key-value pairs in the query string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.size).toBe(2); + }); + + test("returns 0 when the query string is empty", () => { + const queryString = new QueryString(); + + expect(queryString.size).toBe(0); + }); + + test("returns the correct size after adding a key-value pair", () => { + const queryString = new QueryString(); + queryString.append("key1", "value1"); + + expect(queryString.size).toBe(1); + }); + + test("returns the correct size after removing a key-value pair", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + queryString.delete("key1"); + + expect(queryString.size).toBe(1); + }); + + test("returns the correct size after clearing the query string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + queryString.clear(); + + expect(queryString.size).toBe(0); + }); + }); + + describe("get", () => { + test("returns the value of the first key/value pair with the given key", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("returns undefined if there is no key/value pair with the given key", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.get("key3")).toBeUndefined(); + }); + + test("returns undefined if the query string is empty", () => { + const queryString = new QueryString(); + + expect(queryString.get("key1")).toBeUndefined(); + }); + }); + + describe("append", () => { + test("appends a new key/value pair to the query string", () => { + const queryString = new QueryString(); + + queryString.append("key1", "value1"); + + expect(queryString.get("key1")).toBe("value1"); + }); + + test("appends multiple values to the same key", () => { + const queryString = new QueryString(); + + queryString.append("key1", "value1"); + queryString.append("key1", "value2"); + + expect(queryString.getAll("key1")).toEqual(["value1", "value2"]); + }); + }); + + describe("set", () => { + test("sets a single value associated with the specified key, replacing any existing values", () => { + const queryString = new QueryString("key1=value1"); + + queryString.set("key1", "newValue1"); + + expect(queryString.get("key1")).toBe("newValue1"); + }); + + test("sets a single value associated with the specified key when no previous value exists", () => { + const queryString = new QueryString(); + + queryString.set("key1", "value1"); + + expect(queryString.get("key1")).toBe("value1"); + }); + }); + + describe("delete", () => { + test("removes the entry with the specified key from the query string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + const result = queryString.delete("key1"); + + expect(result).toBe(true); + expect(queryString.get("key1")).toBeUndefined(); + expect(queryString.get("key2")).toBe("value2"); + }); + + test("returns false if no entry with the specified key was found", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + const result = queryString.delete("key3"); + + expect(result).toBe(false); + expect(queryString.get("key1")).toBe("value1"); + expect(queryString.get("key2")).toBe("value2"); + }); + }); + + describe("clear", () => { + test("removes all key/value pairs from the query string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + queryString.clear(); + + expect(queryString.size).toBe(0); + expect(queryString.has("key1")).toBe(false); + expect(queryString.has("key2")).toBe(false); + }); + + test("does nothing when the query string is already empty", () => { + const queryString = new QueryString(); + queryString.clear(); + + expect(queryString.size).toBe(0); + }); + }); + + describe("getString", () => { + test("returns the value of the parameter as a string", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.getString("key1")).toBe("value1"); + expect(queryString.getString("key2")).toBe("value2"); + }); + + test("returns undefined if the parameter is not found", () => { + const queryString = new QueryString(); + + expect(queryString.getString("key1")).toBeUndefined(); + }); + }); + + describe("getBoolean", () => { + test("returns the value of the parameter as a boolean when the value is a boolean string", () => { + const params = "key1=true&key2=false"; + + const queryString = new QueryString(params); + + expect(queryString.getBoolean("key1")).toBe(true); + expect(queryString.getBoolean("key2")).toBe(false); + }); + + test("returns undefined when the parameter is not found", () => { + const queryString = new QueryString(); + + expect(queryString.getBoolean("key1")).toBeUndefined(); + }); + + test("returns undefined when the value of the parameter cannot be converted to a boolean", () => { + const params = "key1=value1"; + + const queryString = new QueryString(params); + + expect(queryString.getBoolean("key1")).toBeUndefined(); + }); + + test("returns true when the value of the parameter is an empty string", () => { + const params = "key1="; + + const queryString = new QueryString(params); + + expect(queryString.getBoolean("key1")).toBe(true); + }); + }); + + describe("getNumber", () => { + test("returns the value of the parameter as a number", () => { + const params = "key1=42"; + + const queryString = new QueryString(params); + + expect(queryString.getNumber("key1")).toBe(42); + }); + + test("returns undefined if the parameter is not found", () => { + const queryString = new QueryString(); + + expect(queryString.getNumber("key1")).toBeUndefined(); + }); + + test("returns undefined if the parameter cannot be converted to a number", () => { + const params = "key1=value1"; + + const queryString = new QueryString(params); + + expect(queryString.getNumber("key1")).toBeUndefined(); + }); + + test("returns the value of the parameter as a number even if it's a float", () => { + const params = "key1=42.42"; + + const queryString = new QueryString(params); + + expect(queryString.getNumber("key1")).toBe(42.42); + }); + }); + + describe("getDate", () => { + test("returns the value of the parameter as a date", () => { + const params = "date=2022-12-31"; + + const queryString = new QueryString(params); + + expect(queryString.getDate("date")).toEqual(new Date("2022-12-31")); + }); + + test("returns undefined if the parameter is not found", () => { + const queryString = new QueryString(); + + expect(queryString.getDate("date")).toBeUndefined(); + }); + + test("returns undefined if the parameter cannot be converted to a date", () => { + const params = "date=not-a-date"; + + const queryString = new QueryString(params); + + expect(queryString.getDate("date")).toBeUndefined(); + }); + }); + + describe("getRegExp", () => { + test("returns the value of the parameter as a RegExp", () => { + const params = "key1=/^value1$/g&key2=/^value2$/g"; + + const queryString = new QueryString(params); + + expect(queryString.getRegExp("key1")).toEqual(/^value1$/g); + expect(queryString.getRegExp("key2")).toEqual(/^value2$/g); + }); + + test("returns undefined if the parameter is not found", () => { + const queryString = new QueryString(); + + expect(queryString.getRegExp("key1")).toBeUndefined(); + }); + + test("returns undefined if the parameter cannot be converted to a RegExp", () => { + const params = "key1=value1&key2=value2"; + + const queryString = new QueryString(params); + + expect(queryString.getRegExp("key1")).toBeUndefined(); + expect(queryString.getRegExp("key2")).toBeUndefined(); + }); + }); + + describe("forEach", () => { + test("calls the specified callback function for each element in the query string", () => { + const params = "key1=value1&key2=value2"; + const callback = jest.fn(); + + const queryString = new QueryString(params); + queryString.forEach(callback); + + expect(callback).toHaveBeenCalledTimes(2); + expect(callback).toHaveBeenNthCalledWith(1, "value1", "key1", queryString); + expect(callback).toHaveBeenNthCalledWith(2, "value2", "key2", queryString); + }); + + test("uses the provided thisArg as `this` when executing the callback function", () => { + const params = "key1=value1"; + const thisArg = {}; + const callback = jest.fn(function(this: unknown) { + expect(this).toBe(thisArg); + }); + + const queryString = new QueryString(params); + queryString.forEach(callback, thisArg); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenNthCalledWith(1, "value1", "key1", queryString); + }); + + test("does not call the callback function if the query string is empty", () => { + const callback = jest.fn(); + + const queryString = new QueryString(); + queryString.forEach(callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe("[Symbol.toStringTag]", () => { + test("returns the same string tag as URLSearchParams", () => { + const urlSearchParams = new URLSearchParams(); + const queryString = new QueryString(); + + expect(queryString[Symbol.toStringTag]).toBe(urlSearchParams[Symbol.toStringTag]); + }); + }); +}); + +describe("isURLSearchParams", () => { + test("returns true when the object is an instance of URLSearchParams", () => { + expect(isURLSearchParams(new URLSearchParams())).toBe(true); + expect(isURLSearchParams(new QueryString())).toBe(true); + }); + + test("returns false when the object is not an instance of URLSearchParams", () => { + expect(isURLSearchParams({})).toBe(false); + }); + + test("returns false when the object is null", () => { + expect(isURLSearchParams(null)).toBe(false); + }); + + test("returns false when the object is undefined", () => { + expect(isURLSearchParams(undefined)).toBe(false); + }); +}); + +describe("isQueryString", () => { + test("returns true when the object is an instance of QueryString", () => { + expect(isQueryString(new QueryString())).toBe(true); + }); + + test("returns false when the object is not an instance of QueryString", () => { + expect(isQueryString(new URLSearchParams())).toBe(false); + expect(isQueryString({})).toBe(false); + }); + + test("returns false when the object is null", () => { + expect(isQueryString(null)).toBe(false); + }); + + test("returns false when the object is undefined", () => { + expect(isQueryString(undefined)).toBe(false); + }); +});