syrma 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9ef547e116ba3d928630699b7ae95646b0604108499429bc978bf31faa502bf
4
- data.tar.gz: 01de9448ef8c33a18af96280ba15a993cf08be62292408de956aa8be78572a8c
3
+ metadata.gz: 0125f382e153f0c3485107cf3cbe4719733da7b250f3e2e7aeac507c7f3beab7
4
+ data.tar.gz: 53f0fd7a4f089b5a8b42e3ea36a2e0edef6c9aec39c6b1c8d006cd1850dcfa8a
5
5
  SHA512:
6
- metadata.gz: 15af45c54231f18c6c10b7db2354a644bc206c584c515a169672ad562db43753f5753ececea2c829b04d418b83181c6689c0571be115dea91109425b74bc0342
7
- data.tar.gz: 15c60ea282171fb9e17427769fd81b5510218590afb0126d51349bf16a73c5b5a58b9657fe0a27e3d1140a3fcfc3e70270c013c02445dbedc71ce0cbb93b2640
6
+ metadata.gz: 2a124116d448b9c7d85cacb595e57a155b11cbf1f1f4a7ec2b97904d9b99aff2f00c35751c435fd2563a9efaa31ab8a53c098f192954c270fe95ad719d789a66
7
+ data.tar.gz: 299ed17ddf94e4c81db7f80517e3402640a55af4768386e5e0ac40668f40d015127cc738e79f0e1a9bb5da639ad8a32ce9b59e191a413b5dc700ae55ce770772
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.2.0 - 2026-09-15
6
+
7
+ - Add Minitest assertions and RSpec matchers for instrumented panels and editor decorations.
8
+ - Support Zaniah 0.2 through 0.5 in headless instrumentation.
9
+
3
10
  ## 0.1.0 - 2026-09-11
4
11
 
5
12
  - Initial release.
data/README.md CHANGED
@@ -1,10 +1,41 @@
1
- # Syrma
1
+ <h1 align="center">Syrma</h1>
2
2
 
3
- Syrma drives [Zaniah](https://github.com/noxdea/zaniah) GUI and TUI applications from Ruby tests. It locates rendered elements, sends input through Zaniah's real event path, waits for redraws, and compares text, trees, terminal output, pixels, and screenshots.
3
+ <p align="center">
4
+ <strong>Drive and assert <a href="https://github.com/noxdea/zaniah">Zaniah</a> GUI and TUI applications from Ruby tests.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://rubygems.org/gems/syrma"><img src="https://img.shields.io/gem/v/syrma.svg" alt="Gem version"></a>
9
+ <a href="https://github.com/noxdea/syrma/actions/workflows/main.yml"><img src="https://github.com/noxdea/syrma/actions/workflows/main.yml/badge.svg" alt="CI"></a>
10
+ <a href="https://www.ruby-lang.org/"><img src="https://img.shields.io/badge/ruby-3.1%2B-CC342D.svg" alt="Ruby 3.1+"></a>
11
+ <a href="LICENSE.txt"><img src="https://img.shields.io/github/license/noxdea/syrma.svg" alt="MIT License"></a>
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://noxdea.github.io/syrma/">Website</a> ·
16
+ <a href="#features">Features</a> ·
17
+ <a href="#installation">Installation</a> ·
18
+ <a href="#quick-start">Quick Start</a> ·
19
+ <a href="#snapshots-and-diagnostics">Snapshots</a> ·
20
+ <a href="#documentation">Documentation</a>
21
+ </p>
22
+
23
+ ---
24
+
25
+ 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.
26
+
27
+ ## Features
28
+
29
+ - Drive GUI and TUI sessions with pointer, keyboard, clipboard, composition, file-drop, resize, and terminal input
30
+ - Locate elements by text, test ID, actionability, or nested queries resolved against the latest frame
31
+ - Assert semantic output first, with tree, terminal, pixel, and screenshot snapshots when needed
32
+ - Keep timing deterministic with a session-scoped virtual clock and explicit frame settling
33
+ - Test with Minitest assertions or RSpec matchers
34
+ - Capture screenshots, hit regions, text runs, and recent events when a test fails
4
35
 
5
36
  ## Installation
6
37
 
7
- Add Syrma to the test group in your `Gemfile`:
38
+ Add Syrma and your test framework to the test group in your `Gemfile`:
8
39
 
9
40
  ```ruby
10
41
  group :test do
@@ -13,11 +44,18 @@ group :test do
13
44
  end
14
45
  ```
15
46
 
16
- Then run `bundle install` and `bundle exec syrma doctor`.
47
+ Then install the bundle and verify the test environment:
17
48
 
18
- ## Minimal test
49
+ ```sh
50
+ bundle install
51
+ bundle exec syrma doctor
52
+ ```
53
+
54
+ Syrma requires Ruby 3.1 or later and supports Zaniah `>= 0.2, < 0.6`. RSpec users can replace Minitest with RSpec in the test group.
55
+
56
+ ## Quick start
19
57
 
20
- Keep window creation separate from the code that mounts your UI:
58
+ Keep window creation separate from the code that mounts the interface:
21
59
 
22
60
  ```ruby
23
61
  module Counter
@@ -32,6 +70,8 @@ module Counter
32
70
  end
33
71
  ```
34
72
 
73
+ ### Minitest
74
+
35
75
  ```ruby
36
76
  require "syrma/minitest"
37
77
 
@@ -43,12 +83,27 @@ class CounterTest < Minitest::Test
43
83
  end
44
84
 
45
85
  def test_increment
46
- ui.find(test_id: "increment").click
86
+ ui.test_id("increment").click
47
87
  assert_ui_text "Count: 1"
48
88
  end
49
89
  end
50
90
  ```
51
91
 
92
+ ### RSpec
93
+
94
+ ```ruby
95
+ require "syrma/rspec"
96
+
97
+ RSpec.describe "Counter", type: :zaniah do
98
+ before { zaniah_session(width: 320, height: 200) { |window| Counter.mount(window) } }
99
+
100
+ it "increments the count" do
101
+ ui.test_id("increment").click
102
+ expect(ui).to have_ui_text("Count: 1")
103
+ end
104
+ end
105
+ ```
106
+
52
107
  `test_id` is provided by Zaniah 0.2 and is safe to use in application code.
53
108
 
54
109
  ## API at a glance
@@ -61,10 +116,22 @@ end
61
116
  | Window/input | `resize`, `close`, `drop_files`, `feed_terminal` |
62
117
  | Synchronize | `settle`, `wait_for`, `advance` |
63
118
  | Inspect | `tree`, `texts`, `at`, `pixel`, `screenshot`, `terminal_lines`, `menu`, `tooltip` |
64
- | Assert | text, element, visibility, clickability, background, pixel, tooltip, menu, tree/terminal/image snapshots |
119
+ | Assert | Text, element, visibility, panels, decorations, background, pixel, tooltip, menu, tree/terminal/image snapshots |
65
120
 
66
121
  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`.
67
122
 
123
+ Panel and decoration assertions use rendered `test_id` instrumentation, so application code does not need a Syrma or Canopus runtime dependency:
124
+
125
+ ```ruby
126
+ assert_panel_visible :problems
127
+ assert_panel_badge :problems, 3
128
+ assert_inline_overlay line: 10, text: ": String"
129
+ assert_gutter_marker line: 5, kind: :breakpoint
130
+ assert_line_highlight line: 12, kind: :debug_position
131
+ ```
132
+
133
+ 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_`.
134
+
68
135
  ## Snapshots and diagnostics
69
136
 
70
137
  ```ruby
@@ -81,24 +148,31 @@ bundle exec syrma snapshots prune --dry-run
81
148
  bundle exec syrma report
82
149
  ```
83
150
 
84
- ## Determinism and speed
151
+ ## Determinism and performance
85
152
 
86
153
  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.
87
154
 
88
155
  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.
89
156
 
90
- See [the guide](docs/guide.md) and [recipes](docs/recipes.md) for the full workflow.
157
+ ## Documentation
158
+
159
+ - [Guide](docs/guide.md): application structure, sessions, CI, configuration, and troubleshooting
160
+ - [Recipes](docs/recipes.md): interactions, screenshots, TUI sessions, and multiple windows
161
+ - [Changelog](CHANGELOG.md): release history
91
162
 
92
163
  ## Development
93
164
 
94
165
  ```sh
166
+ bundle install
95
167
  bundle exec rake
96
168
  bundle exec ruby -Ilib:test script/test_gesture.rb
97
169
  bundle exec rbs -I sig validate
98
170
  gem build --strict syrma.gemspec
99
171
  ```
100
172
 
101
- Syrma supports Ruby 3.1+ and Zaniah `~> 0.2.0`. Virtual time is scoped to each session.
173
+ ## Contributing
174
+
175
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/noxdea/syrma).
102
176
 
103
177
  ## License
104
178
 
@@ -6,7 +6,7 @@ module Syrma
6
6
 
7
7
  module WindowCapture
8
8
  attr_reader :testing_root, :testing_clear, :testing_frame
9
- attr_accessor :testing_lazy_raster
9
+ attr_accessor :testing_lazy_raster, :testing_reduce_motion
10
10
 
11
11
  def on_frame(&callback)
12
12
  @testing_on_frame = callback
@@ -29,8 +29,15 @@ module Syrma
29
29
  @testing_recorder = recorder
30
30
  end
31
31
 
32
- def render(element, present: true, **options)
33
- super(element, present: present && !testing_lazy_raster, **options)
32
+ def render(element, present: true, **options) = super(element, present: present && !testing_lazy_raster, **options)
33
+ end
34
+
35
+ module FrameContextCapture
36
+ def theme
37
+ current = super
38
+ return current unless window.respond_to?(:testing_reduce_motion) && window.testing_reduce_motion
39
+
40
+ current.with(motion: current.motion.with(reduced: true))
34
41
  end
35
42
  end
36
43
 
@@ -56,6 +63,7 @@ module Syrma
56
63
  return if @installed
57
64
 
58
65
  Zaniah::Platform.singleton_class.prepend(BackendOverride)
66
+ Zaniah::FrameContext.prepend(FrameContextCapture) if Zaniah::FrameContext.method_defined?(:theme)
59
67
  @installed = true
60
68
  end
61
69
 
@@ -69,9 +77,10 @@ module Syrma
69
77
  Thread.current[:syrma_window_capture] = previous
70
78
  end
71
79
 
72
- def capture(window, lazy_raster:)
80
+ def capture(window, lazy_raster:, reduce_motion:)
73
81
  window.extend(WindowCapture) unless window.is_a?(WindowCapture)
74
82
  window.testing_lazy_raster = lazy_raster
83
+ window.testing_reduce_motion = reduce_motion
75
84
  window.on_frame
76
85
  window
77
86
  end
@@ -5,9 +5,9 @@ module Syrma
5
5
  module_function
6
6
 
7
7
  def style(element) = element.instance_variable_get(:@style) || {}
8
- def background(element) = element.instance_variable_get(:@background)
9
- def border_color(element) = element.instance_variable_get(:@border_color)
10
- def radius(element) = element.instance_variable_get(:@radius)
8
+ def background(element) = paint_value(element, :background, :@background)
9
+ def border_color(element) = paint_value(element, :border_color, :@border_color)
10
+ def radius(element) = paint_value(element, :corner_radii, :@radius)
11
11
  def tooltip(element) = element.instance_variable_get(:@tooltip)
12
12
  def context_menu(element) = element.instance_variable_get(:@context_menu)
13
13
  def key(element) = element.instance_variable_get(:@key)
@@ -22,5 +22,11 @@ module Syrma
22
22
  end
23
23
 
24
24
  def executor_idle?(executor) = executor.instance_variable_get(:@foreground).empty?
25
+
26
+ def paint_value(element, property, legacy)
27
+ resolved = element.respond_to?(:resolved_style) ? element.resolved_style : nil
28
+ resolved&.[](property) || element.instance_variable_get(legacy) || style(element)[property]
29
+ end
30
+ private_class_method :paint_value
25
31
  end
26
32
  end
@@ -94,6 +94,35 @@ module Syrma
94
94
  end
95
95
  end
96
96
 
97
+ def assert_panel_visible(id, session: ui, timeout: session.timeout)
98
+ test_id = UIAssertions.panel_id(id)
99
+ assert_ui_instrumentation(session, timeout, test_id, "panel #{id.inspect} to be visible", "#{UIAssertions::PREFIX}:panel:")
100
+ end
101
+
102
+ def assert_panel_badge(id, value, session: ui, timeout: session.timeout)
103
+ test_id = UIAssertions.panel_id(id, badge: true)
104
+ assert_ui_instrumentation(session, timeout, test_id, "panel #{id.inspect} badge to be #{value.inspect}",
105
+ "#{UIAssertions::PREFIX}:panel:", text: value)
106
+ end
107
+
108
+ def assert_inline_overlay(line:, text:, session: ui, timeout: session.timeout)
109
+ test_id = UIAssertions.decoration_id(:inline, line)
110
+ assert_ui_instrumentation(session, timeout, test_id, "inline overlay on line #{line} with text #{text.inspect}",
111
+ "#{UIAssertions::PREFIX}:decoration:inline:", text: text)
112
+ end
113
+
114
+ def assert_gutter_marker(line:, kind:, session: ui, timeout: session.timeout)
115
+ test_id = UIAssertions.decoration_id(:gutter, line, kind)
116
+ assert_ui_instrumentation(session, timeout, test_id, "#{kind.inspect} gutter marker on line #{line}",
117
+ "#{UIAssertions::PREFIX}:decoration:gutter:")
118
+ end
119
+
120
+ def assert_line_highlight(line:, kind:, session: ui, timeout: session.timeout)
121
+ test_id = UIAssertions.decoration_id(:line, line, kind)
122
+ assert_ui_instrumentation(session, timeout, test_id, "#{kind.inspect} line highlight on line #{line}",
123
+ "#{UIAssertions::PREFIX}:decoration:line:")
124
+ end
125
+
97
126
  def assert_screenshot(name, session: ui, **options)
98
127
  warn "syrma: text: :none does not render text in images" if session.text_mode == :none
99
128
  store = Snapshots::Store.new
@@ -154,6 +183,13 @@ module Syrma
154
183
  flunk error.message
155
184
  end
156
185
 
186
+ def assert_ui_instrumentation(session, timeout, test_id, expectation, prefix, text: UIAssertions::ANY_TEXT)
187
+ ui_eventually(session, timeout,
188
+ -> { "Expected #{expectation}; observed #{UIAssertions.describe(session, prefix)}" }) do
189
+ UIAssertions.visible?(session, test_id, text: text)
190
+ end
191
+ end
192
+
157
193
  def snapshot_path(store, name, extension)
158
194
  test_file = self.class.instance_method(self.name.to_sym).source_location&.first || "unknown"
159
195
  store.path(test_file: test_file, test_class: self.class.name, name: name, ext: extension)
data/lib/syrma/rspec.rb CHANGED
@@ -102,6 +102,33 @@ module Syrma
102
102
  failure_message { |session| "Expected menu items #{labels.inspect} (current: #{session.menu.items.inspect})" }
103
103
  end
104
104
 
105
+ {
106
+ have_panel_visible: ->(id) { [UIAssertions.panel_id(id), UIAssertions::ANY_TEXT, "panel #{id.inspect} to be visible", "#{UIAssertions::PREFIX}:panel:"] },
107
+ have_panel_badge: ->(id, value) { [UIAssertions.panel_id(id, badge: true), value, "panel #{id.inspect} badge to be #{value.inspect}", "#{UIAssertions::PREFIX}:panel:"] },
108
+ have_inline_overlay: ->(line:, text:) { [UIAssertions.decoration_id(:inline, line), text, "inline overlay on line #{line} with text #{text.inspect}", "#{UIAssertions::PREFIX}:decoration:inline:"] },
109
+ have_gutter_marker: ->(line:, kind:) { [UIAssertions.decoration_id(:gutter, line, kind), UIAssertions::ANY_TEXT, "#{kind.inspect} gutter marker on line #{line}", "#{UIAssertions::PREFIX}:decoration:gutter:"] },
110
+ have_line_highlight: ->(line:, kind:) { [UIAssertions.decoration_id(:line, line, kind), UIAssertions::ANY_TEXT, "#{kind.inspect} line highlight on line #{line}", "#{UIAssertions::PREFIX}:decoration:line:"] }
111
+ }.each do |name, query|
112
+ ::RSpec::Matchers.define name do |*args, **keywords|
113
+ match do |session|
114
+ @test_id, @text, @expectation, @prefix = query.call(*args, **keywords)
115
+ @session = session
116
+ RSpec.eventually(session) { UIAssertions.visible?(session, @test_id, text: @text) }
117
+ end
118
+ match_when_negated do |session|
119
+ @test_id, @text, @expectation, @prefix = query.call(*args, **keywords)
120
+ @session = session
121
+ RSpec.eventually(session) { !UIAssertions.visible?(session, @test_id, text: @text) }
122
+ end
123
+ failure_message do
124
+ "Expected #{@expectation}; observed #{UIAssertions.describe(@session, @prefix)}"
125
+ end
126
+ failure_message_when_negated do
127
+ "Expected #{@expectation} not to match; observed #{UIAssertions.describe(@session, @prefix)}"
128
+ end
129
+ end
130
+ end
131
+
105
132
  ::RSpec::Matchers.define :match_screenshot do |name, **options|
106
133
  match do |session|
107
134
  store = Snapshots::Store.new
data/lib/syrma/session.rb CHANGED
@@ -68,6 +68,7 @@ module Syrma
68
68
 
69
69
  def driver = @drivers[@current]
70
70
  def session = self
71
+ def virtual_clock? = @clock.is_a?(Clock)
71
72
  def method_missing(name, ...) = driver.respond_to?(name) ? driver.public_send(name, ...) : super
72
73
  def respond_to_missing?(name, include_all = false) = driver.respond_to?(name, include_all) || super
73
74
 
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Syrma
4
+ module UIAssertions
5
+ PREFIX = "syrma"
6
+ ANY_TEXT = Object.new.freeze
7
+
8
+ module_function
9
+
10
+ def panel_id(id, badge: false) = [PREFIX, "panel", component(id, "panel id"), badge ? "badge" : nil].compact.join(":")
11
+
12
+ def decoration_id(kind, line, type = nil)
13
+ raise ArgumentError, "line must be a nonnegative integer" unless line.is_a?(Integer) && line >= 0
14
+
15
+ kind = component(kind, "decoration kind")
16
+ type = component(type, "decoration type") unless type.nil?
17
+ [PREFIX, "decoration", kind, line, type].compact.join(":")
18
+ end
19
+
20
+ def visible?(session, test_id, text: ANY_TEXT)
21
+ nodes(session, test_id).any? do |node|
22
+ node.visible? && (text.equal?(ANY_TEXT) || text_match?(node.content_text, text))
23
+ end
24
+ end
25
+
26
+ def describe(session, prefix)
27
+ found = session.tree.nodes.select { |node| node.test_id&.start_with?(prefix) }
28
+ return "none" if found.empty?
29
+
30
+ found.first(10).map do |node|
31
+ "#{node.test_id}=#{node.content_text.inspect} (#{node.visible? ? 'visible' : 'hidden'})"
32
+ end.join(", ")
33
+ end
34
+
35
+ def nodes(session, test_id) = session.tree.nodes.select { |node| node.test_id == test_id }
36
+
37
+ def text_match?(actual, expected)
38
+ expected.is_a?(Regexp) ? expected.match?(actual) : actual == expected.to_s
39
+ end
40
+
41
+ def component(value, label)
42
+ valid = value.is_a?(String) || value.is_a?(Symbol)
43
+ value = value.to_s
44
+ valid &&= !value.empty? && !value.include?(":") && !value.match?(/[\x00-\x1f\x7f]/)
45
+ raise ArgumentError, "#{label} must be a nonempty string or symbol without colons or control characters" unless valid
46
+
47
+ value
48
+ end
49
+ private_class_method :component
50
+ end
51
+ end
data/lib/syrma/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Syrma
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -9,7 +9,7 @@ module Syrma
9
9
  def initialize(window, session)
10
10
  @window = window
11
11
  @session = session
12
- Instrumentation.capture(window, lazy_raster: session.raster == :lazy)
12
+ Instrumentation.capture(window, lazy_raster: session.raster == :lazy, reduce_motion: session.virtual_clock?)
13
13
  @builder = SnapshotBuilder.new
14
14
  end
15
15
 
@@ -162,9 +162,19 @@ module Syrma
162
162
  def scroll_until(list, found:, step: 100, max: 50)
163
163
  target = find(**found)
164
164
  max.times do
165
- return target if target.exists? && target.resolve_all.any?(&:visible?)
166
-
167
- scroll(list, dy: step)
165
+ candidates = target.resolve_all
166
+ return target if candidates.any?(&:visible?)
167
+
168
+ container = resolve_node(list)
169
+ candidate = candidates.first
170
+ dy = if candidate && candidate.bounds.y >= container.visible_bounds.y + container.visible_bounds.height
171
+ candidate.bounds.y - container.visible_bounds.y - container.visible_bounds.height + 1
172
+ elsif candidate && candidate.bounds.y + candidate.bounds.height <= container.visible_bounds.y
173
+ candidate.bounds.y + candidate.bounds.height - container.visible_bounds.y - 1
174
+ else
175
+ step
176
+ end
177
+ scroll(list, dy: dy)
168
178
  end
169
179
  raise ElementNotFound.new(target, tree)
170
180
  end
data/lib/syrma.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "syrma/clock"
10
10
  require_relative "syrma/text_systems"
11
11
  require_relative "syrma/snapshot"
12
12
  require_relative "syrma/query"
13
+ require_relative "syrma/ui_assertions"
13
14
  require_relative "syrma/event_log"
14
15
  require_relative "syrma/event_codec"
15
16
  require_relative "syrma/recorder"
data/sig/syrma.rbs CHANGED
@@ -145,6 +145,7 @@ module Syrma
145
145
  def window: (?title: String?, ?index: Integer?) -> untyped
146
146
  def driver: () -> WindowDriver
147
147
  def session: () -> Session
148
+ def virtual_clock?: () -> bool
148
149
  def settle: () -> Session
149
150
  def advance: (Numeric seconds) -> Session
150
151
  def wait_for: (?untyped message, ?timeout: Numeric) { () -> untyped } -> untyped
@@ -258,6 +259,11 @@ module Syrma
258
259
  def assert_pixel: (Integer x, Integer y, untyped color, **untyped) -> bool
259
260
  def assert_tooltip: (String | Regexp expected, **untyped) -> bool
260
261
  def assert_menu_items: (Array[String] labels, **untyped) -> bool
262
+ def assert_panel_visible: (String | Symbol id, **untyped) -> bool
263
+ def assert_panel_badge: (String | Symbol id, untyped value, **untyped) -> bool
264
+ def assert_inline_overlay: (line: Integer, text: String | Regexp, **untyped) -> bool
265
+ def assert_gutter_marker: (line: Integer, kind: String | Symbol, **untyped) -> bool
266
+ def assert_line_highlight: (line: Integer, kind: String | Symbol, **untyped) -> bool
261
267
  def assert_screenshot: (String name, **untyped) -> bool
262
268
  def assert_tree_snapshot: (String name, **untyped) -> bool
263
269
  def assert_terminal_snapshot: (String name, **untyped) -> bool
metadata CHANGED
@@ -1,28 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syrma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-09-15 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: zaniah
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
- - - "~>"
17
+ - - ">="
17
18
  - !ruby/object:Gem::Version
18
- version: 0.2.0
19
+ version: '0.2'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.6'
19
23
  type: :runtime
20
24
  prerelease: false
21
25
  version_requirements: !ruby/object:Gem::Requirement
22
26
  requirements:
23
- - - "~>"
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0.2'
30
+ - - "<"
24
31
  - !ruby/object:Gem::Version
25
- version: 0.2.0
32
+ version: '0.6'
26
33
  description: 'Drive Zaniah windows headlessly: locate elements, send real input events,
27
34
  and assert text, layout, pixels, and snapshots.'
28
35
  email:
@@ -61,18 +68,20 @@ files:
61
68
  - lib/syrma/snapshots/terminal_format.rb
62
69
  - lib/syrma/snapshots/tree_format.rb
63
70
  - lib/syrma/text_systems.rb
71
+ - lib/syrma/ui_assertions.rb
64
72
  - lib/syrma/version.rb
65
73
  - lib/syrma/visual/comparator.rb
66
74
  - lib/syrma/visual/image.rb
67
75
  - lib/syrma/window_driver.rb
68
76
  - sig/syrma.rbs
69
- homepage: https://github.com/ydah/syrma
77
+ homepage: https://github.com/noxdea/syrma
70
78
  licenses:
71
79
  - MIT
72
80
  metadata:
73
- source_code_uri: https://github.com/ydah/syrma
74
- changelog_uri: https://github.com/ydah/syrma/blob/main/CHANGELOG.md
81
+ source_code_uri: https://github.com/noxdea/syrma
82
+ changelog_uri: https://github.com/noxdea/syrma/blob/main/CHANGELOG.md
75
83
  rubygems_mfa_required: 'true'
84
+ post_install_message:
76
85
  rdoc_options: []
77
86
  require_paths:
78
87
  - lib
@@ -87,7 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
96
  - !ruby/object:Gem::Version
88
97
  version: '0'
89
98
  requirements: []
90
- rubygems_version: 4.0.19
99
+ rubygems_version: 3.4.19
100
+ signing_key:
91
101
  specification_version: 4
92
102
  summary: UI testing toolkit for applications built with Zaniah
93
103
  test_files: []