tui-td 0.2.4 → 0.2.6

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.
@@ -37,6 +37,12 @@ module TUITD
37
37
  @plan[:before_all] = @plan[:before_all]&.map { |s| s.transform_keys(&:to_sym) }
38
38
  @plan[:after_all] = @plan[:after_all]&.map { |s| s.transform_keys(&:to_sym) }
39
39
  @on_step = on_step
40
+ rescue JSON::ParserError => e
41
+ raise Error, "Invalid JSON: #{e.message}"
42
+ @plan[:steps] = @plan[:steps].map { |s| s.transform_keys(&:to_sym) }
43
+ @plan[:before_all] = @plan[:before_all]&.map { |s| s.transform_keys(&:to_sym) }
44
+ @plan[:after_all] = @plan[:after_all]&.map { |s| s.transform_keys(&:to_sym) }
45
+ @on_step = on_step
40
46
  end
41
47
 
42
48
  def run
@@ -88,141 +94,98 @@ module TUITD
88
94
  Result.new(step: action, passed: true, message: "Found: #{value}")
89
95
 
90
96
  when "wait_for_stable"
91
- ensure_driver!(driver)
92
- driver.wait_for_stable
93
- Result.new(step: action, passed: true, message: "Stable")
94
-
95
- when "assert_text"
96
- ensure_driver!(driver)
97
- state = State.new(driver.state_data)
98
- if state.find_text(value.to_s).any?
99
- Result.new(step: action, passed: true, message: "Text found: #{value}")
100
- else
101
- Result.new(step: action, passed: false, message: "Text NOT found: #{value}")
102
- end
97
+ ensure_driver!(driver)
98
+ driver.wait_for_stable
99
+ Result.new(step: action, passed: true, message: "Stable")
103
100
 
104
- when "assert_not_text"
105
- ensure_driver!(driver)
106
- state = State.new(driver.state_data)
107
- if state.find_text(value.to_s).any?
108
- Result.new(step: action, passed: false, message: "Text found but should not be: #{value}")
109
- else
110
- Result.new(step: action, passed: true, message: "Text not found: #{value}")
111
- end
101
+ when "assert_text", "assert_not_text", "assert_regex"
102
+ check_text(driver, value, action)
112
103
 
113
- when "assert_regex"
114
- ensure_driver!(driver)
115
- state = State.new(driver.state_data)
116
- pattern = Regexp.new(value.to_s)
117
- if state.find_text(pattern).any?
118
- Result.new(step: action, passed: true, message: "Regex matched: #{value}")
119
- else
120
- Result.new(step: action, passed: false, message: "Regex did not match: #{value}")
121
- end
104
+ when "assert_fg"
105
+ check_color(driver, step, :fg)
122
106
 
123
- when "assert_fg"
124
- ensure_driver!(driver)
125
- row, col = coords(step)
126
- expected = step[:is] || step["is"]
127
- state = State.new(driver.state_data)
128
- actual = state.foreground_at(row, col)
129
- if actual == expected
130
- Result.new(step: action, passed: true, message: "FG at [#{row},#{col}] is #{expected}")
131
- else
132
- Result.new(step: action, passed: false, message: "FG at [#{row},#{col}] is #{actual}, expected #{expected}")
133
- end
134
-
135
- when "assert_bg"
136
- ensure_driver!(driver)
137
- row, col = coords(step)
138
- expected = step[:is] || step["is"]
139
- state = State.new(driver.state_data)
140
- actual = state.background_at(row, col)
141
- if actual == expected
142
- Result.new(step: action, passed: true, message: "BG at [#{row},#{col}] is #{expected}")
143
- else
144
- Result.new(step: action, passed: false, message: "BG at [#{row},#{col}] is #{actual}, expected #{expected}")
145
- end
107
+ when "assert_bg"
108
+ check_color(driver, step, :bg)
146
109
 
147
- when "assert_style"
148
- ensure_driver!(driver)
149
- row, col = coords(step)
150
- state = State.new(driver.state_data)
151
- actual = state.style_at(row, col)
152
- expected = {}
153
- expected[:bold] = step[:bold] unless step[:bold].nil?
154
- expected[:italic] = step[:italic] unless step[:italic].nil?
155
- expected[:underline] = step[:underline] unless step[:underline].nil?
156
- match = expected.all? { |k, v| actual[k] == v }
157
- if match
158
- Result.new(step: action, passed: true, message: "Style at [#{row},#{col}] matches #{expected}")
159
- else
160
- Result.new(step: action, passed: false, message: "Style at [#{row},#{col}] is #{actual}, expected #{expected}")
161
- end
110
+ when "assert_style"
111
+ ensure_driver!(driver)
112
+ row, col = coords(step)
113
+ state = State.new(driver.state_data)
114
+ actual = state.style_at(row, col)
115
+ expected = {}
116
+ expected[:bold] = step[:bold] unless step[:bold].nil?
117
+ expected[:italic] = step[:italic] unless step[:italic].nil?
118
+ expected[:underline] = step[:underline] unless step[:underline].nil?
119
+ match = expected.all? { |k, v| actual[k] == v }
120
+ if match
121
+ Result.new(step: action, passed: true, message: "Style at [#{row},#{col}] matches #{expected}")
122
+ else
123
+ Result.new(step: action, passed: false, message: "Style at [#{row},#{col}] is #{actual}, expected #{expected}")
124
+ end
125
+
126
+ when "screenshot"
127
+ ensure_driver!(driver)
128
+ path = value.is_a?(String) ? value : "/tmp/tui_td_#{Time.now.to_i}.png"
129
+ driver.screenshot(path)
130
+ Result.new(step: action, passed: true, message: "Saved: #{path}")
162
131
 
163
- when "screenshot"
164
- ensure_driver!(driver)
165
- path = value.is_a?(String) ? value : "/tmp/tui_td_#{Time.now.to_i}.png"
166
- driver.screenshot(path)
167
- Result.new(step: action, passed: true, message: "Saved: #{path}")
168
-
169
- when "html"
170
- ensure_driver!(driver)
171
- path = value.is_a?(String) ? value : "/tmp/tui_td_#{Time.now.to_i}.html"
172
- HtmlRenderer.new(driver.state_data).render(path)
173
- Result.new(step: action, passed: true, message: "Saved: #{path}")
174
-
175
- when "wait_for_exit"
176
- ensure_driver!(driver)
177
- driver.wait_for_exit
178
- status = driver.exitstatus
179
- Result.new(step: action, passed: true, message: "Exited with status #{status}")
180
-
181
- when "assert_exit"
182
- ensure_driver!(driver)
183
- expected = value.to_s.to_i
184
- actual = driver.exitstatus
185
- if actual == expected
186
- Result.new(step: action, passed: true, message: "Exit status #{expected} matches")
187
- else
188
- Result.new(step: action, passed: false, message: "Exit status #{actual}, expected #{expected}")
189
- end
132
+ when "html"
133
+ ensure_driver!(driver)
134
+ path = value.is_a?(String) ? value : "/tmp/tui_td_#{Time.now.to_i}.html"
135
+ HtmlRenderer.new(driver.state_data).render(path)
136
+ Result.new(step: action, passed: true, message: "Saved: #{path}")
190
137
 
191
- when "close"
192
- driver&.close
193
- driver = nil
194
- Result.new(step: action, passed: true, message: "Closed")
138
+ when "wait_for_exit"
139
+ ensure_driver!(driver)
140
+ driver.wait_for_exit
141
+ status = driver.exitstatus
142
+ Result.new(step: action, passed: true, message: "Exited with status #{status}")
195
143
 
196
- else
197
- Result.new(step: action, passed: false, message: "Unknown action: #{action}")
198
- end
144
+ when "assert_exit"
145
+ ensure_driver!(driver)
146
+ expected = value.to_s.to_i
147
+ actual = driver.exitstatus
148
+ if actual == expected
149
+ Result.new(step: action, passed: true, message: "Exit status #{expected} matches")
150
+ else
151
+ Result.new(step: action, passed: false, message: "Exit status #{actual}, expected #{expected}")
152
+ end
153
+
154
+ when "close"
155
+ driver&.close
156
+ driver = nil
157
+ Result.new(step: action, passed: true, message: "Closed")
199
158
 
200
- rescue StandardError => e
201
- r = Result.new(step: action, passed: false, message: "#{e.class}: #{e.message}")
202
- end
159
+ else
160
+ Result.new(step: action, passed: false, message: "Unknown action: #{action}")
161
+ end
203
162
 
204
- all_results << r
205
- all_passed &&= r.passed
163
+ rescue StandardError => e
164
+ r = Result.new(step: action, passed: false, message: "#{e.class}: #{e.message}")
165
+ end
206
166
 
207
- if @on_step
208
- state_data = nil
209
- begin
210
- state_data = driver.state_data if driver
211
- rescue StandardError
212
- # ignore — state retrieval is best-effort
167
+ all_results << r
168
+ all_passed &&= r.passed
169
+
170
+ if @on_step
171
+ state_data = nil
172
+ begin
173
+ state_data = driver.state_data if driver
174
+ rescue StandardError
175
+ # ignore — state retrieval is best-effort
176
+ end
177
+ @on_step.call(
178
+ index: all_results.size - 1,
179
+ total: total_steps,
180
+ action: action,
181
+ value: value,
182
+ result: r,
183
+ driver: driver,
184
+ state_data: state_data
185
+ )
213
186
  end
214
- @on_step.call(
215
- index: all_results.size - 1,
216
- total: total_steps,
217
- action: action,
218
- value: value,
219
- result: r,
220
- driver: driver,
221
- state_data: state_data
222
- )
223
187
  end
224
188
  end
225
- end
226
189
 
227
190
  driver&.close
228
191
 
@@ -241,10 +204,65 @@ module TUITD
241
204
 
242
205
  def coords(step)
243
206
  pos = step[:assert_fg] || step[:assert_bg] || step[:assert_style]
244
- pos = value if pos.nil? && (value = step.values.first).is_a?(Array)
245
207
  row = pos.is_a?(Array) ? pos[0] : (pos[:row] || pos["row"] || 0)
246
208
  col = pos.is_a?(Array) ? pos[1] : (pos[:col] || pos["col"] || 0)
247
209
  [row, col]
248
210
  end
211
+
212
+ def check_text(driver, value, action)
213
+ ensure_driver!(driver)
214
+ state = State.new(driver.state_data)
215
+ text = value.to_s
216
+
217
+ if action == "assert_regex"
218
+ begin
219
+ pattern = Regexp.new(text)
220
+ rescue RegexpError => e
221
+ return Result.new(step: action, passed: false, message: "Invalid regex: #{e.message}")
222
+ end
223
+ else
224
+ pattern = text
225
+ end
226
+
227
+ found = state.find_text(pattern).any?
228
+
229
+ case action
230
+ when "assert_text"
231
+ if found
232
+ Result.new(step: action, passed: true, message: "Text found: #{value}")
233
+ else
234
+ Result.new(step: action, passed: false, message: "Text NOT found: #{value}")
235
+ end
236
+ when "assert_not_text"
237
+ if found
238
+ Result.new(step: action, passed: false, message: "Text found but should not be: #{value}")
239
+ else
240
+ Result.new(step: action, passed: true, message: "Text not found: #{value}")
241
+ end
242
+ when "assert_regex"
243
+ if found
244
+ Result.new(step: action, passed: true, message: "Regex matched: #{value}")
245
+ else
246
+ Result.new(step: action, passed: false, message: "Regex did not match: #{value}")
247
+ end
248
+ else
249
+ Result.new(step: action, passed: false, message: "Unknown text check: #{action}")
250
+ end
251
+ end
252
+
253
+ def check_color(driver, step, property)
254
+ ensure_driver!(driver)
255
+ row, col = coords(step)
256
+ expected = step[:is] || step["is"]
257
+ state = State.new(driver.state_data)
258
+ label = property == :fg ? "FG" : "BG"
259
+ actual = property == :fg ? state.foreground_at(row, col) : state.background_at(row, col)
260
+
261
+ if actual == expected
262
+ Result.new(step: step.keys.first.to_s, passed: true, message: "#{label} at [#{row},#{col}] is #{expected}")
263
+ else
264
+ Result.new(step: step.keys.first.to_s, passed: false, message: "#{label} at [#{row},#{col}] is #{actual}, expected #{expected}")
265
+ end
266
+ end
249
267
  end
250
268
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TUITD
4
- VERSION = "0.2.4"
4
+ VERSION = "0.2.6"
5
5
  end
data/lib/tui_td.rb CHANGED
@@ -7,6 +7,7 @@ end
7
7
  require_relative "tui_td/version"
8
8
  require_relative "tui_td/driver"
9
9
  require_relative "tui_td/ansi_parser"
10
+ require_relative "tui_td/ansi_utils"
10
11
  require_relative "tui_td/state"
11
12
  require_relative "tui_td/screenshot"
12
13
  require_relative "tui_td/html_renderer"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tui-td
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Haluk Durmus
@@ -123,6 +123,7 @@ files:
123
123
  - bin/tui-td
124
124
  - lib/tui_td.rb
125
125
  - lib/tui_td/ansi_parser.rb
126
+ - lib/tui_td/ansi_utils.rb
126
127
  - lib/tui_td/cli.rb
127
128
  - lib/tui_td/driver.rb
128
129
  - lib/tui_td/html_renderer.rb