mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-28 15:01:14 -07:00
test(cli): refactor tests for async render utilities (#23252)
This commit is contained in:
committed by
GitHub
parent
86a3a913b5
commit
6c78eb7a39
@@ -6,6 +6,7 @@
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { act, useState } from 'react';
|
||||
import type { FzfResultItem } from 'fzf';
|
||||
import { renderHook } from '../../test-utils/render.js';
|
||||
import { waitFor } from '../../test-utils/async.js';
|
||||
import { useSlashCompletion } from './useSlashCompletion.js';
|
||||
@@ -38,8 +39,26 @@ const getConstructorCallCount = () => asyncFzfConstructorCalls;
|
||||
// Note: This is a simplified reimplementation that may diverge from real fzf behavior.
|
||||
// Integration tests in useSlashCompletion.integration.test.ts use the real fzf library
|
||||
// to catch any behavioral differences and serve as our "canary in a coal mine."
|
||||
|
||||
let deferredMatch: { resolve: (val?: unknown) => void } | null = null;
|
||||
|
||||
export const resolveMatch = async () => {
|
||||
// Wait up to 1s for deferredMatch to be set by the hook
|
||||
const start = Date.now();
|
||||
while (!deferredMatch && Date.now() - start < 1000) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
|
||||
if (deferredMatch) {
|
||||
await act(async () => {
|
||||
deferredMatch?.resolve(null);
|
||||
});
|
||||
deferredMatch = null;
|
||||
}
|
||||
};
|
||||
|
||||
function simulateFuzzyMatching(items: readonly string[], query: string) {
|
||||
const results = [];
|
||||
const results: Array<FzfResultItem<string>> = [];
|
||||
if (query) {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
for (const item of items) {
|
||||
@@ -98,7 +117,13 @@ function simulateFuzzyMatching(items: readonly string[], query: string) {
|
||||
|
||||
// Sort by score descending (better matches first)
|
||||
results.sort((a, b) => b.score - a.score);
|
||||
return Promise.resolve(results);
|
||||
return new Promise((resolve) => {
|
||||
deferredMatch = {
|
||||
resolve: () => {
|
||||
resolve(results);
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Mock the fzf module to provide a working fuzzy search implementation for tests
|
||||
@@ -199,38 +224,25 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
createTestCommand({ name: 'chat', description: 'Manage chat history' }),
|
||||
];
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(slashCommands.length);
|
||||
expect(result.current.suggestions.map((s) => s.label)).toEqual(
|
||||
expect.arrayContaining([
|
||||
'help',
|
||||
'clear',
|
||||
'memory',
|
||||
'chat',
|
||||
'stats',
|
||||
]),
|
||||
);
|
||||
});
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(slashCommands.length);
|
||||
expect(result.current.suggestions.map((s) => s.label)).toEqual(
|
||||
expect.arrayContaining(['help', 'clear', 'memory', 'chat', 'stats']),
|
||||
);
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should filter commands based on partial input', async () => {
|
||||
@@ -241,44 +253,33 @@ describe('useSlashCompletion', () => {
|
||||
const setIsLoadingSuggestions = vi.fn();
|
||||
const setIsPerfectMatch = vi.fn();
|
||||
|
||||
let result: {
|
||||
current: { completionStart: number; completionEnd: number };
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useSlashCompletion({
|
||||
enabled: true,
|
||||
query: '/mem',
|
||||
slashCommands,
|
||||
commandContext: mockCommandContext,
|
||||
setSuggestions,
|
||||
setIsLoadingSuggestions,
|
||||
setIsPerfectMatch,
|
||||
}),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useSlashCompletion({
|
||||
enabled: true,
|
||||
query: '/mem',
|
||||
slashCommands,
|
||||
commandContext: mockCommandContext,
|
||||
setSuggestions,
|
||||
setIsLoadingSuggestions,
|
||||
setIsPerfectMatch,
|
||||
}),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(setSuggestions).toHaveBeenCalledWith([
|
||||
{
|
||||
label: 'memory',
|
||||
value: 'memory',
|
||||
description: 'Manage memory',
|
||||
commandKind: CommandKind.BUILT_IN,
|
||||
},
|
||||
]);
|
||||
expect(result.current.completionStart).toBe(1);
|
||||
expect(result.current.completionEnd).toBe(4);
|
||||
});
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(setSuggestions).toHaveBeenCalledWith([
|
||||
{
|
||||
label: 'memory',
|
||||
value: 'memory',
|
||||
description: 'Manage memory',
|
||||
commandKind: CommandKind.BUILT_IN,
|
||||
},
|
||||
]);
|
||||
expect(result.current.completionStart).toBe(1);
|
||||
expect(result.current.completionEnd).toBe(4);
|
||||
});
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should suggest commands based on partial altNames', async () => {
|
||||
@@ -290,22 +291,17 @@ describe('useSlashCompletion', () => {
|
||||
'check session stats. Usage: /stats [session|model|tools]',
|
||||
}),
|
||||
];
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/usage',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/usage',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([
|
||||
@@ -319,7 +315,7 @@ describe('useSlashCompletion', () => {
|
||||
]);
|
||||
expect(result.current.completionStart).toBe(1);
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should provide suggestions even for a perfectly typed command that is a leaf node', async () => {
|
||||
@@ -330,28 +326,24 @@ describe('useSlashCompletion', () => {
|
||||
action: vi.fn(),
|
||||
}),
|
||||
];
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/clear',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/clear',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(1);
|
||||
expect(result.current.suggestions[0].label).toBe('clear');
|
||||
expect(result.current.completionStart).toBe(1);
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it.each([['/?'], ['/usage']])(
|
||||
@@ -373,28 +365,22 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
query,
|
||||
mockSlashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
query,
|
||||
mockSlashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(1);
|
||||
expect(result.current.completionStart).toBe(1);
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -417,7 +403,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/review',
|
||||
@@ -426,6 +412,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
// All three should match 'review' in our fuzzy mock or as prefix/exact
|
||||
expect(result.current.suggestions.length).toBe(3);
|
||||
@@ -472,15 +460,18 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result: chatResult, unmount: unmountChat } = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/chat',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
const { result: chatResult, unmount: unmountChat } = await renderHook(
|
||||
() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/chat',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(chatResult.current.suggestions[0]).toMatchObject({
|
||||
label: 'list',
|
||||
@@ -489,15 +480,18 @@ describe('useSlashCompletion', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const { result: resumeResult, unmount: unmountResume } = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/resume',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
const { result: resumeResult, unmount: unmountResume } = await renderHook(
|
||||
() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/resume',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(resumeResult.current.suggestions[0]).toMatchObject({
|
||||
label: 'list',
|
||||
@@ -540,7 +534,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/resum',
|
||||
@@ -549,6 +543,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions[0]).toMatchObject({
|
||||
label: 'list',
|
||||
@@ -579,7 +575,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/?',
|
||||
@@ -588,6 +584,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
// 'help' should be first because '?' is an exact altName match
|
||||
expect(result.current.suggestions[0].label).toBe('help');
|
||||
@@ -608,7 +606,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/chat',
|
||||
@@ -617,6 +615,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
// Should show the auto-session entry plus subcommands of 'chat'
|
||||
expect(result.current.suggestions).toHaveLength(3);
|
||||
@@ -638,55 +638,45 @@ describe('useSlashCompletion', () => {
|
||||
const slashCommands = [
|
||||
createTestCommand({ name: 'clear', description: 'Clear the screen' }),
|
||||
];
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/clear ',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/clear ',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(0);
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should not provide suggestions for an unknown command', async () => {
|
||||
const slashCommands = [
|
||||
createTestCommand({ name: 'help', description: 'Show help' }),
|
||||
];
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/unknown-command',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/unknown-command',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(0);
|
||||
expect(result.current.completionStart).toBe(1);
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should not suggest hidden commands', async () => {
|
||||
@@ -701,28 +691,23 @@ describe('useSlashCompletion', () => {
|
||||
hidden: true,
|
||||
}),
|
||||
];
|
||||
let result: {
|
||||
current: ReturnType<typeof useTestHarnessForSlashCompletion>;
|
||||
};
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
const hook = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
result = hook.result;
|
||||
unmount = hook.unmount;
|
||||
});
|
||||
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
expect(result.current.suggestions[0].label).toBe('visible');
|
||||
});
|
||||
unmount!();
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -739,7 +724,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/memory ',
|
||||
@@ -748,6 +733,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(2);
|
||||
expect(result.current.suggestions).toEqual(
|
||||
@@ -785,7 +772,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
const { result } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/memory',
|
||||
@@ -794,6 +781,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
// Should verify that we see BOTH 'memory' and 'memory-leak'
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(2);
|
||||
@@ -827,7 +816,7 @@ describe('useSlashCompletion', () => {
|
||||
],
|
||||
}),
|
||||
];
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/memory ',
|
||||
@@ -836,6 +825,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(2);
|
||||
expect(result.current.suggestions).toEqual(
|
||||
@@ -869,7 +860,7 @@ describe('useSlashCompletion', () => {
|
||||
],
|
||||
}),
|
||||
];
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/memory a',
|
||||
@@ -878,6 +869,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([
|
||||
{
|
||||
@@ -903,7 +896,7 @@ describe('useSlashCompletion', () => {
|
||||
],
|
||||
}),
|
||||
];
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/memory dothisnow',
|
||||
@@ -911,11 +904,12 @@ describe('useSlashCompletion', () => {
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(0);
|
||||
expect(result.current.completionStart).toBe(8);
|
||||
});
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(0);
|
||||
expect(result.current.completionStart).toBe(8);
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
@@ -928,12 +922,18 @@ describe('useSlashCompletion', () => {
|
||||
'my-chat-tag-2',
|
||||
'another-channel',
|
||||
];
|
||||
const mockCompletionFn = vi
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
async (_context: CommandContext, partialArg: string) =>
|
||||
availableTags.filter((tag) => tag.startsWith(partialArg)),
|
||||
);
|
||||
let deferredCompletion: { resolve: (v: string[]) => void } | null = null;
|
||||
const mockCompletionFn = vi.fn().mockImplementation(
|
||||
(_context: CommandContext, partialArg: string) =>
|
||||
new Promise((resolve) => {
|
||||
deferredCompletion = {
|
||||
resolve: () =>
|
||||
resolve(
|
||||
availableTags.filter((tag) => tag.startsWith(partialArg)),
|
||||
),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const slashCommands = [
|
||||
createTestCommand({
|
||||
@@ -949,7 +949,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/chat resume my-ch',
|
||||
@@ -958,38 +958,45 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(mockCompletionFn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
invocation: {
|
||||
raw: '/chat resume my-ch',
|
||||
name: 'resume',
|
||||
args: 'my-ch',
|
||||
},
|
||||
}),
|
||||
'my-ch',
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockCompletionFn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
invocation: {
|
||||
raw: '/chat resume my-ch',
|
||||
name: 'resume',
|
||||
args: 'my-ch',
|
||||
},
|
||||
}),
|
||||
'my-ch',
|
||||
);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([
|
||||
{ label: 'my-chat-tag-1', value: 'my-chat-tag-1' },
|
||||
{ label: 'my-chat-tag-2', value: 'my-chat-tag-2' },
|
||||
]);
|
||||
expect(result.current.completionStart).toBe(13);
|
||||
expect(result.current.isLoadingSuggestions).toBe(false);
|
||||
});
|
||||
deferredCompletion?.resolve([]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([
|
||||
{ label: 'my-chat-tag-1', value: 'my-chat-tag-1' },
|
||||
{ label: 'my-chat-tag-2', value: 'my-chat-tag-2' },
|
||||
]);
|
||||
expect(result.current.completionStart).toBe(13);
|
||||
expect(result.current.isLoadingSuggestions).toBe(false);
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should call command.completion with an empty string when args start with a space', async () => {
|
||||
const mockCompletionFn = vi
|
||||
.fn()
|
||||
.mockResolvedValue(['my-chat-tag-1', 'my-chat-tag-2', 'my-channel']);
|
||||
let deferredCompletion: { resolve: (v: string[]) => void } | null = null;
|
||||
const mockCompletionFn = vi.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
deferredCompletion = {
|
||||
resolve: () =>
|
||||
resolve(['my-chat-tag-1', 'my-chat-tag-2', 'my-channel']),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const slashCommands = [
|
||||
createTestCommand({
|
||||
@@ -1005,7 +1012,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/chat resume ',
|
||||
@@ -1014,32 +1021,38 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(mockCompletionFn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
invocation: {
|
||||
raw: '/chat resume ',
|
||||
name: 'resume',
|
||||
args: '',
|
||||
},
|
||||
}),
|
||||
'',
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockCompletionFn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
invocation: {
|
||||
raw: '/chat resume ',
|
||||
name: 'resume',
|
||||
args: '',
|
||||
},
|
||||
}),
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(3);
|
||||
expect(result.current.completionStart).toBe(13);
|
||||
});
|
||||
deferredCompletion?.resolve([]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(3);
|
||||
expect(result.current.completionStart).toBe(13);
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should handle completion function that returns null', async () => {
|
||||
const mockCompletionFn = vi.fn().mockResolvedValue(null);
|
||||
let deferredCompletion: { resolve: (v: null) => void } | null = null;
|
||||
const mockCompletionFn = vi.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
deferredCompletion = { resolve: () => resolve(null) };
|
||||
}),
|
||||
);
|
||||
|
||||
const slashCommands = [
|
||||
createTestCommand({
|
||||
@@ -1049,7 +1062,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/test arg',
|
||||
@@ -1058,6 +1071,10 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
deferredCompletion?.resolve(null);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([]);
|
||||
expect(result.current.isLoadingSuggestions).toBe(false);
|
||||
@@ -1083,7 +1100,7 @@ describe('useSlashCompletion', () => {
|
||||
},
|
||||
] as SlashCommand[];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/',
|
||||
@@ -1092,6 +1109,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -1129,7 +1148,7 @@ describe('useSlashCompletion', () => {
|
||||
},
|
||||
] as SlashCommand[];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/summ',
|
||||
@@ -1138,6 +1157,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([
|
||||
{
|
||||
@@ -1175,7 +1196,7 @@ describe('useSlashCompletion', () => {
|
||||
},
|
||||
] as SlashCommand[];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/memory ',
|
||||
@@ -1184,6 +1205,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -1215,7 +1238,7 @@ describe('useSlashCompletion', () => {
|
||||
},
|
||||
] as SlashCommand[];
|
||||
|
||||
const { result, unmount } = renderHook(() =>
|
||||
const { result, unmount } = await renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/custom',
|
||||
@@ -1224,6 +1247,8 @@ describe('useSlashCompletion', () => {
|
||||
),
|
||||
);
|
||||
|
||||
await resolveMatch();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toEqual([
|
||||
{
|
||||
@@ -1251,7 +1276,7 @@ describe('useSlashCompletion', () => {
|
||||
}),
|
||||
];
|
||||
|
||||
const { rerender, unmount } = renderHook(
|
||||
const { rerender, unmount } = await renderHook(
|
||||
({ enabled, query }) =>
|
||||
useSlashCompletion({
|
||||
enabled,
|
||||
|
||||
Reference in New Issue
Block a user