|
|
@@ -22,6 +22,21 @@ const INDEX = [
|
|
|
'src/lib/task-runner-manager.ts',
|
|
|
'src/lib/stores/sqlite-store.ts',
|
|
|
'src/lib/stores/postgresql-store.ts',
|
|
|
+ // Kebab-case frontend shapes (the amnisphere extension-less-basename bug):
|
|
|
+ 'src/components/training-set-page/training-set-page.tsx',
|
|
|
+ 'src/components/training-set-page/training-set-page-background-images.tsx',
|
|
|
+ 'src/components/training-set-page/training-set-page.module.scss',
|
|
|
+ 'src/components/training-set-page/background-image-table.tsx',
|
|
|
+ 'src/components/modal/add-to-training-set/add-to-training-set.tsx',
|
|
|
+ 'src/pages/library-page-layout.tsx',
|
|
|
+ 'src/api/job-manager/backgrounds.ts',
|
|
|
+ 'src/x/generic-modal.tsx',
|
|
|
+ 'src/y/generic-modal.tsx',
|
|
|
+ 'scripts/pre-commit',
|
|
|
+ 'src/a/user-profile.tsx',
|
|
|
+ 'src/b/user-profile.tsx',
|
|
|
+ 'src/c/user-profile.tsx',
|
|
|
+ 'src/d/user-profile.tsx',
|
|
|
];
|
|
|
|
|
|
describe('queryMightContainPaths — the cheap pre-gate', () => {
|
|
|
@@ -35,6 +50,18 @@ describe('queryMightContainPaths — the cheap pre-gate', () => {
|
|
|
// `.isPackaged` is 10 chars — past the 8-char extension cap.
|
|
|
expect(queryMightContainPaths('what reads app.isPackaged here')).toBe(false);
|
|
|
});
|
|
|
+
|
|
|
+ it('fires on extension-less kebab basenames — with or without wrapping', () => {
|
|
|
+ expect(queryMightContainPaths('background-image-table Source column')).toBe(true);
|
|
|
+ expect(queryMightContainPaths('the `library-page-layout` wrapper')).toBe(true);
|
|
|
+ expect(queryMightContainPaths('usage, add-to-training-set.')).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('stays quiet on flags, snake_case, and snake-with-a-dash hybrids', () => {
|
|
|
+ expect(queryMightContainPaths('run it with --no-cache maybe')).toBe(false);
|
|
|
+ expect(queryMightContainPaths('where is background_image_table used')).toBe(false);
|
|
|
+ expect(queryMightContainPaths('the foo_bar-baz helper')).toBe(false);
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
describe('extractQueryPaths — resolution and stripping', () => {
|
|
|
@@ -127,3 +154,85 @@ describe('extractQueryPaths — resolution and stripping', () => {
|
|
|
expect(out).toEqual({ strippedQuery: q, pinnedFiles: [], unresolvedPathSpans: [] });
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+describe('extractQueryPaths — extension-less kebab basenames', () => {
|
|
|
+ it('pins the file a bare kebab basename names and consumes the token', () => {
|
|
|
+ const out = extractQueryPaths('background-image-table Source column', INDEX);
|
|
|
+ expect(out.pinnedFiles)
|
|
|
+ .toEqual(['src/components/training-set-page/background-image-table.tsx']);
|
|
|
+ expect(out.strippedQuery).toBe('Source column');
|
|
|
+ expect(out.unresolvedPathSpans).toEqual([]);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('resolves with no slash or extension anywhere in the query (session-4 shape)', () => {
|
|
|
+ const out = extractQueryPaths(
|
|
|
+ 'TrainingSetPage train modal library-page-layout AddToTrainingSetModal usage', INDEX,
|
|
|
+ );
|
|
|
+ expect(out.pinnedFiles).toEqual(['src/pages/library-page-layout.tsx']);
|
|
|
+ // Identifier-shaped tokens stay for the named-symbol seeder.
|
|
|
+ expect(out.strippedQuery).toBe('TrainingSetPage train modal AddToTrainingSetModal usage');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('pins every named file in a mixed dotted + kebab query (session-1 shape)', () => {
|
|
|
+ const out = extractQueryPaths(
|
|
|
+ 'add-to-training-set training-set-page-background-images backgrounds.ts background-image-table Source column',
|
|
|
+ INDEX,
|
|
|
+ );
|
|
|
+ expect(out.pinnedFiles).toEqual([
|
|
|
+ // The dotted pass runs first, so the explicit basename pins ahead of the kebabs.
|
|
|
+ 'src/api/job-manager/backgrounds.ts',
|
|
|
+ 'src/components/modal/add-to-training-set/add-to-training-set.tsx',
|
|
|
+ 'src/components/training-set-page/training-set-page-background-images.tsx',
|
|
|
+ 'src/components/training-set-page/background-image-table.tsx',
|
|
|
+ ]);
|
|
|
+ expect(out.strippedQuery).toBe('Source column');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('leaves kebab prose that names no indexed file untouched — and unreported', () => {
|
|
|
+ const q = 'how does cross-call dedup make explore non-blocking';
|
|
|
+ const out = extractQueryPaths(q, INDEX);
|
|
|
+ expect(out).toEqual({ strippedQuery: q, pinnedFiles: [], unresolvedPathSpans: [] });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('leaves a stem shared by too many files alone — one hot name must not pin half the repo', () => {
|
|
|
+ const q = 'refactor the user-profile rendering';
|
|
|
+ const out = extractQueryPaths(q, INDEX);
|
|
|
+ expect(out).toEqual({ strippedQuery: q, pinnedFiles: [], unresolvedPathSpans: [] });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('pins all files sharing a stem when within the ambiguity budget', () => {
|
|
|
+ const out = extractQueryPaths('generic-modal close behavior', INDEX);
|
|
|
+ expect(out.pinnedFiles).toEqual(['src/x/generic-modal.tsx', 'src/y/generic-modal.tsx']);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('matches case-insensitively and through wrapping punctuation', () => {
|
|
|
+ expect(extractQueryPaths('see `Background-Image-Table`.', INDEX).pinnedFiles)
|
|
|
+ .toEqual(['src/components/training-set-page/background-image-table.tsx']);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('stems drop only the last extension — a kebab token cannot pin a .module.scss sibling', () => {
|
|
|
+ const out = extractQueryPaths('training-set-page props flow', INDEX);
|
|
|
+ expect(out.pinnedFiles)
|
|
|
+ .toEqual(['src/components/training-set-page/training-set-page.tsx']);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('pins an extension-less indexed file by its exact name', () => {
|
|
|
+ expect(extractQueryPaths('what does the pre-commit hook run', INDEX).pinnedFiles)
|
|
|
+ .toEqual(['scripts/pre-commit']);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('skips tokens the dotted pass consumed and dedupes a file named both ways', () => {
|
|
|
+ const out = extractQueryPaths('src/lib/chat-manager.ts vs chat-manager internals', INDEX);
|
|
|
+ expect(out.pinnedFiles).toEqual(['src/lib/chat-manager.ts']);
|
|
|
+ expect(out.strippedQuery).toBe('vs internals');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('explicit paths win the shared maxPins budget over kebab tokens', () => {
|
|
|
+ const out = extractQueryPaths(
|
|
|
+ 'background-image-table then src/lib/chat-manager.ts', INDEX, { maxPins: 1 },
|
|
|
+ );
|
|
|
+ expect(out.pinnedFiles).toEqual(['src/lib/chat-manager.ts']);
|
|
|
+ // The kebab token was not consumed once the budget was spent — it stays for FTS.
|
|
|
+ expect(out.strippedQuery).toBe('background-image-table then');
|
|
|
+ });
|
|
|
+});
|