import { describe, it, expect } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { MessagingProvider } from '../provider'; import { MockAdapter } from '../adapters/mock'; import { ConversationSettings } from './conversation-settings'; function mount(adapter = new MockAdapter()) { render( {}} /> , ); return adapter; } describe('MockAdapter member management', () => { it('adds and removes a member, and renames', async () => { const a = new MockAdapter(); await a.addMember('th_mock_2', 'pp_sofia'); expect((await a.listMembers('th_mock_2')).some((m) => m.id === 'pp_sofia')).toBe(true); await a.removeMember('th_mock_2', 'pp_sofia'); expect((await a.listMembers('th_mock_2')).some((m) => m.id === 'pp_sofia')).toBe(false); await a.renameConversation('th_mock_2', 'Renamed'); expect((await a.listConversations()).find((c) => c.threadId === 'th_mock_2')?.title).toBe('Renamed'); }); }); describe('', () => { it('lists members and adds one from the directory', async () => { const a = mount(); // A current member is shown. await screen.findByText('Dan Whitaker'); // Add an addable person from the directory. const sofia = await screen.findByText('Sofia Ramirez'); fireEvent.click(sofia); await waitFor(async () => { expect((await a.listMembers('th_mock_2')).some((m) => m.id === 'pp_sofia')).toBe(true); }); }); it('“#” switches the picker to channels you belong to, excluding this one', async () => { mount(); await screen.findByText('Dan Whitaker'); fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } }); // Channels the mock user is in show up as import sources… await screen.findByText('#general'); // …and the conversation being edited is never offered as its own source. expect(screen.queryByText('#Storm crew')).toBeNull(); }); it('imports a channel roster only after confirmation, and reports what landed', async () => { const a = mount(); await screen.findByText('Dan Whitaker'); const before = (await a.listMembers('th_mock_2')).length; fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } }); fireEvent.click(await screen.findByText('#general')); // Staged, NOT applied — picking a channel must not mutate membership on its own. const confirm = await screen.findByRole('button', { name: /^Add \d+$/ }); expect((await a.listMembers('th_mock_2')).length).toBe(before); fireEvent.click(confirm); await waitFor(async () => { expect((await a.listMembers('th_mock_2')).length).toBeGreaterThan(before); }); await screen.findByText(/Added \d+/); }); it('cancelling a staged import leaves membership untouched', async () => { const a = mount(); await screen.findByText('Dan Whitaker'); const before = (await a.listMembers('th_mock_2')).length; fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } }); fireEvent.click(await screen.findByText('#general')); fireEvent.click(await screen.findByRole('button', { name: 'Cancel' })); await screen.findByLabelText('Search people'); // back to the picker expect((await a.listMembers('th_mock_2')).length).toBe(before); }); it('hides the channel-import affordance when the adapter cannot bulk-add', async () => { const a = new MockAdapter(); // A host that implements single add but not addMembers => no import path offered. (a as { addMembers?: unknown }).addMembers = undefined; render( {}} /> , ); await screen.findByText('Dan Whitaker'); expect(screen.getByLabelText('Search people').getAttribute('placeholder')).toBe('Search people…'); fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } }); expect(screen.queryByText('#general')).toBeNull(); // '#' is just a search string here }); });