1import { describe, expect, it } from "bun:test";
2import ignore, { isPathValid } from "../src/ignore.ts";
3
4describe("ignore v7.0.5 options and public methods", () => {
5 it("adds another Ignore instance", () => {
6 const a = ignore().add([".abc/*", "!.abc/d/"]);
7 const b = ignore().add(a).add("!.abc/e/");
8 const paths = [".abc/a.js", ".abc/d/e.js", ".abc/e/e.js"];
9
10 expect(a.filter(paths)).toEqual([".abc/d/e.js"]);
11 expect(b.filter(paths)).toEqual([".abc/d/e.js", ".abc/e/e.js"]);
12 });
13
14 it("cannot invoke the instance constructor without new", () => {
15 const { constructor } = ignore();
16 expect(() => (constructor as () => unknown)()).toThrow();
17 });
18
19 it("accepts an Ignore-shaped instance from another module copy", () => {
20 const source = ignore().add([".abc/*", "!.abc/d/"]);
21 const compatible = {
22 _rules: {
23 _rules: (source as any)._rules._rules.slice(),
24 },
25 [Symbol.for("node-ignore")]: true,
26 };
27 const matcher = ignore()
28 .add(compatible as any)
29 .add("!.abc/e/");
30
31 expect(matcher.filter([".abc/a.js", ".abc/d/e.js", ".abc/e/e.js"])).toEqual(
32 [".abc/d/e.js", ".abc/e/e.js"],
33 );
34 });
35
36 it("respects ignorecase", () => {
37 const matcher = ignore({ ignorecase: false }).add("*.[jJ][pP]g");
38 expect(matcher.ignores("a.jpg")).toBe(true);
39 expect(matcher.ignores("a.JPg")).toBe(true);
40 expect(matcher.ignores("a.JPG")).toBe(false);
41 });
42
43 it("respects ignorecase without leaking the compiled-rule cache", () => {
44 const rule = "*.[jJ][pP]g";
45 expect(ignore({ ignorecase: false }).add(rule).ignores("a.JPG")).toBe(
46 false,
47 );
48 expect(ignore({ ignorecase: true }).add(rule).ignores("a.JPG")).toBe(true);
49 });
50
51 it("rejects invalid paths", () => {
52 const matcher = ignore();
53 expect(() => matcher.ignores("")).toThrow();
54 expect(() => matcher.ignores("/a")).toThrow("path.relative");
55 expect(() => matcher.filter([""])).toThrow();
56 expect(() => [""].filter(matcher.createFilter())).toThrow();
57 });
58
59 it("validates paths", () => {
60 expect([".", "./foo", "../foo", "/foo", "foo"].filter(isPathValid)).toEqual(
61 ["foo"],
62 );
63 });
64
65 it("returns ignored and unignored state from test", () => {
66 const cases: Array<
67 [string | string[] | undefined, string, boolean, boolean]
68 > = [
69 [undefined, "foo", false, false],
70 ["bar", "foo", false, false],
71 ["!foo", "foo", false, true],
72 [["foo", "!foo"], "foo", false, true],
73 [["foo", "!foo"], "foo/bar", false, false],
74 [["*.js", "!a/a.js"], "a/a.js", false, true],
75 ...(process.platform === "win32"
76 ? []
77 : ([[undefined, "...", false, false]] as Array<
78 [undefined, string, boolean, boolean]
79 >)),
80 ];
81
82 for (const [patterns, path, ignored, unignored] of cases) {
83 const matcher = ignore();
84 if (patterns) matcher.add(patterns);
85 expect(matcher.test(path)).toEqual({ ignored, unignored });
86 }
87 });
88
89 it("optionally permits relative paths", () => {
90 expect(
91 ignore({ allowRelativePaths: true }).add("foo").ignores("../foo/bar.js"),
92 ).toBe(true);
93 expect(() => ignore().ignores("../foo/bar.js")).toThrow("path.relative");
94 expect(() => ignore().add("foo").ignores("/foo/bar.js")).toThrow(
95 "path.relative",
96 );
97 });
98});