Fix Forgejo CI terminal test mock alias
The remaining Forgejo-only failure was a Next.js module-shape mismatch in the terminal client component. I switched the terminal screen to a namespace import for next/navigation so Forgejo no longer trips over Bun's named-export resolution for usePathname.
New Changes as of 2026-05-30 01:57 EDT
This update follows the earlier Bun PATH and test-harness fixes. Forgejo was still failing inside the terminal component itself, where Bun 1.3.14 treated the direct usePathname import as a named-export mismatch. The component now reads the hook from the namespace import instead.
Summary of changes
- Changed
apps/web/app/terminal.tsxto importnext/navigationas a namespace. - Replaced the three direct
usePathname()calls withnextNavigation.usePathname(). - Left the earlier test mocks in place so the suite still covers both the package specifier and Bun's resolved path.
Why this change was made
The previous test-level mocks were enough for local Bun, but Forgejo's Bun 1.3.14 runtime still errored on the named export lookup inside the client component. Changing the import shape removes that check instead of asking the test harness to paper over it.
Code diff
import * as nextNavigation from "next/navigation";
Related issues or PRs
islandflow-3l6
Summary
The remaining Forgejo failure was inside the terminal client component, not the install or typecheck stages. Using a namespace import keeps Bun from tripping over the usePathname named-export lookup in the runner.
Changes Made
- Updated
apps/web/app/terminal.tsxto readusePathnamethrough thenextNavigationnamespace. - Kept the earlier test-harness aliases intact, since they still cover the old runner behavior and make the tests resilient.
- Left the earlier Bun PATH and redirect-mock fixes intact, since they were already solving the other CI failure modes.
Context
The repository already had the Bun executable path fix and the routes mock alias fix in place. The remaining failure surfaced only in the full CI-shaped test run, where Bun 1.3.14 was stricter about the terminal client component's direct named import from next/navigation.
Important Implementation Details
- The terminal screen now reaches the pathname hook through the module namespace, which avoids Bun's stricter named-export check in CI.
- This stays narrowly scoped to the client component and does not change the route semantics or the visible UI behavior.
- The existing test mocks remain useful as guardrails, but the component import no longer depends on them to satisfy Bun's module loader.
Relevant Diff Snippets
Rendered with @pierre/diffs/ssr from the current working tree. It shows the terminal client component switching to a namespace import for next/navigation and updating the three pathname reads accordingly.
12345675369 unmodified lines53775378537953805381538253831844 unmodified lines72287229723072317232723372341863 unmodified lines9098909991009101910291039104"use client";import Link from "next/link";import { usePathname } from "next/navigation";import {createContext,memo,5369 unmodified lines};const useTerminalState = () => {const pathname = usePathname();const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);const [mode, setMode] = useState<TapeMode>("live");const [replaySource, setReplaySource] = useState<string | null>(null);1844 unmodified lines};export const FlowFilterPopover = ({ filters, onChange }: FlowFilterPopoverProps) => {const pathname = usePathname();const [open, setOpen] = useState(false);const rootRef = useRef<HTMLDivElement | null>(null);const activeCount = countActiveFlowFilterGroups(filters);1863 unmodified linesexport function TerminalAppShell({ children }: { children: ReactNode }) {const state = useTerminalState();const pathname = usePathname();const [drawerOpen, setDrawerOpen] = useState(false);const tickerFieldId = useId();const tickerHintId = useId();12345675369 unmodified lines53775378537953805381538253831844 unmodified lines72287229723072317232723372341863 unmodified lines9098909991009101910291039104"use client";import Link from "next/link";import * as nextNavigation from "next/navigation";import {createContext,memo,5369 unmodified lines};const useTerminalState = () => {const pathname = nextNavigation.usePathname();const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);const [mode, setMode] = useState<TapeMode>("live");const [replaySource, setReplaySource] = useState<string | null>(null);1844 unmodified lines};export const FlowFilterPopover = ({ filters, onChange }: FlowFilterPopoverProps) => {const pathname = nextNavigation.usePathname();const [open, setOpen] = useState(false);const rootRef = useRef<HTMLDivElement | null>(null);const activeCount = countActiveFlowFilterGroups(filters);1863 unmodified linesexport function TerminalAppShell({ children }: { children: ReactNode }) {const state = useTerminalState();const pathname = nextNavigation.usePathname();const [drawerOpen, setDrawerOpen] = useState(false);const tickerFieldId = useId();const tickerHintId = useId();
Expected Impact for End-Users
Forgejo should stop failing on the terminal screen's CI-only module resolution mismatch, which reduces false negative pipeline runs and makes it easier to trust the branch when the suite passes.
Validation
env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun test apps/web/app/terminal.test.tspassed: 74 tests, 0 failures.env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun testpassed: 250 tests, 0 failures.
Issues, Limitations, and Mitigations
This fix is intentionally narrow. If another CI-only Next.js import path shows up later, the same namespace-import pattern should be applied to the affected component or test file instead of broadening the mock surface globally. That keeps the failure signal honest and the test harness easy to reason about.
Follow-up Work
- Watch the next Forgejo run on this branch to confirm the namespace import clears the last failure.
- If another module-shape mismatch appears, fold the shared mock setup into a tiny helper rather than repeating the alias logic by hand.