2026-01-27 17:19:13 -08:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
|
|
import { IgnoreFileParser } from './ignoreFileParser.js';
|
|
|
|
|
import * as fs from 'node:fs/promises';
|
|
|
|
|
import * as path from 'node:path';
|
|
|
|
|
import * as os from 'node:os';
|
|
|
|
|
import { GEMINI_IGNORE_FILE_NAME } from '../config/constants.js';
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
describe('IgnoreFileParser', () => {
|
2026-01-27 17:19:13 -08:00
|
|
|
let projectRoot: string;
|
|
|
|
|
|
|
|
|
|
async function createTestFile(filePath: string, content = '') {
|
|
|
|
|
const fullPath = path.join(projectRoot, filePath);
|
|
|
|
|
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
|
|
|
await fs.writeFile(fullPath, content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
2026-03-27 13:12:26 -04:00
|
|
|
projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'ignore-file-test-'));
|
2026-01-27 17:19:13 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
|
|
|
vi.restoreAllMocks();
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
describe('Basic File Loading', () => {
|
|
|
|
|
it('should identify paths ignored by a single ignore file', async () => {
|
2026-01-27 17:19:13 -08:00
|
|
|
await createTestFile(
|
|
|
|
|
GEMINI_IGNORE_FILE_NAME,
|
2026-03-27 13:12:26 -04:00
|
|
|
'ignored.txt\n/ignored_dir/',
|
2026-01-27 17:19:13 -08:00
|
|
|
);
|
|
|
|
|
const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
expect(parser.isIgnored('ignored.txt', false)).toBe(true);
|
|
|
|
|
expect(parser.isIgnored('ignored_dir/file.txt', false)).toBe(true);
|
|
|
|
|
expect(parser.isIgnored('keep.txt', false)).toBe(false);
|
|
|
|
|
expect(parser.isIgnored('ignored_dir', true)).toBe(true);
|
2026-01-27 17:19:13 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
it('should handle missing or empty ignore files gracefully', () => {
|
|
|
|
|
const parser = new IgnoreFileParser(projectRoot, 'nonexistent.ignore');
|
|
|
|
|
expect(parser.isIgnored('any.txt', false)).toBe(false);
|
2026-01-27 17:19:13 -08:00
|
|
|
expect(parser.hasPatterns()).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
describe('Multiple Ignore File Priority', () => {
|
|
|
|
|
const primary = 'primary.ignore';
|
|
|
|
|
const secondary = 'secondary.ignore';
|
2026-01-27 17:19:13 -08:00
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
it('should prioritize patterns from the first file in the input list', async () => {
|
|
|
|
|
// First file un-ignores, second file ignores
|
|
|
|
|
await createTestFile(primary, '!important.log');
|
|
|
|
|
await createTestFile(secondary, '*.log');
|
2026-01-27 17:19:13 -08:00
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
const parser = new IgnoreFileParser(projectRoot, [primary, secondary]);
|
2026-01-27 17:19:13 -08:00
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
expect(parser.isIgnored('other.log', false)).toBe(true);
|
|
|
|
|
expect(parser.isIgnored('important.log', false)).toBe(false);
|
2026-01-27 17:19:13 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
it('should return existing ignore file paths in priority order', async () => {
|
|
|
|
|
await createTestFile(primary, 'pattern');
|
|
|
|
|
await createTestFile(secondary, 'pattern');
|
2026-01-27 17:19:13 -08:00
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
const parser = new IgnoreFileParser(projectRoot, [primary, secondary]);
|
|
|
|
|
const paths = parser.getIgnoreFilePaths();
|
|
|
|
|
// Implementation returns in reverse order of processing (first file = highest priority = last processed)
|
|
|
|
|
expect(paths[0]).toBe(path.join(projectRoot, secondary));
|
|
|
|
|
expect(paths[1]).toBe(path.join(projectRoot, primary));
|
2026-01-27 17:19:13 -08:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
describe('Direct Pattern Input (isPatterns = true)', () => {
|
|
|
|
|
it('should use raw patterns passed directly in the constructor', () => {
|
2026-01-27 17:19:13 -08:00
|
|
|
const parser = new IgnoreFileParser(
|
|
|
|
|
projectRoot,
|
2026-03-27 13:12:26 -04:00
|
|
|
['*.tmp', '!safe.tmp'],
|
2026-01-27 17:19:13 -08:00
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
expect(parser.isIgnored('temp.tmp', false)).toBe(true);
|
|
|
|
|
expect(parser.isIgnored('safe.tmp', false)).toBe(false);
|
2026-01-27 17:19:13 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-27 13:12:26 -04:00
|
|
|
it('should return provided patterns via getPatterns()', () => {
|
|
|
|
|
const patterns = ['*.a', '*.b'];
|
2026-01-27 17:19:13 -08:00
|
|
|
const parser = new IgnoreFileParser(projectRoot, patterns, true);
|
|
|
|
|
expect(parser.getPatterns()).toEqual(patterns);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|