import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { beforeEach, describe, expect, it } from "vitest";
import { FileScanner } from "../../src/code/scanner.js";
import type { ScannerConfig } from "../../src/code/types.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const fixturesDir = join(__dirname, "fixtures");
describe("FileScanner", () => {
let scanner: FileScanner;
let config: ScannerConfig;
beforeEach(() => {
config = {
supportedExtensions: [".ts", ".js", ".py"],
ignorePatterns: ["node_modules/**", "dist/**"],
};
scanner = new FileScanner(config);
});
describe("scanDirectory", () => {
it("should find all supported files", async () => {
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
expect(files.length).toBeGreaterThan(0);
expect(files.some((f) => f.endsWith("auth.ts"))).toBe(true);
// Verify new fixture files are found
expect(files.some((f) => f.endsWith("database.ts"))).toBe(true);
expect(files.some((f) => f.endsWith("utils.ts"))).toBe(true);
expect(files.some((f) => f.endsWith("validator.ts"))).toBe(true);
expect(files.some((f) => f.endsWith("config.ts"))).toBe(true);
expect(files.some((f) => f.endsWith("index.ts"))).toBe(true);
expect(files.some((f) => f.endsWith("async-operations.ts"))).toBe(true);
expect(files.some((f) => f.endsWith("types-advanced.ts"))).toBe(true);
// Should have at least 8 TypeScript files
expect(files.length).toBeGreaterThanOrEqual(8);
});
it("should respect supported extensions", async () => {
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
files.forEach((file) => {
const hasValidExt = config.supportedExtensions.some((ext) => file.endsWith(ext));
expect(hasValidExt).toBe(true);
});
});
it("should handle empty directories", async () => {
const config: ScannerConfig = {
supportedExtensions: [".nonexistent"],
ignorePatterns: [],
};
const scanner = new FileScanner(config);
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
expect(files).toEqual([]);
});
});
describe("loadIgnorePatterns", () => {
it("should load .gitignore patterns", async () => {
await scanner.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
// .gitignore should be loaded, but we can't directly test internal state
// Instead, we test the effect through scanDirectory
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
expect(files.some((f) => f.includes("node_modules"))).toBe(false);
});
it("should handle missing ignore files gracefully", async () => {
await expect(scanner.loadIgnorePatterns("/nonexistent/path")).resolves.not.toThrow();
});
});
describe("getSupportedExtensions", () => {
it("should return configured extensions", () => {
const extensions = scanner.getSupportedExtensions();
expect(extensions).toEqual([".ts", ".js", ".py"]);
});
});
describe("edge cases", () => {
it("should handle paths with special characters", async () => {
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
expect(Array.isArray(files)).toBe(true);
});
it("should skip symbolic links", async () => {
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
expect(Array.isArray(files)).toBe(true);
});
it("should handle custom ignore patterns", async () => {
const customConfig: ScannerConfig = {
supportedExtensions: [".ts", ".js"],
ignorePatterns: [],
customIgnorePatterns: ["**/*.test.ts"],
};
const customScanner = new FileScanner(customConfig);
await customScanner.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
const files = await customScanner.scanDirectory(join(fixturesDir, "sample-ts"));
expect(files.some((f) => f.includes(".test.ts"))).toBe(false);
});
it("should properly ignore files matching ignore patterns", async () => {
const ignoreConfig: ScannerConfig = {
supportedExtensions: [".ts", ".js"],
ignorePatterns: ["**/auth.ts"],
};
const ignoreScanner = new FileScanner(ignoreConfig);
await ignoreScanner.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
const files = await ignoreScanner.scanDirectory(join(fixturesDir, "sample-ts"));
// Should not include auth.ts due to ignore pattern
expect(files.some((f) => f.endsWith("auth.ts"))).toBe(false);
});
it("should handle directories with .gitignore", async () => {
const scannerWithGitignore = new FileScanner(config);
await scannerWithGitignore.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
const files = await scannerWithGitignore.scanDirectory(join(fixturesDir, "sample-ts"));
// Files matching .gitignore patterns should be excluded
expect(Array.isArray(files)).toBe(true);
});
it("should gracefully handle non-existent directories", async () => {
const files = await scanner.scanDirectory("/nonexistent/directory/path");
expect(files).toEqual([]);
});
});
});