From 880b63d2a0f3829ed440f330b9f93f4dda450abe Mon Sep 17 00:00:00 2001 From: Aamer Akhter Date: Mon, 8 Jun 2026 10:19:38 -0400 Subject: [PATCH] test: COD-30 stabilize focused test suites --- test/mobile/keyboard.test.ts | 54 +++++++++++-------------------- test/operation-lightspeed.test.ts | 20 ++++++++---- test/ralph-integration.test.ts | 33 ++++++++++--------- test/setup.ts | 6 +++- 4 files changed, 54 insertions(+), 59 deletions(-) diff --git a/test/mobile/keyboard.test.ts b/test/mobile/keyboard.test.ts index 48c701f0..92491627 100644 --- a/test/mobile/keyboard.test.ts +++ b/test/mobile/keyboard.test.ts @@ -5,16 +5,22 @@ import { PORTS, KEYBOARD, SELECTORS, BODY_CLASSES, WAIT } from './helpers/consta import { createTestServer, stopTestServer } from './helpers/server.js'; import { createDevicePage, getBrowser, closeAllBrowsers } from './helpers/browser.js'; import { - showKeyboard, hideKeyboard, - showKeyboardViaCDP, hideKeyboardViaCDP, - showKeyboardViaMock, hideKeyboardViaMock, - showKeyboardViaDOM, hideKeyboardViaDOM, + showKeyboard, + hideKeyboard, + showKeyboardViaCDP, + hideKeyboardViaCDP, + showKeyboardViaMock, + hideKeyboardViaMock, + showKeyboardViaDOM, + hideKeyboardViaDOM, setupViewportMock, } from './helpers/keyboard-sim.js'; import { getCDP, setVisualViewportHeight } from './helpers/cdp.js'; import { - assertHasClass, assertNotHasClass, - assertVisible, assertHidden, + assertHasClass, + assertNotHasClass, + assertVisible, + assertHidden, getCSSProperty, } from './helpers/assertions.js'; import { REPRESENTATIVE_DEVICES } from './devices.js'; @@ -167,9 +173,7 @@ describe('Virtual Keyboard', () => { const success = await showKeyboardViaMock(page, KEYBOARD.TYPICAL_IOS_HEIGHT); expect(success).toBe(true); - const hasClass = await page.evaluate(() => - document.body.classList.contains('keyboard-visible'), - ); + const hasClass = await page.evaluate(() => document.body.classList.contains('keyboard-visible')); expect(hasClass).toBe(true); } finally { await context.close(); @@ -280,12 +284,13 @@ describe('Virtual Keyboard', () => { expect(mainPadding).toBe(''); }); - it('accessory bar has 7 action buttons', async () => { - const count = await page.evaluate(() => { - const buttons = document.querySelectorAll('.keyboard-accessory-bar [data-action]'); - return buttons.length; + it('accessory bar has the simple-mode action buttons', async () => { + const actions = await page.evaluate(() => { + return Array.from(document.querySelectorAll('.keyboard-accessory-bar [data-action]')).map( + (button) => (button as HTMLElement).dataset.action + ); }); - expect(count).toBe(7); + expect(actions).toEqual(['scroll-up', 'scroll-down', 'init', 'clear', 'paste', 'dismiss']); }); it('double-tap confirm on /clear button', async () => { @@ -321,27 +326,6 @@ describe('Virtual Keyboard', () => { expect(text).toBe('Tap again'); }); - it('double-tap confirm on /compact button', async () => { - await showKeyboard(page, KEYBOARD.TYPICAL_IOS_HEIGHT); - await page.waitForTimeout(WAIT.KEYBOARD_ANIMATION); - - await page.evaluate(` - if (typeof app !== 'undefined') app.activeSessionId = 'test-session'; - `); - - await page.evaluate(() => { - const btn = document.querySelector('[data-action="compact"]') as HTMLElement; - btn?.click(); - }); - await page.waitForTimeout(100); - - const confirming = await page.evaluate(() => { - const btn = document.querySelector('[data-action="compact"]'); - return btn?.classList.contains('confirming') ?? false; - }); - expect(confirming).toBe(true); - }); - it('double-tap expires after 2s', async () => { await showKeyboard(page, KEYBOARD.TYPICAL_IOS_HEIGHT); await page.waitForTimeout(WAIT.KEYBOARD_ANIMATION); diff --git a/test/operation-lightspeed.test.ts b/test/operation-lightspeed.test.ts index 2e3dbfec..c1c3a351 100644 --- a/test/operation-lightspeed.test.ts +++ b/test/operation-lightspeed.test.ts @@ -1256,7 +1256,7 @@ describe('Operation Lightspeed', () => { await Promise.all(ids.map((id) => deleteSession(baseUrl, id))); }); - it('should correctly filter SSE under concurrent session lifecycle', async () => { + it('should broadcast lifecycle events while filtering concurrent session terminal streams', async () => { // Create 2 sessions const target = await createSession(baseUrl); const other = await createSession(baseUrl); @@ -1309,15 +1309,21 @@ describe('Operation Lightspeed', () => { const events = parseSSEEvents(receivedData); - // Should see target's rename but not other's events - const targetUpdated = events.find((e) => e.event === 'session:updated' && (e.data as any).id === target); + // session:updated is a lifecycle event broadcast to all clients; the + // subscription filter applies only to high-volume terminal streams. + const updatedEvents = events.filter((e) => e.event === 'session:updated'); + const targetUpdated = updatedEvents.find((e) => (e.data as any).id === target); expect(targetUpdated).toBeDefined(); - // Should NOT see other's events - const otherEvents = events.filter( - (e) => ((e.data as any)?.id === other || (e.data as any)?.sessionId === other) && e.event !== 'init' + const otherLifecycleEvents = events.filter( + (e) => e.event !== 'init' && e.event !== 'session:terminal' && (e.data as any)?.id === other ); - expect(otherEvents.length).toBe(0); + expect(otherLifecycleEvents.length).toBeGreaterThan(0); + + const otherTerminalEvents = events.filter( + (e) => e.event === 'session:terminal' && (e.data as any)?.sessionId === other + ); + expect(otherTerminalEvents.length).toBe(0); await deleteSession(baseUrl, target); }); diff --git a/test/ralph-integration.test.ts b/test/ralph-integration.test.ts index d84b9a6c..27521066 100644 --- a/test/ralph-integration.test.ts +++ b/test/ralph-integration.test.ts @@ -106,7 +106,7 @@ describe('Ralph Integration Tests', () => { const res = await fetch(`${baseUrl}/api/sessions/non-existent-id`); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -136,7 +136,8 @@ describe('Ralph Integration Tests', () => { // Verify session is gone const getRes = await fetch(`${baseUrl}/api/sessions/${sessionId}`); const getData = await getRes.json(); - expect(getData.error).toBe('Session not found'); + expect(getRes.status).toBe(404); + expect(getData.error).toContain('not found'); }); it('should create shell session', async () => { @@ -191,7 +192,7 @@ describe('Ralph Integration Tests', () => { const res = await fetch(`${baseUrl}/api/sessions/fake-session/ralph-state`); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.error).toContain('not found'); }); @@ -359,7 +360,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(400); expect(data.success).toBe(false); expect(data.errorCode).toBe('INVALID_INPUT'); }); @@ -372,7 +373,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -395,7 +396,7 @@ describe('Ralph Integration Tests', () => { createdSessions.push(createData.sessionId); // Wait for session to be ready - await new Promise(r => setTimeout(r, 200)); + await new Promise((r) => setTimeout(r, 200)); const res = await fetch(`${baseUrl}/api/sessions/${createData.sessionId}/resize`, { method: 'POST', @@ -422,7 +423,7 @@ describe('Ralph Integration Tests', () => { createdSessions.push(createData.sessionId); // Wait for session to be ready - await new Promise(r => setTimeout(r, 200)); + await new Promise((r) => setTimeout(r, 200)); const res = await fetch(`${baseUrl}/api/sessions/${createData.sessionId}/resize`, { method: 'POST', @@ -431,7 +432,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(400); expect(data.success).toBe(false); expect(data.errorCode).toBe('INVALID_INPUT'); }); @@ -472,7 +473,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -497,7 +498,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(400); expect(data.success).toBe(false); expect(data.errorCode).toBe('INVALID_INPUT'); }); @@ -536,7 +537,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -561,7 +562,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(400); expect(data.success).toBe(false); expect(data.errorCode).toBe('INVALID_INPUT'); }); @@ -626,7 +627,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -907,7 +908,7 @@ describe('Ralph Integration Tests', () => { }); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -991,7 +992,7 @@ describe('Ralph Integration Tests', () => { const res = await fetch(`${baseUrl}/api/sessions/fake-session/output`); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); @@ -1021,7 +1022,7 @@ describe('Ralph Integration Tests', () => { const res = await fetch(`${baseUrl}/api/sessions/fake-session/terminal`); const data = await res.json(); - expect(res.status).toBe(200); + expect(res.status).toBe(404); expect(data.success).toBe(false); expect(data.errorCode).toBe('NOT_FOUND'); }); diff --git a/test/setup.ts b/test/setup.ts index eb853324..f55acbc1 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -6,11 +6,15 @@ * This means tests CANNOT kill, create, or interact with real tmux * sessions regardless of what the test code does. * - * This setup file only handles mock/timer cleanup between tests. + * This setup file strips shell-level auth configuration that can leak from a + * running Codeman instance, then handles mock/timer cleanup between tests. */ import { afterEach, vi } from 'vitest'; +delete process.env.CODEMAN_PASSWORD; +delete process.env.CODEMAN_USERNAME; + afterEach(() => { vi.clearAllMocks(); vi.useRealTimers();