// @vitest-environment jsdom /** * test/queue.test.ts — W2 frontend inject-queue helper (public/queue.ts). * * fetch is mocked; every helper must NEVER throw (mirrors quick-reply's * never-throw discipline). Covers: POST url/body + parsed result, non-2xx → * {ok:false,status}, network reject → {ok:false}, DELETE, and the frame parser. */ import { describe, it, expect, vi, afterEach } from 'vitest' import { enqueueFollowup, clearQueue, queueLengthFromFrame } from '../public/queue.js' afterEach(() => { vi.restoreAllMocks() // @ts-expect-error — reset the mocked global between tests delete globalThis.fetch }) function mockFetch(impl: (url: string, init?: RequestInit) => Promise | Response): void { globalThis.fetch = vi.fn(impl as unknown as typeof fetch) } describe('enqueueFollowup', () => { it('POSTs to the session queue URL with a JSON {text,appendEnter} body', async () => { const calls: Array<{ url: string; init?: RequestInit }> = [] mockFetch((url, init) => { calls.push({ url, init }) return new Response(JSON.stringify({ length: 1 }), { status: 200 }) }) const result = await enqueueFollowup('sess-1', 'run tests', true) expect(result).toEqual({ ok: true, length: 1 }) expect(calls).toHaveLength(1) expect(calls[0]!.url).toBe('/live-sessions/sess-1/queue') expect(calls[0]!.init?.method).toBe('POST') expect(JSON.parse(String(calls[0]!.init?.body))).toEqual({ text: 'run tests', appendEnter: true, }) }) it('returns {ok:false,status} on a non-2xx response and never throws', async () => { mockFetch(() => new Response('nope', { status: 409 })) const result = await enqueueFollowup('sess-1', 'x', false) expect(result).toEqual({ ok: false, status: 409 }) }) it('returns {ok:false} on a network reject and never throws', async () => { mockFetch(() => Promise.reject(new Error('offline'))) const result = await enqueueFollowup('sess-1', 'x', false) expect(result).toEqual({ ok: false }) }) it('returns {ok:true} with undefined length when the body omits length', async () => { mockFetch(() => new Response(JSON.stringify({}), { status: 200 })) const result = await enqueueFollowup('sess-1', 'x', false) expect(result).toEqual({ ok: true, length: undefined }) }) }) describe('clearQueue', () => { it('DELETEs the session queue URL and returns true on success', async () => { const calls: Array<{ url: string; init?: RequestInit }> = [] mockFetch((url, init) => { calls.push({ url, init }) return new Response(JSON.stringify({ length: 0 }), { status: 200 }) }) const ok = await clearQueue('sess-2') expect(ok).toBe(true) expect(calls[0]!.url).toBe('/live-sessions/sess-2/queue') expect(calls[0]!.init?.method).toBe('DELETE') }) it('returns false on a non-2xx and on a network reject (never throws)', async () => { mockFetch(() => new Response('', { status: 404 })) expect(await clearQueue('sess-2')).toBe(false) mockFetch(() => Promise.reject(new Error('offline'))) expect(await clearQueue('sess-2')).toBe(false) }) }) describe('queueLengthFromFrame', () => { it('returns the length for a {type:queue,length} frame', () => { expect(queueLengthFromFrame({ type: 'queue', length: 3 })).toBe(3) }) it('returns null for unrelated or malformed frames', () => { expect(queueLengthFromFrame({ type: 'status', status: 'idle' })).toBeNull() expect(queueLengthFromFrame({ type: 'queue' })).toBeNull() expect(queueLengthFromFrame(null)).toBeNull() expect(queueLengthFromFrame('nope')).toBeNull() }) })