feat: implement HeadlessRenderer for terminal-free UI testing #324

Merged
freemo merged 3 commits from feature/m2-headless-renderer into master 2026-03-16 19:56:16 +00:00
Owner

Summary

  • Implements HeadlessRenderer class as drop-in replacement for Ncurses/Composite stages in tests
  • Captures rendered output in memory for UI testing without terminal dependency
  • Provides test helpers for Cucumber step definitions via HeadlessRendererHelper module

Key Features

  • window_content(window_name) - returns content array for a window
  • last_frame - returns hash of all window contents from last frame
  • frame_count - tracks total frames rendered
  • clear_history - resets all captured state
  • last_bar_update(bar_name) - convenience method for status bar assertions
  • content_includes?(window, text) and bar_equals?(bar, value, max) test helpers
  • Auto-detection via HeadlessRenderer.headless_environment? when TERM unset or HEADLESS=1

Testing

  • 24 unit test scenarios (all passing)
  • 4 integration test scenarios (all passing)

Closes #309

## Summary - Implements `HeadlessRenderer` class as drop-in replacement for Ncurses/Composite stages in tests - Captures rendered output in memory for UI testing without terminal dependency - Provides test helpers for Cucumber step definitions via `HeadlessRendererHelper` module ## Key Features - `window_content(window_name)` - returns content array for a window - `last_frame` - returns hash of all window contents from last frame - `frame_count` - tracks total frames rendered - `clear_history` - resets all captured state - `last_bar_update(bar_name)` - convenience method for status bar assertions - `content_includes?(window, text)` and `bar_equals?(bar, value, max)` test helpers - Auto-detection via `HeadlessRenderer.headless_environment?` when TERM unset or HEADLESS=1 ## Testing - 24 unit test scenarios (all passing) - 4 integration test scenarios (all passing) ## Related Closes #309
Implements the RenderOp value object class for the UI render pipeline as
specified in issue #304. RenderOps are immutable value objects that describe
rendering instructions without specifying how they should be rendered.

Key features:
- Immutable value objects (frozen after creation) with type, window, and data
- Seven core RenderOp types: text_append, map_refresh, bar_update,
  prompt_update, window_clear, overlay_show, overlay_hide
- Factory methods for convenient construction: RenderOp.text_append(),
  RenderOp.bar_update(), etc.
- Data validation per type (required fields are enforced)
- Equality comparison support (== and hash for use as hash keys)
- to_h serialization for logging and debugging
- Comprehensive YARD documentation

Testing:
- 36 Cucumber scenarios covering all factory methods, immutability,
  validation, equality, and type constants
- 3 integration scenarios for realistic usage patterns

ISSUES CLOSED: #304
Implement a modular 6-stage render pipeline that processes RenderOps
into terminal output:

1. Router - Routes ops to target windows, groups by window
2. Formatter - Applies ANSI colors, word wrapping to window width
3. Layout - Solves layout constraints for window positions/sizes
4. Dirty Check - Compares against previous frame, marks clean windows
5. Ncurses - Writes dirty windows to ncurses buffers (wnoutrefresh)
6. Composite - Calls doupdate once per frame

Each stage is a separate class for testability. The pipeline handles
empty op batches gracefully (no-op frame) and supports terminal resize.

Includes unit tests covering all 6 stages and integration tests for
the full pipeline flow.
Implements HeadlessRenderer class that substitutes for NcursesStage and
CompositeStage in the render pipeline during tests, allowing UI testing
without requiring a terminal.

Key features:
- Captures rendered output as structured data per window
- window_content() returns content for a specific window
- last_frame() returns hash of all window contents
- frame_count tracks total frames rendered
- clear_history() resets all captured state
- last_bar_update() convenience method for status bar assertions
- content_includes?() and bar_equals?() test helpers
- Auto-detection via headless_environment?() when TERM unset or HEADLESS=1
- frame_history with configurable max_history limit

Also includes:
- HeadlessRendererHelper module for Cucumber step definitions
- Comprehensive unit tests (24 scenarios)
- Integration tests (4 scenarios)

Closes #309
freemo force-pushed feature/m2-headless-renderer from 1761d434db to 9ea7759011 2026-03-16 16:57:55 +00:00 Compare
freemo force-pushed feature/m2-headless-renderer from 9ea7759011 to 197653ce8a 2026-03-16 17:32:37 +00:00 Compare
freemo force-pushed feature/m2-headless-renderer from 197653ce8a to e34f38212e 2026-03-16 19:56:06 +00:00 Compare
freemo merged commit e34f38212e into master 2026-03-16 19:56:16 +00:00
Sign in to join this conversation.
No reviewers
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: aethyr/Aethyr#324
No description provided.