Skip to content

Instantly share code, notes, and snippets.

@recca0120
Created April 2, 2026 03:47
Show Gist options
  • Select an option

  • Save recca0120/57f2e12d7e21a6f79af31e9ed0982edb to your computer and use it in GitHub Desktop.

Select an option

Save recca0120/57f2e12d7e21a6f79af31e9ed0982edb to your computer and use it in GitHub Desktop.
import { segments as s } from '@code-quest/summoner/test';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useEffect, useRef } from 'react';
import { describe, expect, it } from 'vitest';
import { createFakeClaude } from '../../test/fake-claude';
import { useChannelCompose } from '../channel';
import { ChannelProvider } from '../channel/ChannelContext';
import { PluginProvider } from '../PluginContext';
import { SessionProvider } from '../SessionContext';
import { SocketProvider } from '../SocketContext';
import { TabProvider } from '../TabContext';
/** Renders a test harness that exposes compose context via UI */
function ComposeTestUI() {
const compose = useChannelCompose();
const textareaRef = useRef<HTMLTextAreaElement>(null);
useEffect(() => {
compose.registerFocus((pos?: number) => {
const el = textareaRef.current;
if (!el) return;
if (pos !== undefined) {
setTimeout(() => {
el.focus();
el.setSelectionRange(pos, pos);
}, 0);
} else {
el.focus();
}
});
}, [compose.registerFocus]);
return (
<div>
<textarea
ref={textareaRef}
value={compose.value}
onChange={(e) => {
const pos = e.target.selectionStart;
compose.updateValue(e.target.value, pos);
}}
placeholder="compose"
/>
<span data-testid="hasText">{String(compose.hasText)}</span>
<span data-testid="slashFilter">{compose.slashFilter ?? 'null'}</span>
<span data-testid="hasTextBeforeSlash">{String(compose.hasTextBeforeSlash)}</span>
<button type="button" onClick={compose.submit}>
Submit
</button>
<button type="button" onClick={compose.focusTextarea}>
Focus
</button>
<button type="button" onClick={compose.mentionFile}>
Mention
</button>
<button type="button" onClick={() => compose.addAttachments([new File([''], 'test.txt')])}>
Attach
</button>
<button type="button" onClick={() => compose.insertSlashCommand('/compact ')}>
Insert
</button>
<button type="button" onClick={() => compose.executeSlashCommand('/compact')}>
Execute
</button>
<button type="button" onClick={compose.closeSlash}>
CloseSlash
</button>
{compose.attachedFiles.map((f, i) => (
<span key={f.name}>
{f.name}
<button type="button" onClick={() => compose.removeAttachment(i)}>
Remove
</button>
</span>
))}
</div>
);
}
async function setup() {
const claude = createFakeClaude();
const channelId = await claude.initialize(s.init('sess-1'));
const result = render(
<SocketProvider socket={claude.socket}>
<SessionProvider>
<PluginProvider>
<TabProvider>
<ChannelProvider channelId={channelId}>
<ComposeTestUI />
</ChannelProvider>
</TabProvider>
</PluginProvider>
</SessionProvider>
</SocketProvider>,
);
return { claude, ...result };
}
describe('ChannelComposeProvider', () => {
it('provides initial empty state', async () => {
await setup();
expect(screen.getByPlaceholderText('compose')).toHaveValue('');
expect(screen.getByTestId('hasText')).toHaveTextContent('false');
expect(screen.getByTestId('slashFilter')).toHaveTextContent('null');
});
it('hasText reflects textarea value', async () => {
await setup();
await userEvent.type(screen.getByPlaceholderText('compose'), 'hello');
expect(screen.getByTestId('hasText')).toHaveTextContent('true');
});
it('submit clears value after sending', async () => {
await setup();
await userEvent.type(screen.getByPlaceholderText('compose'), 'hello');
await userEvent.click(screen.getByText('Submit'));
expect(screen.getByPlaceholderText('compose')).toHaveValue('');
});
it('submit does nothing when value is empty', async () => {
await setup();
const textarea = screen.getByPlaceholderText('compose');
expect(textarea).toHaveValue('');
await userEvent.click(screen.getByText('Submit'));
// Still empty, no error thrown
expect(textarea).toHaveValue('');
});
it('focusTextarea focuses the textarea', async () => {
await setup();
await userEvent.click(screen.getByText('Focus'));
expect(screen.getByPlaceholderText('compose')).toHaveFocus();
});
it('mentionFile inserts @ at cursor position', async () => {
await setup();
await userEvent.type(screen.getByPlaceholderText('compose'), 'hello ');
await userEvent.click(screen.getByText('Mention'));
expect(screen.getByPlaceholderText('compose')).toHaveValue('hello @');
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment