00a2cc474a
Adds a third membership beyond dm/group: a discoverable, joinable room. - contract: Membership += 'channel'; Conversation.topic; ChannelSummary + CreateChannelInput; optional adapter methods browseChannels/createChannel/ joinChannel/leaveChannel (capability-degrading — absent hides the UI) - MockAdapter implements them (seeds a joined public, a joinable public, and a private channel); listConversations now returns only threads I'm in - useChannels hook (browse/create/join/leave + supported flag) - UI: sidebar sections (Channels vs Direct messages), a + that opens a ChannelBrowser (join + create), # / lock glyphs; themeable styles - KernelClientAdapter passes channel membership + topic through - 4 channel tests (mock browse/join/create + render browse→join); 52 total green Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 lines
1.7 KiB
TypeScript
43 lines
1.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
import { MessagingProvider } from '../provider';
|
|
import { Messenger } from './messenger';
|
|
import { MockAdapter } from '../adapters/mock';
|
|
|
|
function mount() {
|
|
return render(
|
|
<MessagingProvider adapter={new MockAdapter()}>
|
|
<Messenger />
|
|
</MessagingProvider>,
|
|
);
|
|
}
|
|
|
|
describe('<Messenger /> (rendered UI over an adapter)', () => {
|
|
it('renders the conversation list and auto-selects the first thread', async () => {
|
|
mount();
|
|
// Seeded group conversation title from the mock.
|
|
expect(await screen.findByText('Storm response — East side')).toBeTruthy();
|
|
// Auto-selected thread shows its seeded message.
|
|
expect(await screen.findByText('Crew is rolling out at 7.')).toBeTruthy();
|
|
});
|
|
|
|
it('sends a message through the adapter and shows it in the thread', async () => {
|
|
mount();
|
|
// Wait for the auto-selected thread's composer (avoids a race with the auto-select effect).
|
|
const input = (await screen.findByLabelText('Message')) as HTMLInputElement;
|
|
fireEvent.change(input, { target: { value: 'on our way' } });
|
|
fireEvent.click(screen.getByText('Send'));
|
|
|
|
await waitFor(() => expect(screen.getByText('on our way')).toBeTruthy());
|
|
// Composer cleared after send.
|
|
expect(input.value).toBe('');
|
|
});
|
|
|
|
it('switches threads when another conversation is clicked', async () => {
|
|
mount();
|
|
// Click the DM (its title is the other participant's name from the mock directory).
|
|
fireEvent.click(await screen.findByText('Sofia Ramirez'));
|
|
expect(await screen.findByText('Can you review the Henderson estimate?')).toBeTruthy();
|
|
});
|
|
});
|