import { describe, it, vi } from "vitest" import { arraysEqual, clone, extractNameFromEmail, isValidDescendent, lastFilePathElement, } from "./object" describe("clone", () => { it("deep clones an object so mutations do not leak back", ({ expect }) => { const original = { a: 1, nested: { b: [1, 2] } } const copy = clone(original) copy.nested.b.push(3) expect(copy).not.toBe(original) expect(original.nested.b).toEqual([1, 2]) expect(copy.nested.b).toEqual([1, 2, 3]) }) it.for([ { name: "returns null as-is", input: null }, { name: "returns undefined as-is", input: undefined }, { name: "returns numbers as-is", input: 42 }, { name: "returns strings as-is", input: "hello" }, ])("$name", ({ input }, { expect }) => { expect(clone(input)).toBe(input) }) }) describe("isValidDescendent", () => { // the function only guards nulls and delegates to the DOM contains // check, so a minimal stub is all the element needs to be function element(contains: boolean) { return { contains: vi.fn(() => contains) } as unknown as HTMLElement } it("returns true when the ancestor contains the descendent", ({ expect }) => { const contains = vi.fn(() => true) const ancestor = { contains } as unknown as HTMLElement const descendent = element(false) expect(isValidDescendent(ancestor, descendent)).toBe(true) expect(contains).toHaveBeenCalledTimes(1) expect(contains).toHaveBeenCalledWith(descendent) }) it("returns false when the ancestor does not contain the descendent", ({ expect, }) => { expect(isValidDescendent(element(false), element(false))).toBe(false) }) it.for([ { name: "returns false for a null ancestor", a: null, d: element(true) }, { name: "returns false for a null descendent", a: element(true), d: null }, { name: "returns false for undefined arguments", a: undefined, d: undefined, }, ])("$name", ({ a, d }, { expect }) => { expect(isValidDescendent(a, d)).toBe(false) }) }) describe("lastFilePathElement", () => { it.for([ { name: "returns the file name of a unix path", input: "a/b/c.txt", expected: "c.txt", }, { name: "returns the file name of a windows path", input: "a\\b\\c.txt", expected: "c.txt", }, { name: "ignores a trailing slash", input: "a/b/", expected: "b" }, { name: "returns a bare file name unchanged", input: "c.txt", expected: "c.txt", }, { name: "returns an empty string for an empty path", input: "", expected: "", }, { name: "returns an empty string for a bare slash", input: "/", expected: "", }, ])("$name", ({ input, expected }, { expect }) => { expect(lastFilePathElement(input)).toBe(expected) }) }) describe("arraysEqual", () => { it.for([ { name: "returns true for the same reference", a: [1], b: null, same: true, expected: true, }, { name: "returns true when both are null", a: null, b: null, same: false, expected: true, }, { name: "returns false when only one is null", a: null, b: [1], same: false, expected: false, }, { name: "returns true for equal elements", a: [1, 2], b: [1, 2], same: false, expected: true, }, { name: "returns true for two empty arrays", a: [], b: [], same: false, expected: true, }, { name: "returns false for different lengths", a: [1], b: [1, 2], same: false, expected: false, }, { name: "returns false for different elements", a: [1, 2], b: [1, 3], same: false, expected: false, }, ])("$name", ({ a, b, same, expected }, { expect }) => { expect(arraysEqual(a, same ? a : b)).toBe(expected) }) it("compares elements by reference, not structurally", ({ expect }) => { expect(arraysEqual([{ x: 1 }], [{ x: 1 }])).toBe(false) }) }) describe("extractNameFromEmail", () => { it.for([ { name: "returns the part before the at sign", input: "john.doe@test.io", expected: "john.doe", }, { name: "trims surrounding whitespace", input: " a@test.io ", expected: "a", }, { name: "returns an empty string without an at sign", input: "john.doe", expected: "", }, { name: "returns an empty string for a leading at sign", input: "@test.io", expected: "", }, { name: "returns an empty string for an empty input", input: "", expected: "", }, ])("$name", ({ input, expected }, { expect }) => { expect(extractNameFromEmail(input)).toBe(expected) }) })