Turns the manual QA ask ("verify DiffView + CodeBlock horizontal-scroll
on-device with a populated diff") into two automated layers:
1. Unit (deterministic, runs in `npm test` now): extracted the shared
ScrollView props into src/lib/scroll-config.ts (WIDE_CONTENT_SCROLL_CONFIG)
so DiffView.tsx and CodeBlock.tsx spread the SAME plain object their tests
assert on — no react-native-renderer needed. Added a source-scan
regression test (wide-content-scroll.regression.test.ts) that fails if
either component loses its ScrollView wiring or reintroduces
numberOfLines truncation.
2. E2E (Maestro): .maestro/flows/diff-scroll.yaml opens a session with a
pre-seeded wide edit-diff tool call and a wide fenced code block, then
swipes each horizontal ScrollView left and asserts the off-screen marker
text becomes visible. mock-opencode-server.ts gained a --seed-diff mode
that serves this session via GET /session/:id/message (pre-existing
history), not SSE — issue #90 (a separate SSE-render bug) is being fixed
independently, and this flow must not depend on it landing first. Wired
the new flow + port 4100 into run-e2e-flows.sh and activation-e2e.yml.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { View, Text, StyleSheet, Platform, ScrollView } from "react-native"
|
||||
import { WIDE_CONTENT_SCROLL_CONFIG } from "../../lib/scroll-config"
|
||||
|
||||
const mono = Platform.OS === "ios" ? "Menlo" : "monospace"
|
||||
|
||||
@@ -76,7 +77,7 @@ export function DiffView({ before, after, isDark }: Props) {
|
||||
|
||||
return (
|
||||
<View style={[s.container, isDark && s.containerDark]}>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator>
|
||||
<ScrollView {...WIDE_CONTENT_SCROLL_CONFIG} testID="diff-view-scroll">
|
||||
<View>
|
||||
{lines.map((line, idx) => (
|
||||
<View
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react"
|
||||
import { View, Text, TouchableOpacity, StyleSheet, useColorScheme, Platform, ScrollView } from "react-native"
|
||||
import * as Clipboard from "expo-clipboard"
|
||||
import { WIDE_CONTENT_SCROLL_CONFIG } from "../../lib/scroll-config"
|
||||
|
||||
interface Props {
|
||||
code: string
|
||||
@@ -27,7 +28,7 @@ export function CodeBlock({ code, language }: Props) {
|
||||
<Text style={[styles.copyBtn, isDark && styles.copyBtnDark]}>{copied ? "Copied!" : "Copy"}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator contentContainerStyle={styles.codeScroll}>
|
||||
<ScrollView {...WIDE_CONTENT_SCROLL_CONFIG} testID="code-block-scroll" contentContainerStyle={styles.codeScroll}>
|
||||
<Text style={[styles.code, isDark && styles.codeDark]} selectable>
|
||||
{code}
|
||||
</Text>
|
||||
|
||||
45
src/components/wide-content-scroll.regression.test.ts
Normal file
45
src/components/wide-content-scroll.regression.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { test } from "node:test"
|
||||
import assert from "node:assert/strict"
|
||||
import { readFileSync } from "node:fs"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import path from "node:path"
|
||||
|
||||
// GitHub issue #21 ("QA: verify DiffView + CodeBlock horizontal-scroll
|
||||
// on-device with a populated diff"). The underlying fixes were:
|
||||
// - src/components/chat/DiffView.tsx: wrap diff lines in a horizontal
|
||||
// ScrollView (previously truncated with numberOfLines={1}).
|
||||
// - src/components/markdown/CodeBlock.tsx: wrap code in a horizontal
|
||||
// ScrollView (previously long lines wrapped/mangled).
|
||||
//
|
||||
// This repo's runtime is React Native, so these components can't be rendered
|
||||
// with node:test (no react-test-renderer / RN jest preset is set up here —
|
||||
// see package.json's "test" script, which only globs plain .test.ts files).
|
||||
// Instead of skipping component-level coverage, this test reads the actual
|
||||
// .tsx source and asserts on its structure: it's a plain-text/regex check,
|
||||
// but it directly targets the two markers that would prove a regression —
|
||||
// (a) the ScrollView wiring, (b) reintroducing line-truncation — so it can't
|
||||
// pass on a source file where the fix was reverted.
|
||||
|
||||
function readComponent(relativePath: string): string {
|
||||
const dir = path.dirname(fileURLToPath(import.meta.url))
|
||||
return readFileSync(path.join(dir, relativePath), "utf8")
|
||||
}
|
||||
|
||||
test("DiffView wraps diff lines in the shared horizontal-scroll ScrollView", () => {
|
||||
const src = readComponent("chat/DiffView.tsx")
|
||||
assert.match(src, /<ScrollView\s+\{\.\.\.WIDE_CONTENT_SCROLL_CONFIG\}/)
|
||||
assert.doesNotMatch(src, /numberOfLines/, "DiffView must not truncate diff line text with numberOfLines")
|
||||
})
|
||||
|
||||
test("CodeBlock wraps code in the shared horizontal-scroll ScrollView", () => {
|
||||
const src = readComponent("markdown/CodeBlock.tsx")
|
||||
assert.match(src, /<ScrollView\s+\{\.\.\.WIDE_CONTENT_SCROLL_CONFIG\}/)
|
||||
assert.doesNotMatch(src, /numberOfLines/, "CodeBlock must not truncate code text with numberOfLines")
|
||||
})
|
||||
|
||||
test("DiffView and CodeBlock both source the config from the shared, unit-tested module", () => {
|
||||
const diffView = readComponent("chat/DiffView.tsx")
|
||||
const codeBlock = readComponent("markdown/CodeBlock.tsx")
|
||||
assert.match(diffView, /from ["']\.\.\/\.\.\/lib\/scroll-config["']/)
|
||||
assert.match(codeBlock, /from ["']\.\.\/\.\.\/lib\/scroll-config["']/)
|
||||
})
|
||||
26
src/lib/scroll-config.test.ts
Normal file
26
src/lib/scroll-config.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { test } from "node:test"
|
||||
import assert from "node:assert/strict"
|
||||
import { WIDE_CONTENT_SCROLL_CONFIG } from "./scroll-config.ts"
|
||||
|
||||
// GitHub issue #21: DiffView + CodeBlock must render wide content in a
|
||||
// horizontally-scrollable container (not wrapped, not truncated). Both
|
||||
// components spread WIDE_CONTENT_SCROLL_CONFIG onto their ScrollView (see
|
||||
// src/components/chat/DiffView.tsx and src/components/markdown/CodeBlock.tsx)
|
||||
// so asserting on this object here is asserting on the actual runtime props,
|
||||
// not a parallel copy that can drift out of sync.
|
||||
|
||||
test("wide-content scroll config enables horizontal scrolling", () => {
|
||||
assert.equal(WIDE_CONTENT_SCROLL_CONFIG.horizontal, true)
|
||||
})
|
||||
|
||||
test("wide-content scroll config shows the horizontal scroll indicator", () => {
|
||||
// Regression guard: a container that scrolls but hides its indicator is
|
||||
// easy to mistake for content that simply doesn't overflow. Keep the
|
||||
// indicator visible so on-device QA (and screenshots) can tell scrollable
|
||||
// content apart from clipped/truncated content.
|
||||
assert.equal(WIDE_CONTENT_SCROLL_CONFIG.showsHorizontalScrollIndicator, true)
|
||||
})
|
||||
|
||||
test("wide-content scroll config has no unexpected keys", () => {
|
||||
assert.deepEqual(Object.keys(WIDE_CONTENT_SCROLL_CONFIG).sort(), ["horizontal", "showsHorizontalScrollIndicator"])
|
||||
})
|
||||
18
src/lib/scroll-config.ts
Normal file
18
src/lib/scroll-config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// Pure (no React Native imports) horizontal-scroll configuration shared by
|
||||
// src/components/chat/DiffView.tsx and src/components/markdown/CodeBlock.tsx.
|
||||
//
|
||||
// GitHub issue #21: wide diff lines and wide code-block lines must render in
|
||||
// a horizontally-scrollable container instead of being wrap-broken or
|
||||
// truncated with `numberOfLines`. Centralizing the actual runtime props in
|
||||
// one plain object lets that decision be unit-tested with node:test (no React
|
||||
// Native renderer needed) while both components spread the SAME object onto
|
||||
// their ScrollView, so the test and the real components can't drift apart.
|
||||
export interface HorizontalScrollConfig {
|
||||
horizontal: true
|
||||
showsHorizontalScrollIndicator: boolean
|
||||
}
|
||||
|
||||
export const WIDE_CONTENT_SCROLL_CONFIG: HorizontalScrollConfig = {
|
||||
horizontal: true,
|
||||
showsHorizontalScrollIndicator: true,
|
||||
}
|
||||
Reference in New Issue
Block a user