From 823cf96e7788cc9f69d65b97688c56df84e6b632 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 26 Mar 2026 15:10:48 -0400 Subject: [PATCH] test(ui): cover bounded code editor scroll mode --- .../ui/code-editor-usage-contract.test.ts | 42 +++++++++++++ .../unit/components/ui/code-editor.test.tsx | 60 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 ui/tests/unit/components/ui/code-editor-usage-contract.test.ts create mode 100644 ui/tests/unit/components/ui/code-editor.test.tsx diff --git a/ui/tests/unit/components/ui/code-editor-usage-contract.test.ts b/ui/tests/unit/components/ui/code-editor-usage-contract.test.ts new file mode 100644 index 00000000..7175b8d8 --- /dev/null +++ b/ui/tests/unit/components/ui/code-editor-usage-contract.test.ts @@ -0,0 +1,42 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { describe, expect, it } from 'vitest'; + +const boundedConsumers = [ + { + file: 'src/pages/cliproxy-ai-providers.tsx', + expectedCount: 2, + }, + { + file: 'src/components/cliproxy/provider-editor/raw-editor-section.tsx', + expectedCount: 1, + }, + { + file: 'src/components/profiles/editor/raw-editor-section.tsx', + expectedCount: 1, + }, + { + file: 'src/components/copilot/config-form/raw-editor-section.tsx', + expectedCount: 1, + }, + { + file: 'src/components/compatible-cli/raw-json-settings-editor-panel.tsx', + expectedCount: 1, + }, + { + file: 'src/components/shared/settings-dialog.tsx', + expectedCount: 1, + }, +] as const; + +describe('bounded CodeEditor consumers', () => { + it.each(boundedConsumers)('$file opts into fill-parent mode for every bounded editor', ({ + file, + expectedCount, + }) => { + const source = readFileSync(resolve(process.cwd(), file), 'utf8'); + const matches = source.match(/heightMode="fill-parent"/g) ?? []; + + expect(matches).toHaveLength(expectedCount); + }); +}); diff --git a/ui/tests/unit/components/ui/code-editor.test.tsx b/ui/tests/unit/components/ui/code-editor.test.tsx new file mode 100644 index 00000000..3a8d71cc --- /dev/null +++ b/ui/tests/unit/components/ui/code-editor.test.tsx @@ -0,0 +1,60 @@ +import { describe, expect, it, vi } from 'vitest'; +import { render, screen } from '@tests/setup/test-utils'; + +import { CodeEditor } from '@/components/shared/code-editor'; + +vi.mock('@/hooks/use-theme', () => ({ + useTheme: () => ({ isDark: false }), +})); + +describe('CodeEditor', () => { + it('creates an internal scroll viewport in fill-parent mode and keeps status outside it', () => { + const { container } = render( + + ); + + const viewport = container.querySelector('[data-slot="code-editor-viewport"]'); + + expect(viewport).toBeInTheDocument(); + expect(viewport).toHaveStyle({ height: '100%' }); + expect(viewport).not.toContainElement(screen.getByText('Valid JSON')); + }); + + it('keeps readonly status outside the scroll viewport for bounded editors', () => { + const { container } = render( + + ); + + const viewport = container.querySelector('[data-slot="code-editor-viewport"]'); + const textarea = container.querySelector('textarea'); + + expect(viewport).toHaveStyle({ height: 'calc(60vh - 120px)' }); + expect(textarea).toBeDisabled(); + expect(viewport).not.toContainElement(screen.getByText('(Read-only)')); + }); + + it('preserves content mode as the default layout contract', () => { + const { container } = render( + + ); + + expect(container.querySelector('[data-slot="code-editor-viewport"]')).not.toBeInTheDocument(); + }); +});