You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
3.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ScreenPoller } from '../../src/bridge/screen-poller.js';
// Mock CmuxClient
function createMockClient(screenContent = 'line1\nline2\nline3') {
return {
readScreen: vi.fn().mockResolvedValue(screenContent),
sendText: vi.fn(),
sendKey: vi.fn(),
} as any;
}
describe('ScreenPoller', () => {
beforeEach(() => {
vi.useFakeTimers();
});
it('emits update on first poll', async () => {
const client = createMockClient();
const poller = new ScreenPoller(client, 200);
const updates: any[] = [];
poller.on('update', (u: any) => updates.push(u));
poller.subscribe('client1', 'workspace:1', 'surface:1');
// Fast-forward past first poll
await vi.advanceTimersByTimeAsync(50);
expect(updates.length).toBe(1);
expect(updates[0].full).toBe(true);
expect(updates[0].lines).toEqual(['line1', 'line2', 'line3']);
poller.destroy();
});
it('does not emit when content is unchanged', async () => {
const client = createMockClient();
const poller = new ScreenPoller(client, 200);
const updates: any[] = [];
poller.on('update', (u: any) => updates.push(u));
poller.subscribe('client1', 'workspace:1', 'surface:1');
// First poll
await vi.advanceTimersByTimeAsync(50);
expect(updates.length).toBe(1);
// Second poll — same content
await vi.advanceTimersByTimeAsync(250);
expect(updates.length).toBe(1);
poller.destroy();
});
it('sends diff patches when content changes', async () => {
const client = createMockClient('line1\nline2\nline3');
const poller = new ScreenPoller(client, 200);
const updates: any[] = [];
poller.on('update', (u: any) => updates.push(u));
poller.subscribe('client1', 'workspace:1', 'surface:1');
// First poll
await vi.advanceTimersByTimeAsync(50);
expect(updates.length).toBe(1);
// Change content
client.readScreen.mockResolvedValue('line1\nline2-changed\nline3');
// Second poll
await vi.advanceTimersByTimeAsync(250);
expect(updates.length).toBe(2);
expect(updates[1].full).toBe(false);
expect(updates[1].patches).toBeDefined();
poller.destroy();
});
it('stops polling when all subscribers unsubscribe', async () => {
const client = createMockClient();
const poller = new ScreenPoller(client, 200);
poller.subscribe('client1', 'workspace:1', 'surface:1');
await vi.advanceTimersByTimeAsync(50);
poller.unsubscribe('client1', 'surface:1');
const callCount = client.readScreen.mock.calls.length;
await vi.advanceTimersByTimeAsync(500);
// Should not have polled again
expect(client.readScreen.mock.calls.length).toBe(callCount);
poller.destroy();
});
it('resets to fast polling on resetToFast', async () => {
const client = createMockClient();
const poller = new ScreenPoller(client, 200);
poller.subscribe('client1', 'workspace:1', 'surface:1');
await vi.advanceTimersByTimeAsync(50);
// Simulate idle
for (let i = 0; i < 5; i++) {
await vi.advanceTimersByTimeAsync(250);
}
poller.resetToFast('surface:1');
// Should have reset idle count internally
poller.destroy();
});
});