Skip to content

Latest commit

 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Syrma

Drive and assert Zaniah GUI and TUI applications from Ruby tests.

Gem version CI Ruby 3.1+ MIT License

Website · Features · Installation · Quick Start · Snapshots · Documentation


Syrma locates rendered elements, sends input through Zaniah's real event path, waits for redraws, and compares the observable result. Tests can inspect text, element trees, terminal output, pixels, screenshots, menus, and tooltips without bypassing application input handling.

Syrma deterministic recording

Features

  • Drive GUI and TUI sessions with pointer, keyboard, clipboard, composition, file-drop, resize, and terminal input
  • Locate elements by text, test ID, actionability, or nested queries resolved against the latest frame
  • Assert semantic output first, with tree, terminal, pixel, and screenshot snapshots when needed
  • Keep timing deterministic with a session-scoped virtual clock and explicit frame settling
  • Test with Minitest assertions or RSpec matchers
  • Capture screenshots, hit regions, text runs, and recent events when a test fails
  • Record deterministic APNG/GIF frames and asciinema events from the virtual clock

Installation

Add Syrma and your test framework to the test group in your Gemfile:

group :test do
  gem "minitest", "~> 5.0"
  gem "syrma"
end

Then install the bundle and verify the test environment:

bundle install
bundle exec syrma doctor

Syrma requires Ruby 3.1 or later and supports Zaniah >= 0.7, < 1.0. RSpec users can replace Minitest with RSpec in the test group.

Demo recording

Install wezen alongside Syrma to export a virtual-clock recording:

result = session.record(fps: 12, seed: 42) do |recording|
  recording.cursor
  recording.caption("Open the file")
  recording.type_humanly("README.md")
  recording.pause(1.0)
end
result.write_apng("docs/media/overview.apng")

Recordings require a virtual clock and headless session for repeatable pixels. Use session.record_cast with a TUI session for asciinema v2 output.

Quick start

Keep window creation separate from the code that mounts the interface:

module Counter
  def self.mount(window)
    count = 0
    window.draw do
      Zaniah::Div.new
        .child(Zaniah::Text.new("Count: #{count}"))
        .child(Zaniah::Div.new.test_id("increment").on_click { count += 1 })
    end
  end
end

Minitest

require "syrma/minitest"

class CounterTest < Minitest::Test
  include Syrma::Minitest

  def setup
    zaniah_session(width: 320, height: 200) { |window| Counter.mount(window) }
  end

  def test_increment
    ui.test_id("increment").click
    assert_ui_text "Count: 1"
  end
end

RSpec

require "syrma/rspec"

RSpec.describe "Counter", type: :zaniah do
  before { zaniah_session(width: 320, height: 200) { |window| Counter.mount(window) } }

  it "increments the count" do
    ui.test_id("increment").click
    expect(ui).to have_ui_text("Count: 1")
  end
end

test_id is provided by Zaniah 0.2 and is safe to use in application code.

API at a glance

Task API
Locate find, all, test_id, text, button, nested find, nth, first, last
Pointer click, double_click, right_click, hover, drag, scroll
Keyboard/text press, type, paste, compose, commit
Window/input resize, close, drop_files, feed_terminal
Synchronize settle, wait_for, advance
Inspect tree, texts, at, accessibility, pixel, screenshot, terminal_lines, menu, tooltip
Assert Text, element, visibility, panels, decorations, background, pixel, tooltip, menu, tree/terminal/image snapshots

Locators are lazy: every operation resolves them against the latest rendered frame. Actions wait for visibility and an unobscured matching event handler, then send events through Window#input.

ui.accessibility(role: :button, label: "Save") searches Zaniah's semantic tree and returns [node, path] pairs. Unlike visual locators, semantic nodes need not correspond to a rendered element or have bounds; use ui.find for pointer actions.

Panel and decoration assertions use rendered test_id instrumentation, so application code does not need a Syrma or Canopus runtime dependency:

assert_panel_visible :problems
assert_panel_badge :problems, 3
assert_inline_overlay line: 10, text: ": String"
assert_gutter_marker line: 5, kind: :breakpoint
assert_line_highlight line: 12, kind: :debug_position

The corresponding IDs are syrma:panel:problems, syrma:panel:problems:badge, syrma:decoration:inline:10, syrma:decoration:gutter:5:breakpoint, and syrma:decoration:line:12:debug_position. Lines are zero-based. Instrument the element that was actually laid out and painted; assertions require positive visible bounds. RSpec provides the same names with have_ in place of assert_.

Snapshots and diagnostics

assert_tree_snapshot "sidebar"
assert_screenshot "saved", region: ui.test_id("panel"), mask: [ui.test_id("clock")]
assert_terminal_snapshot "main"

New goldens are created locally and rejected on CI. Set SYRMA_UPDATE_SNAPSHOTS=1 to update them. A failed UI test writes its screenshot, element tree, hit regions, text runs, recent events, and summary below tmp/syrma.

bundle exec syrma snapshots update
bundle exec syrma snapshots prune --dry-run
bundle exec syrma report

Determinism and performance

The default text renderer uses only Zaniah's bundled Abel font plus files passed in fonts:. Add a repository-owned font for non-Latin screenshot tests. text: :none is faster but does not draw glyphs.

On Ruby 4.0 arm64 macOS, the included 101-element benchmark measured event_frames: :gesture at 3.1 ms per click/check with text: :none and 7.9 ms with deterministic text; Syrma's tree build plus locator resolution was about 0.28 ms. Run bundle exec ruby -Ilib bench/click_bench.rb gesture on the target CI host for relevant numbers.

Documentation

  • Guide: application structure, sessions, CI, configuration, and troubleshooting
  • Recipes: interactions, screenshots, TUI sessions, and multiple windows
  • Changelog: release history

Development

bundle install
bundle exec rake
bundle exec rake demo
bundle exec ruby -Ilib:test script/test_gesture.rb
bundle exec rbs -I sig validate
gem build --strict syrma.gemspec

Contributing

Bug reports and pull requests are welcome on GitHub.

License

Syrma is available under the MIT License.

About

Drive and assert Zaniah GUI and TUI applications from Ruby tests.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages