[GH-ISSUE #562] You can't test the app which uses useTerminalDimensions() #920

Closed
opened 2026-03-14 09:03:02 +03:00 by kerem · 1 comment
Owner

Originally created by @jcubic on GitHub (Jan 20, 2026).
Original GitHub issue: https://github.com/anomalyco/opentui/issues/562

Originally assigned to: @msmps on GitHub.

I was creating a new project with Opus 4.5 using OpenTUI skills.

I wanted to test an app that renders figlet and I needed useTerminalDimensions to make the text responsive.

LLM found a bug; here is a repro:

https://github.com/jcubic/open-tui-bug

It throws: "Renderer not found"

Here is the explanation of the bug and how to fix it. You probably know the project better, so you will know if this is correct. I'm not brave enough to create a PR using LLM.

OpenTUI Bug: testRender Cannot Test Components Using useTerminalDimensions

Summary

When using testRender from @opentui/react/test-utils to test components that call useTerminalDimensions(), the test fails with "Renderer not found" error, even though the same component works correctly in production.

Environment

  • @opentui/react: 0.1.74
  • @opentui/core: 0.1.74
  • bun: 1.3.0

Reproduction

git clone https://github.com/jcubic/open-tui-bug.git
cd open-tui-bug
bun install
bun test      # Fails with "Renderer not found"
bun run start # Works correctly

Expected Behavior

testRender should set up the renderer context so that hooks like useTerminalDimensions() can access the test renderer and return the configured dimensions (e.g., { width: 80, height: 24 }).

Actual Behavior

Components that call useTerminalDimensions() throw:

Error: Renderer not found.
    at useRenderer (node_modules/@opentui/react/index.js:161:20)
    at useTerminalDimensions (node_modules/@opentui/react/index.js:181:31)

Root Cause

The bug is caused by duplicate React contexts created during bundling.

The Problem

The test-utils.js bundle creates its own AppContext:

// In node_modules/@opentui/react/src/test-utils/test-utils.js
var AppContext = createContext({
  keyHandler: null,
  renderer: null
});

This is a separate object from the AppContext in the main index.js bundle:

// In node_modules/@opentui/react/index.js
var AppContext = createContext({
  keyHandler: null,
  renderer: null
});

Why This Causes the Bug

  1. When testRender is called, it creates a root and wraps the component with AppContext.Provider from test-utils.js:

    // test-utils.js createRoot function
    container = _render(
      React.createElement(AppContext.Provider, 
        { value: { keyHandler: renderer.keyInput, renderer } },
        React.createElement(ErrorBoundary, null, node)
      ),
      renderer.root
    );
    
  2. When the component calls useTerminalDimensions(), it internally calls useAppContext() which reads from the AppContext in index.js:

    // index.js
    const useAppContext = () => useContext(AppContext);
    
    const useRenderer = () => {
      const { renderer } = useAppContext();
      if (!renderer) {
        throw new Error("Renderer not found.");
      }
      return renderer;
    };
    
  3. Since AppContext in test-utils.js and AppContext in index.js are different objects (created by separate createContext() calls), the provider from test-utils.js does not satisfy the consumer in index.js.

  4. The component receives the default context value { renderer: null } instead of the test renderer, causing the "Renderer not found" error.

Visual Diagram

testRender (test-utils.js)
    │
    ├── Creates AppContext_A via createContext()
    ├── Provides { renderer: testRenderer } via AppContext_A.Provider
    │
    └── Renders <App />
            │
            └── App calls useTerminalDimensions() (from index.js)
                    │
                    ├── Reads from AppContext_B (different object!)
                    ├── Gets default value { renderer: null }
                    │
                    └── Throws "Renderer not found"

Suggested Fix

The test-utils bundle should import the AppContext from the main package instead of creating its own. This ensures both the provider (in testRender) and consumer (in hooks) use the same context object.

Option 1: Export AppContext from the main package and import it in test-utils:

// In test-utils.ts
import { AppContext } from "../components/app";

Option 2: Adjust the build configuration to prevent duplicating the context module when bundling test-utils.

Workaround

Until this is fixed, components can accept dimensions as props and only fall back to the hook when props aren't provided:

interface AppProps {
  width?: number;
  height?: number;
}

function App({ width: propWidth, height: propHeight }: AppProps) {
  // Skip the hook if props are provided (avoids the bug in tests)
  const hookDimensions = propWidth === undefined ? useTerminalDimensions() : { width: 0, height: 0 };
  const width = propWidth ?? hookDimensions.width;
  const height = propHeight ?? hookDimensions.height;
  
  return <text>Terminal: {width}x{height}</text>;
}

However, this is a hack and defeats the purpose of having a test renderer that simulates the real environment.

Originally created by @jcubic on GitHub (Jan 20, 2026). Original GitHub issue: https://github.com/anomalyco/opentui/issues/562 Originally assigned to: @msmps on GitHub. I was creating a new project with Opus 4.5 using [OpenTUI skills](https://github.com/msmps/opentui-skill/). I wanted to test an app that renders [figlet](https://github.com/patorjk/figlet.js) and I needed useTerminalDimensions to make the text responsive. LLM found a bug; here is a repro: https://github.com/jcubic/open-tui-bug It throws: "Renderer not found" Here is the explanation of the bug and how to fix it. You probably know the project better, so you will know if this is correct. I'm not brave enough to create a PR using LLM. # OpenTUI Bug: `testRender` Cannot Test Components Using `useTerminalDimensions` ## Summary When using `testRender` from `@opentui/react/test-utils` to test components that call `useTerminalDimensions()`, the test fails with "Renderer not found" error, even though the same component works correctly in production. ## Environment - @opentui/react: 0.1.74 - @opentui/core: 0.1.74 - bun: 1.3.0 ## Reproduction ```bash git clone https://github.com/jcubic/open-tui-bug.git cd open-tui-bug bun install bun test # Fails with "Renderer not found" bun run start # Works correctly ``` ## Expected Behavior `testRender` should set up the renderer context so that hooks like `useTerminalDimensions()` can access the test renderer and return the configured dimensions (e.g., `{ width: 80, height: 24 }`). ## Actual Behavior Components that call `useTerminalDimensions()` throw: ``` Error: Renderer not found. at useRenderer (node_modules/@opentui/react/index.js:161:20) at useTerminalDimensions (node_modules/@opentui/react/index.js:181:31) ``` ## Root Cause The bug is caused by **duplicate React contexts** created during bundling. ### The Problem The `test-utils.js` bundle creates its **own** `AppContext`: ```javascript // In node_modules/@opentui/react/src/test-utils/test-utils.js var AppContext = createContext({ keyHandler: null, renderer: null }); ``` This is a **separate object** from the `AppContext` in the main `index.js` bundle: ```javascript // In node_modules/@opentui/react/index.js var AppContext = createContext({ keyHandler: null, renderer: null }); ``` ### Why This Causes the Bug 1. When `testRender` is called, it creates a root and wraps the component with `AppContext.Provider` from **test-utils.js**: ```javascript // test-utils.js createRoot function container = _render( React.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React.createElement(ErrorBoundary, null, node) ), renderer.root ); ``` 2. When the component calls `useTerminalDimensions()`, it internally calls `useAppContext()` which reads from the `AppContext` in **index.js**: ```javascript // index.js const useAppContext = () => useContext(AppContext); const useRenderer = () => { const { renderer } = useAppContext(); if (!renderer) { throw new Error("Renderer not found."); } return renderer; }; ``` 3. Since `AppContext` in test-utils.js and `AppContext` in index.js are **different objects** (created by separate `createContext()` calls), the provider from test-utils.js does not satisfy the consumer in index.js. 4. The component receives the **default context value** `{ renderer: null }` instead of the test renderer, causing the "Renderer not found" error. ### Visual Diagram ``` testRender (test-utils.js) │ ├── Creates AppContext_A via createContext() ├── Provides { renderer: testRenderer } via AppContext_A.Provider │ └── Renders <App /> │ └── App calls useTerminalDimensions() (from index.js) │ ├── Reads from AppContext_B (different object!) ├── Gets default value { renderer: null } │ └── Throws "Renderer not found" ``` ## Suggested Fix The `test-utils` bundle should **import** the `AppContext` from the main package instead of creating its own. This ensures both the provider (in testRender) and consumer (in hooks) use the same context object. Option 1: Export `AppContext` from the main package and import it in test-utils: ```javascript // In test-utils.ts import { AppContext } from "../components/app"; ``` Option 2: Adjust the build configuration to prevent duplicating the context module when bundling test-utils. ## Workaround Until this is fixed, components can accept dimensions as props and only fall back to the hook when props aren't provided: ```tsx interface AppProps { width?: number; height?: number; } function App({ width: propWidth, height: propHeight }: AppProps) { // Skip the hook if props are provided (avoids the bug in tests) const hookDimensions = propWidth === undefined ? useTerminalDimensions() : { width: 0, height: 0 }; const width = propWidth ?? hookDimensions.width; const height = propHeight ?? hookDimensions.height; return <text>Terminal: {width}x{height}</text>; } ``` However, this is a hack and defeats the purpose of having a test renderer that simulates the real environment.
kerem 2026-03-14 09:03:02 +03:00
  • closed this issue
  • added the
    react
    label
Author
Owner

@msmps commented on GitHub (Jan 22, 2026):

Thanks for reporting this @jcubic ! I'll publish the fix later today 👍

<!-- gh-comment-id:3784050246 --> @msmps commented on GitHub (Jan 22, 2026): Thanks for reporting this @jcubic ! I'll publish the fix later today 👍
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#920
No description provided.