ag-toolkit 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/git.js +1 -1
- package/dist/lib/arg-parser.d.ts +9 -5
- package/dist/lib/arg-parser.js +14 -0
- package/dist/tests/arg-parser.test.d.ts +1 -0
- package/dist/tests/arg-parser.test.js +404 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +234 -0
- package/dist/tests/format.test.d.ts +1 -0
- package/dist/tests/format.test.js +76 -0
- package/dist/tests/isDeepEqual.test.d.ts +1 -0
- package/dist/tests/isDeepEqual.test.js +203 -0
- package/dist/tests/isValidBranchName.test.d.ts +1 -0
- package/dist/tests/isValidBranchName.test.js +89 -0
- package/dist/tests/match.test.d.ts +1 -0
- package/dist/tests/match.test.js +86 -0
- package/dist/tests/package.test.d.ts +1 -0
- package/dist/tests/package.test.js +77 -0
- package/dist/tests/sanitizeString.test.d.ts +1 -0
- package/dist/tests/sanitizeString.test.js +48 -0
- package/package.json +10 -10
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { findLocalConfigPath, findUp, getGlobalConfigPath, loadConfig, loadLocalConfig, readConfigFile, resetGlobalConfig, writeConfig, writeGlobalConfig, writeLocalConfig, } from "../lib/config.js";
|
|
6
|
+
describe("config", () => {
|
|
7
|
+
let testDir;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
testDir = await fs.mkdtemp(join(tmpdir(), "config-test-"));
|
|
10
|
+
});
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
describe("findUp", () => {
|
|
15
|
+
test("should find file in current directory", async () => {
|
|
16
|
+
const testFile = "test-config.json";
|
|
17
|
+
await fs.writeFile(join(testDir, testFile), "{}");
|
|
18
|
+
const result = await findUp(testFile, testDir);
|
|
19
|
+
expect(result).toBe(join(testDir, testFile));
|
|
20
|
+
});
|
|
21
|
+
test("should find file in parent directory", async () => {
|
|
22
|
+
const testFile = "test-config.json";
|
|
23
|
+
const subDir = join(testDir, "subdir");
|
|
24
|
+
await fs.mkdir(subDir);
|
|
25
|
+
await fs.writeFile(join(testDir, testFile), "{}");
|
|
26
|
+
const result = await findUp(testFile, subDir);
|
|
27
|
+
expect(result).toBe(join(testDir, testFile));
|
|
28
|
+
});
|
|
29
|
+
test("should return null when file is not found", async () => {
|
|
30
|
+
const subDir = join(process.cwd(), "src", "tests");
|
|
31
|
+
const result = await findUp("nonexistent-file-that-should-not-exist.json", subDir);
|
|
32
|
+
expect(result).toBeNull();
|
|
33
|
+
});
|
|
34
|
+
test("should find file in nested parent directories", async () => {
|
|
35
|
+
const testFile = "test-config.json";
|
|
36
|
+
const deepDir = join(testDir, "a", "b", "c");
|
|
37
|
+
await fs.mkdir(deepDir, { recursive: true });
|
|
38
|
+
await fs.writeFile(join(testDir, testFile), "{}");
|
|
39
|
+
const result = await findUp(testFile, deepDir);
|
|
40
|
+
expect(result).toBe(join(testDir, testFile));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("readConfigFile", () => {
|
|
44
|
+
test("should read and parse valid JSON config file", async () => {
|
|
45
|
+
const configPath = join(testDir, "config.json");
|
|
46
|
+
const config = { key: "value", number: 42 };
|
|
47
|
+
await fs.writeFile(configPath, JSON.stringify(config));
|
|
48
|
+
const validate = (c) => c;
|
|
49
|
+
const result = await readConfigFile(configPath, validate);
|
|
50
|
+
expect(result).toEqual(config);
|
|
51
|
+
});
|
|
52
|
+
test("should return null for non-existent file", async () => {
|
|
53
|
+
const validate = (c) => c;
|
|
54
|
+
const result = await readConfigFile(join(testDir, "nonexistent.json"), validate);
|
|
55
|
+
expect(result).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
test("should throw error for invalid JSON", async () => {
|
|
58
|
+
const configPath = join(testDir, "invalid.json");
|
|
59
|
+
await fs.writeFile(configPath, "{ invalid json }");
|
|
60
|
+
const validate = (c) => c;
|
|
61
|
+
await expect(readConfigFile(configPath, validate)).rejects.toThrow();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("writeConfig", () => {
|
|
65
|
+
test("should write config to file", async () => {
|
|
66
|
+
const configPath = join(testDir, "config.json");
|
|
67
|
+
const config = { key: "value", nested: { prop: 123 } };
|
|
68
|
+
await writeConfig(configPath, config);
|
|
69
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
70
|
+
expect(JSON.parse(content)).toEqual(config);
|
|
71
|
+
});
|
|
72
|
+
test("should create parent directories if they don't exist", async () => {
|
|
73
|
+
const configPath = join(testDir, "nested", "deep", "config.json");
|
|
74
|
+
const config = { key: "value" };
|
|
75
|
+
await writeConfig(configPath, config);
|
|
76
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
77
|
+
expect(JSON.parse(content)).toEqual(config);
|
|
78
|
+
});
|
|
79
|
+
test("should format JSON with 2-space indentation", async () => {
|
|
80
|
+
const configPath = join(testDir, "config.json");
|
|
81
|
+
const config = { key: "value" };
|
|
82
|
+
await writeConfig(configPath, config);
|
|
83
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
84
|
+
expect(content).toBe('{\n "key": "value"\n}');
|
|
85
|
+
});
|
|
86
|
+
test("should throw error when write fails", async () => {
|
|
87
|
+
const invalidPath = "/invalid/path/that/cannot/be/created/config.json";
|
|
88
|
+
const config = { key: "value" };
|
|
89
|
+
expect(writeConfig(invalidPath, config)).rejects.toThrow("Failed to write config file");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe("loadConfig", () => {
|
|
93
|
+
test("should return default config when no config files exist", async () => {
|
|
94
|
+
const defaultConfig = { option1: "default", option2: 42 };
|
|
95
|
+
const validate = (c) => c;
|
|
96
|
+
const cwdDir = join(process.cwd(), "src", "tests");
|
|
97
|
+
const result = await loadConfig({
|
|
98
|
+
toolName: "test-tool-nonexistent",
|
|
99
|
+
configFile: "config-nonexistent.json",
|
|
100
|
+
localConfigFile: ".testrc-nonexistent",
|
|
101
|
+
defaultConfig,
|
|
102
|
+
validate,
|
|
103
|
+
}, cwdDir);
|
|
104
|
+
expect(result.config).toEqual(defaultConfig);
|
|
105
|
+
expect(result.sources.option1).toBe("default");
|
|
106
|
+
expect(result.sources.option2).toBe("default");
|
|
107
|
+
});
|
|
108
|
+
test("should merge local config over default", async () => {
|
|
109
|
+
const defaultConfig = { option1: "default", option2: 42 };
|
|
110
|
+
const localConfig = { option1: "local" };
|
|
111
|
+
const validate = (c) => c;
|
|
112
|
+
await fs.writeFile(join(testDir, ".testrc"), JSON.stringify(localConfig));
|
|
113
|
+
const result = await loadConfig({
|
|
114
|
+
toolName: "test-tool",
|
|
115
|
+
configFile: "config.json",
|
|
116
|
+
localConfigFile: ".testrc",
|
|
117
|
+
defaultConfig,
|
|
118
|
+
validate,
|
|
119
|
+
}, testDir);
|
|
120
|
+
expect(result.config.option1).toBe("local");
|
|
121
|
+
expect(result.config.option2).toBe(42);
|
|
122
|
+
expect(result.sources.option1).toBe("local");
|
|
123
|
+
expect(result.sources.option2).toBe("default");
|
|
124
|
+
});
|
|
125
|
+
test("should merge global config over default", async () => {
|
|
126
|
+
const defaultConfig = { option1: "default", option2: 42 };
|
|
127
|
+
const globalConfig = { option1: "global" };
|
|
128
|
+
const validate = (c) => c;
|
|
129
|
+
const globalConfigPath = getGlobalConfigPath("test-tool-global", "config.json");
|
|
130
|
+
try {
|
|
131
|
+
await writeGlobalConfig(globalConfig, "test-tool-global", "config.json");
|
|
132
|
+
const cwdDir = join(process.cwd(), "src", "tests");
|
|
133
|
+
const result = await loadConfig({
|
|
134
|
+
toolName: "test-tool-global",
|
|
135
|
+
configFile: "config.json",
|
|
136
|
+
localConfigFile: ".testrc-nonexistent",
|
|
137
|
+
defaultConfig,
|
|
138
|
+
validate,
|
|
139
|
+
}, cwdDir);
|
|
140
|
+
expect(result.config.option1).toBe("global");
|
|
141
|
+
expect(result.config.option2).toBe(42);
|
|
142
|
+
expect(result.sources.option1).toBe("global");
|
|
143
|
+
expect(result.sources.option2).toBe("default");
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
fs.rm(globalConfigPath, { force: true });
|
|
147
|
+
const dir = join(globalConfigPath, "..");
|
|
148
|
+
fs.rm(dir, { force: true, recursive: true });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe("getGlobalConfigPath", () => {
|
|
153
|
+
test("should return correct global config path", () => {
|
|
154
|
+
const path = getGlobalConfigPath("my-tool", "config.json");
|
|
155
|
+
expect(path).toContain(".config");
|
|
156
|
+
expect(path).toContain("my-tool");
|
|
157
|
+
expect(path).toContain("config.json");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe("findLocalConfigPath", () => {
|
|
161
|
+
test("should find local config file", async () => {
|
|
162
|
+
await fs.writeFile(join(testDir, ".testrc"), "{}");
|
|
163
|
+
const result = await findLocalConfigPath(".testrc", testDir);
|
|
164
|
+
expect(result).toBe(join(testDir, ".testrc"));
|
|
165
|
+
});
|
|
166
|
+
test("should return null when local config doesn't exist", async () => {
|
|
167
|
+
const cwdDir = join(process.cwd(), "src", "tests");
|
|
168
|
+
const result = await findLocalConfigPath(".testrc-nonexistent", cwdDir);
|
|
169
|
+
expect(result).toBeNull();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe("writeLocalConfig", () => {
|
|
173
|
+
test("should write local config", async () => {
|
|
174
|
+
const configPath = join(testDir, ".testrc");
|
|
175
|
+
const config = { key: "value" };
|
|
176
|
+
await writeLocalConfig(configPath, config);
|
|
177
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
178
|
+
expect(JSON.parse(content)).toEqual(config);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("loadLocalConfig", () => {
|
|
182
|
+
test("should load existing local config", async () => {
|
|
183
|
+
const config = { key: "value" };
|
|
184
|
+
const configPath = join(testDir, ".testrc");
|
|
185
|
+
await fs.writeFile(configPath, JSON.stringify(config));
|
|
186
|
+
const validate = (c) => c;
|
|
187
|
+
const result = await loadLocalConfig(".testrc", validate, testDir);
|
|
188
|
+
expect(result.exists).toBe(true);
|
|
189
|
+
expect(result.config).toEqual(config);
|
|
190
|
+
expect(result.path).toBe(configPath);
|
|
191
|
+
});
|
|
192
|
+
test("should return null config when file doesn't exist", async () => {
|
|
193
|
+
const validate = (c) => c;
|
|
194
|
+
const cwdDir = join(process.cwd(), "src", "tests");
|
|
195
|
+
const result = await loadLocalConfig(".testrc-nonexistent", validate, cwdDir);
|
|
196
|
+
expect(result.exists).toBe(false);
|
|
197
|
+
expect(result.config).toBeNull();
|
|
198
|
+
expect(result.path).toBe(join(cwdDir, ".testrc-nonexistent"));
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe("writeGlobalConfig", () => {
|
|
202
|
+
test("should write global config", async () => {
|
|
203
|
+
const config = { key: "value" };
|
|
204
|
+
const globalPath = getGlobalConfigPath("test-tool-temp", "config.json");
|
|
205
|
+
try {
|
|
206
|
+
await writeGlobalConfig(config, "test-tool-temp", "config.json");
|
|
207
|
+
const content = await fs.readFile(globalPath, "utf-8");
|
|
208
|
+
expect(JSON.parse(content)).toEqual(config);
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
fs.rm(globalPath, { force: true });
|
|
212
|
+
const dir = join(globalPath, "..");
|
|
213
|
+
fs.rm(dir, { force: true, recursive: true });
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe("resetGlobalConfig", () => {
|
|
218
|
+
test("should reset global config to defaults", async () => {
|
|
219
|
+
const defaultConfig = { key: "default" };
|
|
220
|
+
const globalPath = getGlobalConfigPath("test-tool-reset", "config.json");
|
|
221
|
+
try {
|
|
222
|
+
await writeGlobalConfig({ key: "modified" }, "test-tool-reset", "config.json");
|
|
223
|
+
await resetGlobalConfig(defaultConfig, "test-tool-reset", "config.json");
|
|
224
|
+
const content = await fs.readFile(globalPath, "utf-8");
|
|
225
|
+
expect(JSON.parse(content)).toEqual(defaultConfig);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
fs.rm(globalPath, { force: true });
|
|
229
|
+
const dir = join(globalPath, "..");
|
|
230
|
+
fs.rm(dir, { force: true, recursive: true });
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { formatConfigValue, formatDate } from "../lib/format.js";
|
|
3
|
+
describe("formatDate", () => {
|
|
4
|
+
test("should format valid date to ISO date string", () => {
|
|
5
|
+
const date = new Date("2024-01-15T10:30:00Z");
|
|
6
|
+
expect(formatDate(date)).toBe("2024-01-15");
|
|
7
|
+
});
|
|
8
|
+
test("should handle different dates", () => {
|
|
9
|
+
expect(formatDate(new Date("2023-12-31T00:00:00Z"))).toBe("2023-12-31");
|
|
10
|
+
expect(formatDate(new Date("2024-06-15T23:59:59Z"))).toBe("2024-06-15");
|
|
11
|
+
});
|
|
12
|
+
test("should return '--' for null", () => {
|
|
13
|
+
expect(formatDate(null)).toBe("--");
|
|
14
|
+
});
|
|
15
|
+
test("should handle dates at year boundaries", () => {
|
|
16
|
+
expect(formatDate(new Date("2024-01-01T00:00:00Z"))).toBe("2024-01-01");
|
|
17
|
+
expect(formatDate(new Date("2024-12-31T23:59:59Z"))).toBe("2024-12-31");
|
|
18
|
+
});
|
|
19
|
+
test("should handle dates with different times on same day", () => {
|
|
20
|
+
expect(formatDate(new Date("2024-03-15T00:00:00Z"))).toBe("2024-03-15");
|
|
21
|
+
expect(formatDate(new Date("2024-03-15T12:00:00Z"))).toBe("2024-03-15");
|
|
22
|
+
expect(formatDate(new Date("2024-03-15T23:59:59Z"))).toBe("2024-03-15");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe("formatConfigValue", () => {
|
|
26
|
+
describe("arrays", () => {
|
|
27
|
+
test("should join array elements with comma and space", () => {
|
|
28
|
+
expect(formatConfigValue(["item1", "item2", "item3"])).toBe("item1, item2, item3");
|
|
29
|
+
expect(formatConfigValue([1, 2, 3])).toBe("1, 2, 3");
|
|
30
|
+
});
|
|
31
|
+
test("should return '(not set)' for empty arrays", () => {
|
|
32
|
+
expect(formatConfigValue([])).toBe("(not set)");
|
|
33
|
+
});
|
|
34
|
+
test("should handle arrays with single element", () => {
|
|
35
|
+
expect(formatConfigValue(["single"])).toBe("single");
|
|
36
|
+
});
|
|
37
|
+
test("should handle arrays with mixed types", () => {
|
|
38
|
+
expect(formatConfigValue([1, "two", true])).toBe("1, two, true");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("null, undefined, and empty string", () => {
|
|
42
|
+
test("should return '(not set)' for undefined", () => {
|
|
43
|
+
expect(formatConfigValue(undefined)).toBe("(not set)");
|
|
44
|
+
});
|
|
45
|
+
test("should return '(not set)' for null", () => {
|
|
46
|
+
expect(formatConfigValue(null)).toBe("(not set)");
|
|
47
|
+
});
|
|
48
|
+
test("should return '(not set)' for empty string", () => {
|
|
49
|
+
expect(formatConfigValue("")).toBe("(not set)");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("other values", () => {
|
|
53
|
+
test("should convert strings to string", () => {
|
|
54
|
+
expect(formatConfigValue("test")).toBe("test");
|
|
55
|
+
expect(formatConfigValue("hello world")).toBe("hello world");
|
|
56
|
+
});
|
|
57
|
+
test("should convert numbers to string", () => {
|
|
58
|
+
expect(formatConfigValue(42)).toBe("42");
|
|
59
|
+
expect(formatConfigValue(0)).toBe("0");
|
|
60
|
+
expect(formatConfigValue(-10)).toBe("-10");
|
|
61
|
+
expect(formatConfigValue(3.14)).toBe("3.14");
|
|
62
|
+
});
|
|
63
|
+
test("should convert booleans to string", () => {
|
|
64
|
+
expect(formatConfigValue(true)).toBe("true");
|
|
65
|
+
expect(formatConfigValue(false)).toBe("false");
|
|
66
|
+
});
|
|
67
|
+
test("should convert objects to string", () => {
|
|
68
|
+
expect(formatConfigValue({ a: 1, b: 2 })).toBe("[object Object]");
|
|
69
|
+
});
|
|
70
|
+
test("should handle special numeric values", () => {
|
|
71
|
+
expect(formatConfigValue(Number.NaN)).toBe("NaN");
|
|
72
|
+
expect(formatConfigValue(Number.POSITIVE_INFINITY)).toBe("Infinity");
|
|
73
|
+
expect(formatConfigValue(Number.NEGATIVE_INFINITY)).toBe("-Infinity");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { isDeepEqual } from "../lib/isDeepEqual.js";
|
|
3
|
+
describe("isDeepEqual", () => {
|
|
4
|
+
describe("primitive values", () => {
|
|
5
|
+
test("should return true for identical primitive values", () => {
|
|
6
|
+
expect(isDeepEqual(1, 1)).toBe(true);
|
|
7
|
+
expect(isDeepEqual("hello", "hello")).toBe(true);
|
|
8
|
+
expect(isDeepEqual(true, true)).toBe(true);
|
|
9
|
+
expect(isDeepEqual(null, null)).toBe(true);
|
|
10
|
+
expect(isDeepEqual(undefined, undefined)).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
test("should return false for different primitive values", () => {
|
|
13
|
+
expect(isDeepEqual(1, 2)).toBe(false);
|
|
14
|
+
expect(isDeepEqual("hello", "world")).toBe(false);
|
|
15
|
+
expect(isDeepEqual(true, false)).toBe(false);
|
|
16
|
+
expect(isDeepEqual(null, undefined)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
test("should handle NaN correctly", () => {
|
|
19
|
+
expect(isDeepEqual(Number.NaN, Number.NaN)).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
test("should handle +0 and -0 correctly", () => {
|
|
22
|
+
expect(isDeepEqual(0, 0)).toBe(true);
|
|
23
|
+
expect(isDeepEqual(+0, +0)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe("arrays", () => {
|
|
27
|
+
test("should return true for identical arrays with strictOrder", () => {
|
|
28
|
+
expect(isDeepEqual([1, 2, 3], [1, 2, 3])).toBe(true);
|
|
29
|
+
expect(isDeepEqual(["a", "b", "c"], ["a", "b", "c"])).toBe(true);
|
|
30
|
+
expect(isDeepEqual([{ a: 1 }, { b: 2 }], [{ a: 1 }, { b: 2 }])).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
test("should return false for arrays with different order when strictOrder is true", () => {
|
|
33
|
+
expect(isDeepEqual([1, 2, 3], [3, 2, 1])).toBe(false);
|
|
34
|
+
expect(isDeepEqual(["a", "b", "c"], ["c", "b", "a"])).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
test("should return true for arrays with different order when strictOrder is false", () => {
|
|
37
|
+
expect(isDeepEqual([1, 2, 3], [3, 2, 1], { strictOrder: false })).toBe(true);
|
|
38
|
+
expect(isDeepEqual(["a", "b", "c"], ["c", "b", "a"], { strictOrder: false })).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
test("should return false when array elements don't match with strictOrder false", () => {
|
|
41
|
+
expect(isDeepEqual([1, 2, 3], [1, 2, 4], { strictOrder: false })).toBe(false);
|
|
42
|
+
expect(isDeepEqual([1, 2, 3], [1, 4, 5], { strictOrder: false })).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
test("should return false for arrays with different lengths", () => {
|
|
45
|
+
expect(isDeepEqual([1, 2], [1, 2, 3])).toBe(false);
|
|
46
|
+
expect(isDeepEqual([1, 2, 3], [1, 2])).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
test("should return false for arrays with different elements", () => {
|
|
49
|
+
expect(isDeepEqual([1, 2, 3], [1, 2, 4])).toBe(false);
|
|
50
|
+
expect(isDeepEqual(["a", "b"], ["a", "c"])).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
test("should handle nested arrays", () => {
|
|
53
|
+
expect(isDeepEqual([
|
|
54
|
+
[1, 2],
|
|
55
|
+
[3, 4],
|
|
56
|
+
], [
|
|
57
|
+
[1, 2],
|
|
58
|
+
[3, 4],
|
|
59
|
+
])).toBe(true);
|
|
60
|
+
expect(isDeepEqual([
|
|
61
|
+
[1, 2],
|
|
62
|
+
[3, 4],
|
|
63
|
+
], [
|
|
64
|
+
[1, 2],
|
|
65
|
+
[3, 5],
|
|
66
|
+
])).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("objects", () => {
|
|
70
|
+
test("should return true for identical objects", () => {
|
|
71
|
+
expect(isDeepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true);
|
|
72
|
+
expect(isDeepEqual({ b: 2, a: 1 }, { a: 1, b: 2 })).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
test("should return false for objects with different keys", () => {
|
|
75
|
+
expect(isDeepEqual({ a: 1 }, { b: 1 })).toBe(false);
|
|
76
|
+
expect(isDeepEqual({ a: 1, b: 2 }, { a: 1 })).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
test("should return false for objects with different values", () => {
|
|
79
|
+
expect(isDeepEqual({ a: 1, b: 2 }, { a: 1, b: 3 })).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
test("should handle nested objects", () => {
|
|
82
|
+
expect(isDeepEqual({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } })).toBe(true);
|
|
83
|
+
expect(isDeepEqual({ a: { b: { c: 1 } } }, { a: { b: { c: 2 } } })).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
test("should handle objects with arrays", () => {
|
|
86
|
+
expect(isDeepEqual({ a: [1, 2, 3], b: "test" }, { a: [1, 2, 3], b: "test" })).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("Date objects", () => {
|
|
90
|
+
test("should return true for identical dates", () => {
|
|
91
|
+
const date1 = new Date("2024-01-01");
|
|
92
|
+
const date2 = new Date("2024-01-01");
|
|
93
|
+
expect(isDeepEqual(date1, date2)).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
test("should return false for different dates", () => {
|
|
96
|
+
const date1 = new Date("2024-01-01");
|
|
97
|
+
const date2 = new Date("2024-01-02");
|
|
98
|
+
expect(isDeepEqual(date1, date2)).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe("RegExp objects", () => {
|
|
102
|
+
test("should return true for identical regex patterns", () => {
|
|
103
|
+
expect(isDeepEqual(/test/g, /test/g)).toBe(true);
|
|
104
|
+
expect(isDeepEqual(/hello/i, /hello/i)).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
test("should return false for different regex patterns", () => {
|
|
107
|
+
expect(isDeepEqual(/test/g, /test/i)).toBe(false);
|
|
108
|
+
expect(isDeepEqual(/hello/, /world/)).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("Set objects", () => {
|
|
112
|
+
test("should return true for identical sets", () => {
|
|
113
|
+
expect(isDeepEqual(new Set([1, 2, 3]), new Set([1, 2, 3]))).toBe(true);
|
|
114
|
+
expect(isDeepEqual(new Set([1, 2, 3]), new Set([3, 2, 1]))).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
test("should return false for different sets", () => {
|
|
117
|
+
expect(isDeepEqual(new Set([1, 2, 3]), new Set([1, 2, 4]))).toBe(false);
|
|
118
|
+
expect(isDeepEqual(new Set([1, 2]), new Set([1, 2, 3]))).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
test("should handle sets with objects", () => {
|
|
121
|
+
expect(isDeepEqual(new Set([{ a: 1 }, { b: 2 }]), new Set([{ a: 1 }, { b: 2 }]))).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe("Map objects", () => {
|
|
125
|
+
test("should return true for identical maps", () => {
|
|
126
|
+
expect(isDeepEqual(new Map([
|
|
127
|
+
["a", 1],
|
|
128
|
+
["b", 2],
|
|
129
|
+
]), new Map([
|
|
130
|
+
["a", 1],
|
|
131
|
+
["b", 2],
|
|
132
|
+
]))).toBe(true);
|
|
133
|
+
expect(isDeepEqual(new Map([
|
|
134
|
+
["a", 1],
|
|
135
|
+
["b", 2],
|
|
136
|
+
]), new Map([
|
|
137
|
+
["b", 2],
|
|
138
|
+
["a", 1],
|
|
139
|
+
]))).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
test("should return false for different maps", () => {
|
|
142
|
+
expect(isDeepEqual(new Map([
|
|
143
|
+
["a", 1],
|
|
144
|
+
["b", 2],
|
|
145
|
+
]), new Map([
|
|
146
|
+
["a", 1],
|
|
147
|
+
["b", 3],
|
|
148
|
+
]))).toBe(false);
|
|
149
|
+
expect(isDeepEqual(new Map([["a", 1]]), new Map([
|
|
150
|
+
["a", 1],
|
|
151
|
+
["b", 2],
|
|
152
|
+
]))).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
describe("TypedArray", () => {
|
|
156
|
+
test("should return true for identical typed arrays", () => {
|
|
157
|
+
expect(isDeepEqual(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]))).toBe(true);
|
|
158
|
+
expect(isDeepEqual(new Int16Array([10, 20, 30]), new Int16Array([10, 20, 30]))).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
test("should return false for different typed arrays", () => {
|
|
161
|
+
expect(isDeepEqual(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 4]))).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
test("should return false for typed arrays with different types", () => {
|
|
164
|
+
expect(isDeepEqual(new Uint8Array([1, 2, 3]), new Int8Array([1, 2, 3]))).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
test("should return false for typed arrays with different lengths", () => {
|
|
167
|
+
expect(isDeepEqual(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2]))).toBe(false);
|
|
168
|
+
expect(isDeepEqual(new Uint8Array([1, 2]), new Uint8Array([1, 2, 3]))).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe("circular references", () => {
|
|
172
|
+
test("should handle circular references", () => {
|
|
173
|
+
const obj1 = { a: 1 };
|
|
174
|
+
obj1.self = obj1;
|
|
175
|
+
const obj2 = { a: 1 };
|
|
176
|
+
obj2.self = obj2;
|
|
177
|
+
expect(isDeepEqual(obj1, obj2)).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe("mixed types", () => {
|
|
181
|
+
test("should return false for different types", () => {
|
|
182
|
+
expect(isDeepEqual(1, "1")).toBe(false);
|
|
183
|
+
expect(isDeepEqual([1], { 0: 1 })).toBe(false);
|
|
184
|
+
expect(isDeepEqual(null, {})).toBe(false);
|
|
185
|
+
expect(isDeepEqual(undefined, null)).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
test("should handle complex nested structures", () => {
|
|
188
|
+
const obj1 = {
|
|
189
|
+
a: [1, 2, { b: 3 }],
|
|
190
|
+
c: new Set([4, 5]),
|
|
191
|
+
d: new Map([["e", 6]]),
|
|
192
|
+
f: new Date("2024-01-01"),
|
|
193
|
+
};
|
|
194
|
+
const obj2 = {
|
|
195
|
+
a: [1, 2, { b: 3 }],
|
|
196
|
+
c: new Set([4, 5]),
|
|
197
|
+
d: new Map([["e", 6]]),
|
|
198
|
+
f: new Date("2024-01-01"),
|
|
199
|
+
};
|
|
200
|
+
expect(isDeepEqual(obj1, obj2)).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { isValidBranchName } from "../lib/isValidBranchName.js";
|
|
3
|
+
describe("isValidBranchName", () => {
|
|
4
|
+
describe("valid branch names", () => {
|
|
5
|
+
test("should return true for simple branch names", () => {
|
|
6
|
+
expect(isValidBranchName("main")).toBe(true);
|
|
7
|
+
expect(isValidBranchName("develop")).toBe(true);
|
|
8
|
+
expect(isValidBranchName("feature")).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
test("should return true for branch names with hyphens", () => {
|
|
11
|
+
expect(isValidBranchName("feature-branch")).toBe(true);
|
|
12
|
+
expect(isValidBranchName("bug-fix")).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
test("should return true for branch names with underscores", () => {
|
|
15
|
+
expect(isValidBranchName("feature_branch")).toBe(true);
|
|
16
|
+
expect(isValidBranchName("bug_fix")).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
test("should return true for branch names with dots", () => {
|
|
19
|
+
expect(isValidBranchName("release.1.0")).toBe(true);
|
|
20
|
+
expect(isValidBranchName("v1.2.3")).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
test("should return true for branch names with slashes", () => {
|
|
23
|
+
expect(isValidBranchName("feature/new-feature")).toBe(true);
|
|
24
|
+
expect(isValidBranchName("bugfix/issue-123")).toBe(true);
|
|
25
|
+
expect(isValidBranchName("release/v1.0.0")).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
test("should return true for branch names with numbers", () => {
|
|
28
|
+
expect(isValidBranchName("feature123")).toBe(true);
|
|
29
|
+
expect(isValidBranchName("v1.2.3")).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
test("should return true for mixed valid characters", () => {
|
|
32
|
+
expect(isValidBranchName("feature/ABC-123_test.v1")).toBe(true);
|
|
33
|
+
expect(isValidBranchName("release/2024.01.01")).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("invalid branch names", () => {
|
|
37
|
+
test("should return false for empty strings", () => {
|
|
38
|
+
expect(isValidBranchName("")).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
test("should return false for branch names with invalid characters", () => {
|
|
41
|
+
expect(isValidBranchName("feature@branch")).toBe(false);
|
|
42
|
+
expect(isValidBranchName("bug#fix")).toBe(false);
|
|
43
|
+
expect(isValidBranchName("test branch")).toBe(false);
|
|
44
|
+
expect(isValidBranchName("feature:branch")).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
test("should return false for branch names with double dots", () => {
|
|
47
|
+
expect(isValidBranchName("feature..branch")).toBe(false);
|
|
48
|
+
expect(isValidBranchName("test..test")).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
test("should return false for branch names starting with slash", () => {
|
|
51
|
+
expect(isValidBranchName("/feature")).toBe(false);
|
|
52
|
+
expect(isValidBranchName("/main")).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
test("should return false for branch names ending with slash", () => {
|
|
55
|
+
expect(isValidBranchName("feature/")).toBe(false);
|
|
56
|
+
expect(isValidBranchName("main/")).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
test("should return false for branch names with double slashes", () => {
|
|
59
|
+
expect(isValidBranchName("feature//branch")).toBe(false);
|
|
60
|
+
expect(isValidBranchName("test//test")).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
test("should return false for branch names ending with dot", () => {
|
|
63
|
+
expect(isValidBranchName("feature.")).toBe(false);
|
|
64
|
+
expect(isValidBranchName("branch.")).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
test("should return false for components starting with dot", () => {
|
|
67
|
+
expect(isValidBranchName("feature/.hidden")).toBe(false);
|
|
68
|
+
expect(isValidBranchName(".hidden/branch")).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
test("should return false for components ending with .lock", () => {
|
|
71
|
+
expect(isValidBranchName("feature/branch.lock")).toBe(false);
|
|
72
|
+
expect(isValidBranchName("test.lock")).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe("edge cases", () => {
|
|
76
|
+
test("should handle single character branch names", () => {
|
|
77
|
+
expect(isValidBranchName("a")).toBe(true);
|
|
78
|
+
expect(isValidBranchName("1")).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
test("should handle very long branch names", () => {
|
|
81
|
+
const longName = "a".repeat(255);
|
|
82
|
+
expect(isValidBranchName(longName)).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
test("should handle branch names with multiple slashes", () => {
|
|
85
|
+
expect(isValidBranchName("feature/sub/branch")).toBe(true);
|
|
86
|
+
expect(isValidBranchName("a/b/c/d/e")).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|