From 4aeaba69225151b36ab7c23803eefb99ae140f8f Mon Sep 17 00:00:00 2001 From: xxxigm Date: Thu, 25 Jun 2026 00:05:48 +0700 Subject: [PATCH] test(desktop): cover undefined/null attachment holes in ref helpers Regression for the refText crash: attachmentDisplayText and optimisticAttachmentRef must return null (not throw) when handed an undefined/null attachment hole, so the submit path can't reproduce "Cannot read properties of undefined (reading 'refText')". --- apps/desktop/src/lib/chat-runtime.test.ts | 28 ++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/lib/chat-runtime.test.ts b/apps/desktop/src/lib/chat-runtime.test.ts index 1b4efb33ad5..46ebcfefb1a 100644 --- a/apps/desktop/src/lib/chat-runtime.test.ts +++ b/apps/desktop/src/lib/chat-runtime.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest' import type { ComposerAttachment } from '@/store/composer' -import { coerceThinkingText, optimisticAttachmentRef, parseCommandDispatch } from './chat-runtime' +import { attachmentDisplayText, coerceThinkingText, optimisticAttachmentRef, parseCommandDispatch } from './chat-runtime' const DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANS' @@ -36,6 +36,32 @@ describe('optimisticAttachmentRef', () => { '@file:src/a.ts' ) }) + + // Session switches / draft restores can leave undefined|null holes in the + // composer attachments array. AttachmentList already filters them (#49624), + // but the submit path maps the same array through these helpers — an unguarded + // hole threw "Cannot read properties of undefined (reading 'refText')", + // crashing the chat surface (blank pane). The helpers must no-op on holes. + it('returns null for an undefined attachment instead of throwing', () => { + expect(() => optimisticAttachmentRef(undefined as unknown as ComposerAttachment)).not.toThrow() + expect(optimisticAttachmentRef(undefined as unknown as ComposerAttachment)).toBeNull() + }) + + it('returns null for a null attachment instead of throwing', () => { + expect(optimisticAttachmentRef(null as unknown as ComposerAttachment)).toBeNull() + }) +}) + +describe('attachmentDisplayText', () => { + it('returns null for undefined|null instead of reading .kind/.refText on a hole', () => { + expect(() => attachmentDisplayText(undefined as unknown as ComposerAttachment)).not.toThrow() + expect(attachmentDisplayText(undefined as unknown as ComposerAttachment)).toBeNull() + expect(attachmentDisplayText(null as unknown as ComposerAttachment)).toBeNull() + }) + + it('still resolves a normal file ref', () => { + expect(attachmentDisplayText(attachment({ kind: 'file', refText: '@file:src/a.ts' }))).toBe('@file:src/a.ts') + }) }) describe('coerceThinkingText', () => {