ttytest2 1.0.6 → 1.1.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: 986b298bb959d217e338bd7ce6c6ad5640ee656b99c44a85358bd02d408524b2
4
- data.tar.gz: '08f35657997cbad512a7251c895853249fa80342a7a338a7fd83c6dca7fc73f1'
3
+ metadata.gz: 47258d8644be543f645de9e34a17eca3a0e38c5fb764e4e72f4d78a63a888388
4
+ data.tar.gz: 4aa9221ee6fc4f5e8fda754b2cd3b9c8218d7b8cf1e7e8b67dfe2a36dd9360b8
5
5
  SHA512:
6
- metadata.gz: 1df9f3105198994fddb7105892bb0a1a659832bd5de3ac0dca99c87f584ccd11eaa9a9f008be97071f318445b77cde6007c161260616f1f9cd6f55a7193fa239
7
- data.tar.gz: 75ddbafbb51117a557e943bd63f7c6b3d908d0e5b3f58ac10f60dff5b9d11a17c29490974cd8a6f5d75c17b89a0320c004c4a2034120170346b089a661d19ee3
6
+ metadata.gz: f20f297c6b30fb549ade96a5a2644f11e9ad846f0e8950f5e119bcfc83a63228dcbe51b75e635fdcfeb0bd47b17122bf568e574305406582cf4a0e9978356137
7
+ data.tar.gz: 0ee8c938c0b66151ba208c1769bfd5ac65277d1c62dbc3ede3976926dfef964870a7119b6003601c4f2fb0c7a90cc2d6736e8cffc9df86de81d7f6efc5c2877a
data/README.md CHANGED
@@ -121,6 +121,8 @@ If you are reading this on github, the ruby docs accessible from [RubyDoc.Info](
121
121
 
122
122
  * `assert_row_regexp(row_number, regexp_str)`
123
123
 
124
+ * `assert_rows_each_match_regexp(row_start, row_end, regexp_str)`
125
+
124
126
  * `assert_cursor_position(x: x, y: y)`
125
127
 
126
128
  * `assert_cursor_visible`
@@ -131,6 +133,22 @@ If you are reading this on github, the ruby docs accessible from [RubyDoc.Info](
131
133
 
132
134
  * `assert_contents_at(row_start, row_end, expected_text)`
133
135
 
136
+ * `assert_contents_include(expected)`
137
+
138
+ * `assert_contents_empty`
139
+
140
+ * `assert_contents_match_regexp(regexp_str)`
141
+
142
+ * `assert_file_exists(file_path)`
143
+
144
+ * `assert_file_doesnt_exist(file_path)`
145
+
146
+ * `assert_file_contains(file_path, needle)`
147
+
148
+ * `assert_file_has_permissions(file_path, permissions)`
149
+
150
+ * `assert_file_has_line_count(file_path, expected_count)`
151
+
134
152
  ## Output
135
153
 
136
154
  You can send output to the terminal with the following calls.
@@ -262,10 +280,11 @@ Max wait time represents the amount of time in seconds that ttytest2 will keep r
262
280
  You can configure max wait time as shown below.
263
281
 
264
282
  ``` ruby
265
- @tty = TTYtest::new_terminal('')
266
- @tty.max_wait_time = 1 # sets the max wait time to 1 second
283
+ @tty = TTYtest::new_terminal('', max_wait_time: 1) # sets the max wait time to 1 second
267
284
 
268
285
  @tty.assert_row(0, 'echo Hello, world') # this assertion would fail after 1 second
286
+ @tty.max_wait_time = 3
287
+ @tty.assert_row(0, 'echo Hello, world') # this assertion would fail after 3 seconds
269
288
  ```
270
289
 
271
290
  ## Troubleshooting
@@ -2,7 +2,7 @@
2
2
 
3
3
  module TTYtest
4
4
  # Assertions for ttytest2.
5
- module Matchers
5
+ module Assertions
6
6
  # Asserts the contents of a single row match the value expected
7
7
  # @param [Integer] row_number the row (starting from 0) to test against
8
8
  # @param [String] expected the expected value of the row. Any trailing whitespace is ignored
@@ -88,6 +88,7 @@ module TTYtest
88
88
  raise MatchError,
89
89
  "expected row #{row_number} to start with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
90
90
  end
91
+ alias assert_line_starts_with assert_row_starts_with
91
92
 
92
93
  # Asserts the contents of a single row end with expected
93
94
  # @param [Integer] row_number the row (starting from 0) to test against
@@ -103,6 +104,7 @@ module TTYtest
103
104
  raise MatchError,
104
105
  "expected row #{row_number} to end with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
105
106
  end
107
+ alias assert_line_ends_with assert_row_ends_with
106
108
 
107
109
  # Asserts the contents of a single row match against the passed in regular expression
108
110
  # @param [Integer] row_number the row (starting from 0) to test against
@@ -118,6 +120,7 @@ module TTYtest
118
120
  raise MatchError,
119
121
  "expected row #{row_number} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual)}\nEntire screen:\n#{self}"
120
122
  end
123
+ alias assert_line_regexp assert_row_regexp
121
124
 
122
125
  # Asserts the contents of a multiple rows each match against the passed in regular expression
123
126
  # @param [Integer] row_start the row (starting from 0) to test against
@@ -136,6 +139,7 @@ module TTYtest
136
139
  "expected row #{index} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual_row)}\nEntire screen:\n#{self}"
137
140
  end
138
141
  end
142
+ alias assert_lines_each_match_regexp assert_rows_each_match_regexp
139
143
 
140
144
  # Asserts that the cursor is in the expected position
141
145
  # @param [Integer] x cursor x (row) position, starting from 0
@@ -151,6 +155,7 @@ module TTYtest
151
155
  "expected cursor to be at #{expected.inspect} but was at #{get_inspection(actual)}\nEntire screen:\n#{self}"
152
156
  end
153
157
 
158
+ # Asserts the cursor is currently visible
154
159
  # @raise [MatchError] if the cursor is hidden
155
160
  def assert_cursor_visible
156
161
  return if cursor_visible?
@@ -158,6 +163,7 @@ module TTYtest
158
163
  raise MatchError, "expected cursor to be visible was hidden\nEntire screen:\n#{self}"
159
164
  end
160
165
 
166
+ # Asserts the cursor is currently hidden
161
167
  # @raise [MatchError] if the cursor is visible
162
168
  def assert_cursor_hidden
163
169
  return if cursor_hidden?
@@ -165,29 +171,11 @@ module TTYtest
165
171
  raise MatchError, "expected cursor to be hidden was visible\nEntire screen:\n#{self}"
166
172
  end
167
173
 
168
- def matched(expected, actual)
169
- expected_rows = expected.split("\n")
170
- diff = []
171
- matched = true
172
- actual.each_with_index do |actual_row, index|
173
- expected_row = (expected_rows[index] || '').rstrip
174
- if actual_row != expected_row
175
- diff << "-#{expected_row}"
176
- diff << "+#{actual_row}"
177
- matched = false
178
- else
179
- diff << " #{actual_row}".rstrip
180
- end
181
- end
182
-
183
- [matched, diff]
184
- end
185
-
186
174
  # Asserts the full contents of the terminal
187
175
  # @param [String] expected the full expected contents of the terminal. Trailing whitespace on each line is ignored
188
176
  # @raise [MatchError] if the terminal doesn't match the expected content
189
177
  def assert_contents(expected)
190
- matched, diff = matched(expected, rows)
178
+ matched, diff = get_diff(expected, rows)
191
179
 
192
180
  return if matched
193
181
 
@@ -204,7 +192,7 @@ module TTYtest
204
192
  validate(row_end)
205
193
  row_end += 1 if row_end.zero?
206
194
 
207
- matched, diff = matched(expected, rows.slice(row_start, row_end))
195
+ matched, diff = get_diff(expected, rows.slice(row_start, row_end))
208
196
 
209
197
  return if matched
210
198
 
@@ -214,11 +202,56 @@ module TTYtest
214
202
  alias assert_matches_at assert_contents_at
215
203
  alias assert_rows assert_contents_at
216
204
 
205
+ # Asserts the contents of the screen include the passed in string
206
+ # @param [String] expected the string value expected to be found in the screen contents
207
+ # @raise [MatchError] if the screen does not contain the expected value
208
+ def assert_contents_include(expected)
209
+ found = false
210
+ rows.each do |row|
211
+ found = true if row.include?(expected)
212
+ end
213
+ return if found
214
+
215
+ raise MatchError,
216
+ "Expected screen contents to include #{expected}, but it was not found.\nEntire screen:\n#{self}"
217
+ end
218
+ alias assert_screen_includes assert_contents_include
219
+
220
+ # Asserts the contents of the screen are empty
221
+ # @raise [MatchError] if the screen is not empty
222
+ def assert_contents_empty
223
+ return if rows.all? { |s| s.to_s.empty? }
224
+
225
+ raise MatchError,
226
+ "Expected screen to be empty, but found content.\nEntire screen:\n#{self}"
227
+ end
228
+ alias assert_screen_empty assert_contents_empty
229
+
230
+ # Asserts the contents of the screen as a single string match the passed in regular expression
231
+ # @param [String] regexp_str the regular expression as a string that will be used to match with
232
+ # @raise [MatchError] if the screen as a string doesn't match against the regular expression
233
+ def assert_contents_match_regexp(regexp_str)
234
+ regexp = Regexp.new(regexp_str)
235
+ screen = capture.to_s
236
+
237
+ return if !screen.nil? && screen.match?(regexp)
238
+
239
+ raise MatchError,
240
+ "Expected screen contents to match regexp #{regexp_str} but they did not\nEntire screen:\n#{self}"
241
+ end
242
+ alias assert_screen_matches_regexp assert_contents_match_regexp
243
+
244
+ # Asserts the specified file exists
245
+ # @param [String] file_path the path to the file
246
+ # @raise [MatchError] if the file is not found or is a directory/symlink
217
247
  def assert_file_exists(file_path)
218
248
  raise file_not_found_error(file_path) unless File.exist?(file_path)
219
249
  raise file_is_dir_error(file_path) unless File.file?(file_path)
220
250
  end
221
251
 
252
+ # Asserts the specified file does not exists
253
+ # @param [String] file_path the path to the file
254
+ # @raise [MatchError] if the file is found or is a directory/symlink
222
255
  def assert_file_doesnt_exist(file_path)
223
256
  return unless File.exist?(file_path) || File.file?(file_path)
224
257
 
@@ -226,6 +259,10 @@ module TTYtest
226
259
  "File with path #{file_path} was found or is a directory when it was asserted it did not exist.\nEntire screen:\n#{self}"
227
260
  end
228
261
 
262
+ # Asserts the specified file contains the passed in string value
263
+ # @param [String] file_path the path to the file
264
+ # @param [String] needle the value to search for in the file
265
+ # @raise [MatchError] if the file does not contain value in variable needle
229
266
  def assert_file_contains(file_path, needle)
230
267
  raise file_not_found_error(file_path) unless File.exist?(file_path)
231
268
  raise file_is_dir_error(file_path) unless File.file?(file_path)
@@ -242,7 +279,12 @@ module TTYtest
242
279
  raise MatchError,
243
280
  "File with path #{file_path} did not contain #{needle}.\nEntire screen:\n#{self}"
244
281
  end
282
+ alias assert_file_like assert_file_contains
245
283
 
284
+ # Asserts the specified file has the permissions specified
285
+ # @param [String] file_path the path to the file
286
+ # @param [String] permissions the expected permissions of the file (in form '644' or '775')
287
+ # @raise [MatchError] if the file has different permissions than specified
246
288
  def assert_file_has_permissions(file_path, permissions)
247
289
  raise file_not_found_error(file_path) unless File.exist?(file_path)
248
290
  raise file_is_dir_error(file_path) unless File.file?(file_path)
@@ -255,6 +297,21 @@ module TTYtest
255
297
  "File had permissions #{perms_octal}, not #{permissions} as expected.\n Entire screen:\n#{self}"
256
298
  end
257
299
 
300
+ # Asserts the specified file has line count specified
301
+ # @param [String] file_path the path to the file
302
+ # @param [String] expected_count the expected line count of the file
303
+ # @raise [MatchError] if the file has a different line count than specified
304
+ def assert_file_has_line_count(file_path, expected_count)
305
+ raise file_not_found_error(file_path) unless File.exist?(file_path)
306
+ raise file_is_dir_error(file_path) unless File.file?(file_path)
307
+
308
+ actual_count = File.foreach(file_path).count
309
+ return if actual_count == expected_count
310
+
311
+ raise MatchError,
312
+ "File had #{actual_count} lines, not #{expected_count} lines as expected.\nEntire screen:\n#{self}"
313
+ end
314
+
258
315
  METHODS = public_instance_methods
259
316
 
260
317
  private
@@ -284,6 +341,24 @@ module TTYtest
284
341
  Entire screen:\n#{self}"
285
342
  end
286
343
 
344
+ def get_diff(expected, actual)
345
+ expected_rows = expected.split("\n")
346
+ diff = []
347
+ matched = true
348
+ actual.each_with_index do |actual_row, index|
349
+ expected_row = (expected_rows[index] || '').rstrip
350
+ if actual_row != expected_row
351
+ diff << "-#{expected_row}"
352
+ diff << "+#{actual_row}"
353
+ matched = false
354
+ else
355
+ diff << " #{actual_row}".rstrip
356
+ end
357
+ end
358
+
359
+ [matched, diff]
360
+ end
361
+
287
362
  def file_not_found_error(file_path)
288
363
  raise MatchError,
289
364
  "File with path #{file_path} was not found when asserted it did exist.\nEntire screen:\n#{self}"
@@ -7,7 +7,7 @@ module TTYtest
7
7
  # @attr_reader [Integer] cursor_x The cursor's column (starting at 0) in the captured terminal.
8
8
  # @attr_reader [Integer] cursor_y The cursor's row (starting at 0) in the captured terminal.
9
9
  class Capture
10
- include TTYtest::Matchers
10
+ include TTYtest::Assertions
11
11
 
12
12
  attr_reader :cursor_x, :cursor_y, :width, :height
13
13
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require 'ttytest/matchers'
4
+ require 'ttytest/assertions'
5
5
  require 'ttytest/capture'
6
6
 
7
7
  module TTYtest
@@ -193,7 +193,7 @@ module TTYtest
193
193
  :cursor_x, :cursor_y,
194
194
  :cursor_visible?, :cursor_hidden?
195
195
 
196
- Matchers::METHODS.each do |matcher_name|
196
+ Assertions::METHODS.each do |matcher_name|
197
197
  define_method matcher_name do |*args, **kwargs|
198
198
  synchronize do
199
199
  capture.public_send(matcher_name, *args, **kwargs)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTYtest
4
- VERSION = '1.0.6'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/ttytest.rb CHANGED
@@ -25,7 +25,22 @@ module TTYtest
25
25
  # @!method new_sh_terminal(width: 80, height: 24)
26
26
  # Create a new terminal using '/bin/sh' with ability to set width and height.
27
27
  # Useful for Unixes.
28
- def_delegators :driver, :new_terminal, :new_default_sh_terminal, :new_sh_terminal
28
+ def_delegators :driver
29
+
30
+ def new_terminal(cmd, width: 80, height: 24, max_wait_time: 2)
31
+ @max_wait_time = max_wait_time
32
+ driver.new_terminal(cmd, width: width, height: height)
33
+ end
34
+
35
+ def new_default_sh_terminal(max_wait_time: 2)
36
+ @max_wait_time = max_wait_time
37
+ driver.new_default_sh_terminal
38
+ end
39
+
40
+ def new_sh_terminal(width: 80, height: 24, max_wait_time: 2)
41
+ @max_wait_time = max_wait_time
42
+ driver.new_sh_terminal(width: width, height: height)
43
+ end
29
44
  end
30
45
 
31
46
  # The error type raised when an assertion fails.
data/notes.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  to push new version to github
2
- git tag v1.0.6
2
+ git tag v1.1.0
3
3
  git push origin --tags
4
4
 
5
5
  to push new version to rubygems.org
6
6
  gem build ttytest2.gemspec
7
- gem push ttytest2-1.0.6.gem
7
+ gem push ttytest2-1.1.0.gem
data/todo.txt CHANGED
@@ -1,2 +1,6 @@
1
1
  assert_exit_code(0) # For success
2
2
  assert_exit_code(1) # For expected failure
3
+ assert_variable_equals
4
+ assert_prompt
5
+ assert_prompt_after("command failed")
6
+ assert_style(row, col, style)
data/ttytest2.gemspec CHANGED
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['Alex Eski']
11
11
  spec.email = ['alexeski@gmail.com']
12
12
 
13
- spec.summary = 'ttytest2 is an integration test framework for interactive console (tty) applications'
14
- spec.description = 'ttytest2 runs shell/cli applications inside of tmux and allows you to make assertions on what the output should be'
13
+ spec.summary = 'ttytest2 is for integration/acceptance testing your shells/CLIs/REPLs.'
14
+ spec.description = 'ttytest2 is an integration test framework for interactive console (tty) applications'
15
15
  spec.homepage = 'https://github.com/a-eski/ttytest2'
16
16
  spec.license = 'MIT'
17
17
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ttytest2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Eski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-28 00:00:00.000000000 Z
11
+ date: 2025-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,8 +80,8 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.9'
83
- description: ttytest2 runs shell/cli applications inside of tmux and allows you to
84
- make assertions on what the output should be
83
+ description: ttytest2 is an integration test framework for interactive console (tty)
84
+ applications
85
85
  email:
86
86
  - alexeski@gmail.com
87
87
  executables: []
@@ -96,9 +96,9 @@ files:
96
96
  - Rakefile
97
97
  - examples/integration_tests.rb
98
98
  - lib/ttytest.rb
99
+ - lib/ttytest/assertions.rb
99
100
  - lib/ttytest/capture.rb
100
101
  - lib/ttytest/constants.rb
101
- - lib/ttytest/matchers.rb
102
102
  - lib/ttytest/terminal.rb
103
103
  - lib/ttytest/tmux/driver.rb
104
104
  - lib/ttytest/tmux/session.rb
@@ -129,5 +129,5 @@ requirements: []
129
129
  rubygems_version: 3.4.20
130
130
  signing_key:
131
131
  specification_version: 4
132
- summary: ttytest2 is an integration test framework for interactive console (tty) applications
132
+ summary: ttytest2 is for integration/acceptance testing your shells/CLIs/REPLs.
133
133
  test_files: []