diff --git a/src/renderer/src/components/crash-report/CrashReportDialogSurface.overflow.test.tsx b/src/renderer/src/components/crash-report/CrashReportDialogSurface.overflow.test.tsx new file mode 100644 index 00000000000..a26ff7b8942 --- /dev/null +++ b/src/renderer/src/components/crash-report/CrashReportDialogSurface.overflow.test.tsx @@ -0,0 +1,84 @@ +// @vitest-environment happy-dom + +import type { ReactNode } from 'react' +import { cleanup, render, waitFor } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { CrashReportRecord } from '../../../../shared/crash-reporting' +import { CrashReportDialogSurface } from './CrashReportDialogSurface' + +const viewer = vi.fn(async () => null) + +vi.mock('./use-crash-report-copy', () => ({ + useCrashReportCopy: () => vi.fn(async () => {}) +})) + +vi.mock('@/components/ui/dialog', () => ({ + Dialog: ({ open, children }: { open: boolean; children?: ReactNode }) => + open ?
{children}
: null, + DialogContent: ({ className, children }: { className?: string; children?: ReactNode }) => ( +
+ {children} +
+ ), + DialogDescription: ({ children }: { children?: ReactNode }) =>

{children}

, + DialogFooter: ({ children }: { children?: ReactNode }) =>
{children}
, + DialogHeader: ({ children }: { children?: ReactNode }) =>
{children}
, + DialogTitle: ({ children }: { children?: ReactNode }) =>

{children}

+})) + +function crashReport(error: string): CrashReportRecord { + return { + id: 'crash-1', + createdAt: '2026-08-10T00:00:00.000Z', + status: 'pending', + source: 'renderer', + processType: 'renderer', + reason: 'crashed', + exitCode: 5, + appVersion: '1.0.0', + platform: 'darwin', + osRelease: 'test', + arch: 'arm64', + electronVersion: '41', + chromeVersion: '141', + details: { error } + } +} + +beforeEach(() => { + viewer.mockClear() + Object.defineProperty(window, 'api', { + configurable: true, + value: { gh: { viewer } } + }) +}) + +afterEach(() => cleanup()) + +describe('CrashReportDialogSurface overflow containment', () => { + it('keeps unbroken diagnostic output inside the dialog grid', async () => { + const unbrokenError = 'A'.repeat(1000) + const { container } = render( + {}} + onReportChange={() => {}} + /> + ) + await waitFor(() => expect(viewer).toHaveBeenCalledOnce()) + + const dialog = container.querySelector('[role="dialog"]') + const output = dialog?.querySelector('pre') + expect(output?.textContent).toContain(unbrokenError) + expect(output?.className).toContain('[overflow-wrap:anywhere]') + expect(output?.className).not.toContain('break-words') + + const gridChild = Array.from(dialog?.children ?? []).find((child) => + child.contains(output ?? null) + ) + expect(gridChild?.className).toContain('min-w-0') + expect(output?.parentElement?.className).toContain('min-w-0') + }) +}) diff --git a/src/renderer/src/components/crash-report/CrashReportDialogSurface.tsx b/src/renderer/src/components/crash-report/CrashReportDialogSurface.tsx index 0f263bf5310..841c6c4bb44 100644 --- a/src/renderer/src/components/crash-report/CrashReportDialogSurface.tsx +++ b/src/renderer/src/components/crash-report/CrashReportDialogSurface.tsx @@ -241,7 +241,7 @@ export function CrashReportDialogSurface({ {getDialogDescription(report)} -
+
{report ? ( <>
@@ -252,14 +252,14 @@ export function CrashReportDialogSurface({ {report.appVersion}
-
+
{translate( 'auto.components.crash.report.CrashReportDialog.6d3ebe216a', 'Diagnostic text' )}
-
+                
                   {diagnosticText}
                 
diff --git a/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.test.tsx b/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.test.tsx index f652a2ad1ca..31c17454a1d 100644 --- a/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.test.tsx +++ b/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.test.tsx @@ -73,8 +73,10 @@ vi.mock('@/components/ui/collapsible', () => ({ CollapsibleTrigger: ({ children }: { children?: ReactNode }) =>
{children}
, // Tagged so tests can prove WHERE content sits: this mock renders it whether // or not the disclosure is open, so presence in the DOM alone proves nothing. - CollapsibleContent: ({ children }: { children?: ReactNode }) => ( -
{children}
+ CollapsibleContent: ({ className, children }: { className?: string; children?: ReactNode }) => ( +
+ {children} +
) })) @@ -386,6 +388,34 @@ describe('SkillFreshnessUpdateDialog', () => { ) }) + it('contains unbroken failure output inside the dialog grid', async () => { + const unbrokenOutput = 'A'.repeat(1000) + const unbrokenMessage = 'B'.repeat(1000) + await renderDialog() + await openViaRequest() + await emitRun({ + state: 'error', + names: ['orca-cli'], + failedNames: ['orca-cli'], + finishedAt: 3, + output: unbrokenOutput, + message: unbrokenMessage + }) + + const output = container?.querySelector('pre') + expect(output?.textContent).toContain(unbrokenOutput) + expect(output?.className).toContain('[overflow-wrap:anywhere]') + expect(output?.className).not.toContain('break-words') + expect(output?.parentElement?.className).toContain('min-w-0') + expect(output?.closest('[data-collapsible-open]')?.className).toContain('min-w-0') + + const message = Array.from(container?.querySelectorAll('p') ?? []).find( + (candidate) => candidate.textContent === unbrokenMessage + ) + expect(message?.className).toContain('[overflow-wrap:anywhere]') + expect(message?.parentElement?.className).toContain('min-w-0') + }) + it('shows the up-to-date state once every installation is current', async () => { mocks.inventory = { schemaVersion: 1, @@ -673,6 +703,21 @@ describe('SkillFreshnessUpdateDialog', () => { expect(container?.querySelector('[data-skill-row="orca-cli"]')).toBeNull() expect(findButton('Update 1 skill')).toBeUndefined() }) + + it('wraps an unbroken scan error inside the dialog grid', async () => { + const unbrokenError = 'C'.repeat(1000) + mocks.inventory = null + mocks.error = unbrokenError + await renderDialog() + await openViaRequest() + + const error = Array.from(container?.querySelectorAll('p') ?? []).find( + (candidate) => candidate.textContent === unbrokenError + ) + expect(error?.className).toContain('min-w-0') + expect(error?.className).toContain('[overflow-wrap:anywhere]') + }) + it('shows incomplete plugin coverage without presenting a fabricated skill copy', async () => { mocks.inventory = { schemaVersion: 1, diff --git a/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.tsx b/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.tsx index 26edc09cfa4..262a34d6b0e 100644 --- a/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.tsx +++ b/src/renderer/src/components/skills/SkillFreshnessUpdateDialog.tsx @@ -41,7 +41,7 @@ function RunLog({ output }: { output: string }): React.JSX.Element | null { return null } return ( - +