Skip to content

Commit b3691a0

Browse files
Added additional tests
1 parent 6fb62d0 commit b3691a0

8 files changed

Lines changed: 376 additions & 4 deletions

src/test/cli.test.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
Options,
1414
ParsedArgs,
1515
} from "../types.js";
16-
import { EXIT_FAIL, EXIT_OK } from "../util/constants.js";
16+
import { EXIT_ERROR, EXIT_FAIL, EXIT_OK } from "../util/constants.js";
1717
import { createLogger, createTestLogger } from "../util/createLogger.js";
1818
import { getDefaultArguments } from "../util/getDefaultArguments.js";
1919
import { normalizeToPosix } from "../util/normalizeToPosix.js";
@@ -377,6 +377,41 @@ suite("CLI", () => {
377377
}
378378
});
379379

380+
test("Check mode reports success when all files are unchanged", async () => {
381+
const fileName = await createTempFile(
382+
"talonfmt-",
383+
"example.txt",
384+
"content",
385+
);
386+
const cli = createCLI((text) => text);
387+
const originalArgv = process.argv;
388+
const originalExitCode = process.exitCode;
389+
390+
try {
391+
process.argv = ["node", "talon-fmt", "--check", fileName];
392+
393+
const output = await captureStdoutAndStderr(async () => {
394+
await main(cli);
395+
return process.exitCode;
396+
});
397+
398+
assert.equal(output.result, EXIT_OK);
399+
assert.equal(
400+
output.stdoutText,
401+
[
402+
"Checking formatting...",
403+
"All matched files use correct code style!",
404+
"",
405+
].join("\n"),
406+
);
407+
assert.equal(output.stderrText, "");
408+
} finally {
409+
process.argv = originalArgv;
410+
process.exitCode = originalExitCode;
411+
await cleanupTempFile(fileName);
412+
}
413+
});
414+
380415
test("Returns success for unchanged stdin in check mode", async () => {
381416
const cli = createCLI((text) => text);
382417
const logger = createTestLogger();
@@ -558,6 +593,58 @@ suite("CLI", () => {
558593
);
559594
});
560595

596+
test("Main prints version and exits successfully", async () => {
597+
const cli = createCLI((text) => text);
598+
const originalArgv = process.argv;
599+
const originalExitCode = process.exitCode;
600+
601+
try {
602+
process.argv = ["node", "talon-fmt", "--version"];
603+
604+
const output = await captureStdoutAndStderr(async () => {
605+
await main(cli);
606+
return process.exitCode;
607+
});
608+
609+
assert.equal(output.result, EXIT_OK);
610+
assert.match(output.stdoutText, /^\d+\.\d+\.\d+\n$/);
611+
assert.equal(output.stderrText, "");
612+
} finally {
613+
process.argv = originalArgv;
614+
process.exitCode = originalExitCode;
615+
}
616+
});
617+
618+
test("Main reports file pattern errors", async () => {
619+
const cli = createCLI((text) => text);
620+
const originalArgv = process.argv;
621+
const originalExitCode = process.exitCode;
622+
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "talonfmt-"));
623+
const cwd = process.cwd();
624+
625+
try {
626+
process.chdir(directory);
627+
process.argv = ["node", "talon-fmt", "**/*.txt"];
628+
629+
const output = await captureStdoutAndStderr(async () => {
630+
await main(cli);
631+
return process.exitCode;
632+
});
633+
634+
assert.equal(output.result, EXIT_ERROR);
635+
assert.equal(output.stdoutText, "");
636+
assert.equal(
637+
output.stderrText,
638+
"[error] No files matching the pattern were found: **/*.txt\n",
639+
);
640+
} finally {
641+
process.chdir(cwd);
642+
process.argv = originalArgv;
643+
process.exitCode = originalExitCode;
644+
await fs.rm(directory, { recursive: true, force: true });
645+
}
646+
});
647+
561648
test("Captures log entries without writing when quiet", async () => {
562649
const fileName = await createTempFile(
563650
"talonfmt-",

src/test/getOptionsFromConfig.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,24 @@ import type { EditorConfigOptions } from "../types.js";
88
import { getOptionsFromConfig } from "../util/getOptionsFromConfig.js";
99

1010
suite("getOptionsFromConfig", () => {
11+
test("indent_style: space sets indentTabs: false", async () => {
12+
const fileName = await createTempFile("talonfmt-", "example.talon");
13+
14+
try {
15+
await writeEditorConfig(fileName, {
16+
indent_style: "space",
17+
});
18+
19+
const actual = await getOptionsFromConfig(fileName);
20+
21+
assert.deepEqual(actual, {
22+
indentTabs: false,
23+
});
24+
} finally {
25+
await cleanupTempFile(fileName);
26+
}
27+
});
28+
1129
test("indent_style: tab sets indentTabs: true", async () => {
1230
const fileName = await createTempFile("talonfmt-", "example.talon");
1331

@@ -47,6 +65,24 @@ suite("getOptionsFromConfig", () => {
4765
}
4866
});
4967

68+
test("indent_size: 2 sets indentSize: 2", async () => {
69+
const fileName = await createTempFile("talonfmt-", "example.talon");
70+
71+
try {
72+
await writeEditorConfig(fileName, {
73+
indent_size: 2,
74+
});
75+
76+
const actual = await getOptionsFromConfig(fileName);
77+
78+
assert.deepEqual(actual, {
79+
indentSize: 2,
80+
});
81+
} finally {
82+
await cleanupTempFile(fileName);
83+
}
84+
});
85+
5086
test("insert_final_newline: false sets insertFinalNewline: false", async () => {
5187
const fileName = await createTempFile("talonfmt-", "example.talon");
5288

@@ -83,6 +119,42 @@ suite("getOptionsFromConfig", () => {
83119
}
84120
});
85121

122+
test("max_line_length: 80 sets maxLineLength: 80", async () => {
123+
const fileName = await createTempFile("talonfmt-", "example.talon");
124+
125+
try {
126+
await writeEditorConfig(fileName, {
127+
max_line_length: 80,
128+
});
129+
130+
const actual = await getOptionsFromConfig(fileName);
131+
132+
assert.deepEqual(actual, {
133+
maxLineLength: 80,
134+
});
135+
} finally {
136+
await cleanupTempFile(fileName);
137+
}
138+
});
139+
140+
test("column_width: 24 sets columnWidth: 24", async () => {
141+
const fileName = await createTempFile("talonfmt-", "example.talon");
142+
143+
try {
144+
await writeEditorConfig(fileName, {
145+
column_width: 24,
146+
});
147+
148+
const actual = await getOptionsFromConfig(fileName);
149+
150+
assert.deepEqual(actual, {
151+
columnWidth: 24,
152+
});
153+
} finally {
154+
await cleanupTempFile(fileName);
155+
}
156+
});
157+
86158
test("end_of_line: crlf sets endOfLine: crlf", async () => {
87159
const fileName = await createTempFile("talonfmt-", "example.talon");
88160

src/test/parseFilePatterns.test.ts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,34 @@ suite("Parse file patterns", () => {
5151
}
5252
});
5353

54+
test("Expands directories with multiple supported endings", async () => {
55+
const directory = await createTempDirectory();
56+
const cwd = process.cwd();
57+
58+
try {
59+
await fs.writeFile(path.join(directory, "one.txt"), "one", "utf8");
60+
await fs.writeFile(
61+
path.join(directory, "two.talon"),
62+
"two",
63+
"utf8",
64+
);
65+
await fs.writeFile(path.join(directory, "skip.md"), "skip", "utf8");
66+
process.chdir(directory);
67+
68+
const files = await parseFilePatterns(createCLI(["txt", "talon"]), [
69+
".",
70+
]);
71+
72+
assert.deepEqual(files, [
73+
path.join(directory, "one.txt"),
74+
path.join(directory, "two.talon"),
75+
]);
76+
} finally {
77+
process.chdir(cwd);
78+
await cleanupDirectory(directory);
79+
}
80+
});
81+
5482
test("Ignores hardcoded directories during directory expansion", async () => {
5583
const directory = await createTempDirectory();
5684
const cwd = process.cwd();
@@ -105,6 +133,24 @@ suite("Parse file patterns", () => {
105133
}
106134
});
107135

136+
test("Glob patterns filter by supported endings", async () => {
137+
const directory = await createTempDirectory();
138+
const cwd = process.cwd();
139+
140+
try {
141+
await fs.writeFile(path.join(directory, "one.txt"), "one", "utf8");
142+
await fs.writeFile(path.join(directory, "two.md"), "two", "utf8");
143+
process.chdir(directory);
144+
145+
const files = await parseFilePatterns(createCLI(), ["**/*.*"]);
146+
147+
assert.deepEqual(files, [path.join(directory, "one.txt")]);
148+
} finally {
149+
process.chdir(cwd);
150+
await cleanupDirectory(directory);
151+
}
152+
});
153+
108154
test("Expands Windows-style glob patterns on Windows", async function () {
109155
if (path.sep !== "\\") {
110156
this.skip();
@@ -241,12 +287,35 @@ suite("Parse file patterns", () => {
241287
await cleanupDirectory(directory);
242288
}
243289
});
290+
291+
test("Aggregates multiple pattern errors", async () => {
292+
const directory = await createTempDirectory();
293+
const cwd = process.cwd();
294+
295+
try {
296+
process.chdir(directory);
297+
298+
await assert.rejects(
299+
parseFilePatterns(createCLI(), [".", "**/*.txt"]),
300+
(error: unknown) =>
301+
error instanceof FilePatternError &&
302+
error.messages.length === 2 &&
303+
error.messages[0] ===
304+
"No matching files were found in the directory: ." &&
305+
error.messages[1] ===
306+
"No files matching the pattern were found: **/*.txt",
307+
);
308+
} finally {
309+
process.chdir(cwd);
310+
await cleanupDirectory(directory);
311+
}
312+
});
244313
});
245314

246-
function createCLI(): CLI {
315+
function createCLI(fileEndings: readonly string[] = ["txt"]): CLI {
247316
return {
248317
binName: "talon-fmt",
249-
fileEndings: ["txt"],
318+
fileEndings,
250319
getStdinFileEnding: () => "txt",
251320
format: (text) => Promise.resolve(text),
252321
};

src/test/parseTalonList.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import assert from "node:assert";
2+
import { parseTalonList, type TalonList } from "../talon/parseTalonList.js";
3+
4+
suite("parseTalonList", () => {
5+
test("Parses headers, comments, and items", () => {
6+
const fixture = `\
7+
# header
8+
list: user.my_list
9+
tag: user.test
10+
-
11+
12+
# body
13+
air: a
14+
bat
15+
`;
16+
17+
const expected: TalonList = {
18+
headers: [
19+
{ type: "comment", text: "# header" },
20+
{ type: "header", key: "list", value: "user.my_list" },
21+
{ type: "header", key: "tag", value: "user.test" },
22+
],
23+
items: [
24+
{ type: "comment", text: "# body" },
25+
{ type: "item", key: "air", value: "a" },
26+
{ type: "item", key: "bat", value: undefined },
27+
],
28+
};
29+
30+
const actual = parseTalonList(fixture);
31+
32+
assert.deepEqual(actual, expected);
33+
});
34+
35+
test("trims leading and trailing blank body lines but preserves internal blanks", () => {
36+
const fixture = `\
37+
list : user.my_list
38+
-
39+
40+
41+
air : a
42+
43+
bat: b
44+
45+
46+
`;
47+
48+
const expected: TalonList = {
49+
headers: [{ type: "header", key: "list", value: "user.my_list" }],
50+
items: [
51+
{ type: "item", key: "air", value: "a" },
52+
{ type: "empty" },
53+
{ type: "item", key: "bat", value: "b" },
54+
],
55+
};
56+
57+
const actual = parseTalonList(fixture);
58+
59+
assert.deepEqual(actual, expected);
60+
});
61+
62+
test("Supports CRLF input", () => {
63+
const actual = parseTalonList("list: user.my_list\r\n-\r\na: b");
64+
65+
assert.deepEqual(actual, {
66+
headers: [{ type: "header", key: "list", value: "user.my_list" }],
67+
items: [{ type: "item", key: "a", value: "b" }],
68+
});
69+
});
70+
71+
test("Throws when separator is missing", () => {
72+
assert.throws(
73+
() => parseTalonList("list: user.my_list"),
74+
/Separator not found in talon list/,
75+
);
76+
});
77+
78+
test("Throws when a header value is missing", () => {
79+
assert.throws(
80+
() => parseTalonList("list\n-\na: b"),
81+
/Header value missing/,
82+
);
83+
});
84+
});

0 commit comments

Comments
 (0)