test(ui): restore the two suites CI was failing on
Neither failure came from the change that surfaced them; both suites were asserting behavior the product had already moved. issue-2039 mocks `session-actions`, and session creation's authoritative steps live there: the created session becomes current under the directory the server confirmed, and that directory enters sync's routing index. The mock did neither, so every assertion about current session and directory routing read an empty store. It now performs both, which is what makes those assertions test the store's real resolution order again. issue-2903 pinned `<ChatEmptyState />` inside ChatContainer's empty-and-idle branch. The draft transition animation moved that surface to the draft, and the branch now returns a neutral placeholder. What the test is actually for — the idle branch not rendering a status row of its own — is kept.
This commit is contained in:
+13
-10
@@ -226,20 +226,23 @@ describe('issue #2903 busy embedded subagent status-line-only', () => {
|
||||
expect(chatContainerSource).toContain('void ensureSessionRenderable(currentSessionId);');
|
||||
});
|
||||
|
||||
test('empty+busy branch skips empty state so StatusRowContainer can stand alone', () => {
|
||||
test('the empty and idle branch leaves the status row to the busy path', () => {
|
||||
// A busy session with no messages yet must fall through to the viewport so
|
||||
// StatusRowContainer is the only thing on screen. The idle branch returns
|
||||
// before it and must not render one of its own. The empty state itself no
|
||||
// longer lives here: the draft surface owns it since the draft transition
|
||||
// animation landed.
|
||||
expect(chatContainerSource).toContain('if (sessionMessages.length === 0 && !sessionIsWorking)');
|
||||
expect(chatContainerSource).toContain('<ChatEmptyState');
|
||||
expect(chatContainerSource).toContain('<StatusRowContainer />');
|
||||
|
||||
const emptyBusyGuard = 'if (sessionMessages.length === 0 && !sessionIsWorking)';
|
||||
const emptyStateReturn = chatContainerSource.indexOf(emptyBusyGuard);
|
||||
expect(emptyStateReturn).toBeGreaterThan(-1);
|
||||
const emptyStateBlock = chatContainerSource.slice(
|
||||
emptyStateReturn,
|
||||
emptyStateReturn + 1600,
|
||||
const emptyIdleGuard = 'if (sessionMessages.length === 0 && !sessionIsWorking)';
|
||||
const emptyIdleReturn = chatContainerSource.indexOf(emptyIdleGuard);
|
||||
expect(emptyIdleReturn).toBeGreaterThan(-1);
|
||||
const emptyIdleBlock = chatContainerSource.slice(
|
||||
emptyIdleReturn,
|
||||
emptyIdleReturn + 1600,
|
||||
);
|
||||
expect(emptyStateBlock).toContain('<ChatEmptyState');
|
||||
expect(emptyStateBlock).not.toContain('<StatusRowContainer />');
|
||||
expect(emptyIdleBlock).not.toContain('<StatusRowContainer />');
|
||||
});
|
||||
|
||||
test('visibility handshake remains as defense-in-depth for background work', () => {
|
||||
|
||||
@@ -4,6 +4,9 @@ import { togglePermissionAutoAccept } from "../../components/chat/permissionAuto
|
||||
const storage = new Map<string, string>()
|
||||
const createSessionCalls: Array<{ title?: string; directory: string | null; parentID: string | null; metadata?: unknown }> = []
|
||||
const permissionAutoAcceptCalls: Array<[string, boolean]> = []
|
||||
// Sync's session→directory index. `createSession` writes it, and directory
|
||||
// resolution reads it as the authoritative source, so the mock has to keep one.
|
||||
const sessionDirectoryRegistry = new Map<string, string>()
|
||||
let createdSessionDirectory: string | undefined
|
||||
|
||||
const getMockCalls = (fn: unknown): unknown[][] => ((fn as { mock?: { calls: unknown[][] } }).mock?.calls ?? [])
|
||||
@@ -241,13 +244,34 @@ mock.module("../sync-refs", () => ({
|
||||
getSyncMessages: () => [],
|
||||
getSyncParts: () => [],
|
||||
getAllSyncSessions: () => [],
|
||||
getSyncSessionDirectory: () => null,
|
||||
getSyncSessionDirectory: (sessionId: string) => sessionDirectoryRegistry.get(sessionId) ?? null,
|
||||
registerSessionDirectory: (sessionId: string, directory: string) => {
|
||||
sessionDirectoryRegistry.set(sessionId, directory)
|
||||
},
|
||||
}))
|
||||
|
||||
mock.module("../session-actions", () => ({
|
||||
createSession: mock(async (title: string | undefined, directory: string | null, parentID: string | null, metadata?: unknown) => {
|
||||
// Mirrors the real action's authoritative steps: the created session becomes
|
||||
// current under the directory the server confirmed, and that directory enters
|
||||
// the routing index. Everything these tests assert about routing depends on
|
||||
// those two, so a mock without them tests nothing.
|
||||
createSession: mock(async (
|
||||
title: string | undefined,
|
||||
directory: string | null,
|
||||
parentID: string | null,
|
||||
metadata?: unknown,
|
||||
selectionTransition?: "submitted-draft",
|
||||
) => {
|
||||
createSessionCalls.push({ title, directory, parentID, metadata })
|
||||
return { id: "ses_issue_2039", directory: createdSessionDirectory ?? directory }
|
||||
const session = { id: "ses_issue_2039", directory: createdSessionDirectory ?? directory }
|
||||
const sessionDirectory = session.directory ?? null
|
||||
if (sessionDirectory) {
|
||||
sessionDirectoryRegistry.set(session.id, sessionDirectory)
|
||||
}
|
||||
const { useSessionUIStore: store } = await import("../session-ui-store")
|
||||
store.getState().setCurrentSession(session.id, sessionDirectory, selectionTransition)
|
||||
store.getState().markSessionAsOpenChamberCreated(session.id)
|
||||
return session
|
||||
}),
|
||||
deleteSession: mock(async () => true),
|
||||
deleteSessions: mock(async () => ({ deletedIds: [], failedIds: [] })),
|
||||
@@ -322,6 +346,7 @@ describe("issue 2039 draft auto-accept", () => {
|
||||
beforeEach(() => {
|
||||
storage.clear()
|
||||
createSessionCalls.length = 0
|
||||
sessionDirectoryRegistry.clear()
|
||||
permissionAutoAcceptCalls.length = 0
|
||||
createdSessionDirectory = undefined
|
||||
|
||||
|
||||
Reference in New Issue
Block a user