Repository navigation
Expand file tree
/
Copy pathuploader.test.ts
More file actions
138 lines (120 loc) · 5.18 KB
/
Copy pathuploader.test.ts
File metadata and controls
138 lines (120 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { describe, it, expect, beforeEach } from "vite-plus/test";
import { shouldSkip, safeSlice, isBinaryFile, buildChunks } from "../src/uploader";
import { settings, saveSettings } from "../src/settings";
import { FileObj } from "../src/types";
describe("uploader utils", () => {
beforeEach(() => {
// Reset settings to default before each test
settings.skipHidden = true;
settings.maxFileBytes = 10 * 1024 * 1024;
settings.maxChunkChars = 100000;
settings.ignoreFolders = "node_modules, .git, .idea, dist, build, .vscode";
settings.ignoreExts = ".png, .jpg, .jpeg, .gif, .mp4, .zip, .tar, .gz, .pdf";
settings.customPrompt = "";
saveSettings();
});
describe("shouldSkip", () => {
it("returns false for normal source files and recognized text dotfiles", () => {
expect(shouldSkip("src/index.ts", 1024)).toBe(false);
expect(shouldSkip("components/Button.tsx", 2048)).toBe(false);
expect(shouldSkip(".env", 100)).toBe(false); // recognized text file extension
});
it("returns true for files in ignored folders", () => {
expect(shouldSkip("node_modules/express/index.js", 500)).toBe(true);
expect(shouldSkip(".git/HEAD", 50)).toBe(true);
expect(shouldSkip("dist/bundle.js", 10000)).toBe(true);
});
it("returns true for hidden files when skipHidden is active", () => {
expect(shouldSkip(".unknown_secret", 100)).toBe(true);
expect(shouldSkip(".hidden_dir/config.json", 100)).toBe(true);
});
it("returns true for ignored extensions", () => {
expect(shouldSkip("assets/logo.png", 5000)).toBe(true);
expect(shouldSkip("docs/manual.pdf", 50000)).toBe(true);
expect(shouldSkip("archive.zip", 100000)).toBe(true);
});
it("returns true for files exceeding maxFileBytes", () => {
expect(shouldSkip("src/large.ts", 15 * 1024 * 1024)).toBe(true);
});
});
describe("safeSlice", () => {
it("slices standard strings correctly given start and length", () => {
expect(safeSlice("hello world", 0, 5)).toBe("hello");
expect(safeSlice("hello world", 6, 5)).toBe("world");
});
it("handles out-of-bound start or invalid length gracefully", () => {
expect(safeSlice("hello", 10, 5)).toBe("");
expect(safeSlice("hello", 0, 0)).toBe("");
expect(safeSlice("hello", 2, -1)).toBe("");
});
it("handles surrogate pairs (emojis) without splitting them", () => {
const emojiStr = "a🚀b";
// '🚀' consists of high surrogate \uD83D (index 1) and low surrogate \uDE80 (index 2)
// Slicing with length 2 ends between surrogates; safeSlice backs off to avoid invalid character
const sliced = safeSlice(emojiStr, 0, 2);
expect(sliced).toBe("a");
});
});
describe("isBinaryFile", () => {
it("identifies binary files by extension", () => {
expect(isBinaryFile("image.png")).toBe(true);
expect(isBinaryFile("photo.JPG")).toBe(true);
expect(isBinaryFile("archive.zip")).toBe(true);
expect(isBinaryFile("document.pdf")).toBe(true);
});
it("returns false for text files", () => {
expect(isBinaryFile("script.ts")).toBe(false);
expect(isBinaryFile("styles.css")).toBe(false);
expect(isBinaryFile("index.html")).toBe(false);
expect(isBinaryFile("README")).toBe(false);
});
});
describe("buildChunks", () => {
it("generates manifest and content chunk files", async () => {
const textFiles: FileObj[] = [
{
file: new File(['console.log("hello world");'], "main.ts", { type: "text/typescript" }),
path: "src/main.ts",
selected: true,
isBinary: false,
},
];
const binaryFiles: FileObj[] = [
{
file: new File(["fake binary content"], "icon.png", { type: "image/png" }),
path: "public/icon.png",
selected: true,
isBinary: true,
},
];
const result = await buildChunks(textFiles, binaryFiles);
expect(result.length).toBe(2);
const manifestFile = result[0];
expect(manifestFile.name).toBe("codebase_manifest.md");
const manifestText = await manifestFile.text();
expect(manifestText).toContain("# Codebase Manifest");
expect(manifestText).toContain("- `src/main.ts`");
expect(manifestText).toContain("- `public/icon.png` (binary)");
const part1File = result[1];
expect(part1File.name).toBe("codebase_part_1.md");
const part1Text = await part1File.text();
expect(part1Text).toContain("# Codebase Context — Part 1");
expect(part1Text).toContain("## File: `src/main.ts`");
expect(part1Text).toContain('console.log("hello world");');
});
it("splits large files into multiple chunk parts when exceeding maxChunkChars", async () => {
settings.maxChunkChars = 1000;
const largeContent = "a".repeat(2000);
const textFiles: FileObj[] = [
{
file: new File([largeContent], "big.txt", { type: "text/plain" }),
path: "big.txt",
selected: true,
isBinary: false,
},
];
const result = await buildChunks(textFiles, []);
expect(result.length).toBeGreaterThan(2); // manifest + at least 2 part files
});
});
});