ttytest2 1.1.0 → 1.3.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: 47258d8644be543f645de9e34a17eca3a0e38c5fb764e4e72f4d78a63a888388
4
- data.tar.gz: 4aa9221ee6fc4f5e8fda754b2cd3b9c8218d7b8cf1e7e8b67dfe2a36dd9360b8
3
+ metadata.gz: 15e01162a84bf95dce1f5694dfc57daa77decd5c7136af567c469d4e2148daa6
4
+ data.tar.gz: e29d3aae969a70b6bb0ba1003a87b4da7e2b819c0e80595f6a9604b11c52f7a8
5
5
  SHA512:
6
- metadata.gz: f20f297c6b30fb549ade96a5a2644f11e9ad846f0e8950f5e119bcfc83a63228dcbe51b75e635fdcfeb0bd47b17122bf568e574305406582cf4a0e9978356137
7
- data.tar.gz: 0ee8c938c0b66151ba208c1769bfd5ac65277d1c62dbc3ede3976926dfef964870a7119b6003601c4f2fb0c7a90cc2d6736e8cffc9df86de81d7f6efc5c2877a
6
+ metadata.gz: dd61cb3a8e293c928e1d3b5fcb3637273bd6a27a55ecae96ea737b1f0b6959df44c223aae366131175411320d0c185d3b3a339a128c519e43a4d75dbe8990147
7
+ data.tar.gz: 38a1a8dfa89b91b8209fb033fe2a7b3af161f4bfdc87dd07bc2f30154c1f925cf48b1c4697eef25a11773e1a65eb0422edd6811ffcef56559167709e85e63180
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # ttytest2
2
2
 
3
+ <a href="https://rubygems.org/gems/ttytest2">
4
+ <img src="images/ttytest2.png" alt="ttytest2 logo" style="width:70%; height:auto;">
5
+ </a>
6
+
3
7
  ttytest2 is a user acceptance test framework for CLI & shell applications.
4
8
 
5
9
  ttytest2 is a fork and a drop-in replacement for [ttytest](https://github.com/jhawthorn/ttytest).
@@ -31,6 +35,7 @@ The assertions will wait a specified amount of time (configurable, default 2 sec
31
35
 
32
36
  ## Usage
33
37
 
38
+ * Download the [gem](https://rubygems.org/gems/ttytest2) here.
34
39
  * More documentation available at [ttytest2 docs](https://www.rubydoc.info/gems/ttytest2).
35
40
  * There are more examples in the examples folder.
36
41
 
@@ -191,8 +196,10 @@ Helper functions to make sending output easier! They use the methods above under
191
196
 
192
197
  * `send_newline` # simulate hitting enter, equivalent to @tty.send_keys(%(\n))
193
198
  * `send_newlines(number_of_times)` # equivalent to calling send_newline number_of_times
194
- * `send_enter` # alias for send_newline
195
- * `send_enters(number_of_times)` # alias for send_newlines
199
+
200
+ * `send_return` # simulate hitting enter, equivalent to @tty.send_keys(%(\r))
201
+ * `send_returns(number_of_times)` # equivalent to calling send_return number_of_times
202
+
196
203
 
197
204
  * `send_backspace` # simulate hitting backspace, equivalent to @tty.send_keys(TTYtest::BACKSPACE)
198
205
  * `send_backspaces(number_of_times)` # equivalent to calling send_backspace number_of_times
@@ -220,6 +227,9 @@ Helper functions to make sending output easier! They use the methods above under
220
227
  * `send_escape`
221
228
  * `send_escapes(number_of_times)`
222
229
 
230
+ * `send_tab`
231
+ * `send_tab(number_of_times)`
232
+
223
233
  ### F keys?
224
234
 
225
235
  Send F keys like F1, F2, etc. as shown below:
@@ -253,7 +263,7 @@ There are some commonly used keys available as constants to make interacting wit
253
263
  TTYtest::SHIFT_ENTER # \v
254
264
  TTYtest::FORM_FEED # \f or New Page NP
255
265
  TTYtest::CTRLL
256
- TTYtest::CARRIAGE_RETURN # \r
266
+ TTYtest::RETURN # \r
257
267
  TTYtest::CTRLU
258
268
  TTYtest::CTRLW
259
269
  TTYtest::ESCAPE # 27 decimal or ^[ or /033
@@ -273,7 +283,9 @@ There are some commonly used keys available as constants to make interacting wit
273
283
 
274
284
  ## Configurables
275
285
 
276
- Currently the only configuration for ttytest2 is max wait time.
286
+ There are 2 main configurations for ttytest2: max_wait_time, and use_return_for_newline.
287
+
288
+ ### Max wait time
277
289
 
278
290
  Max wait time represents the amount of time in seconds that ttytest2 will keep retrying an assertion before failing.
279
291
 
@@ -287,6 +299,24 @@ You can configure max wait time as shown below.
287
299
  @tty.assert_row(0, 'echo Hello, world') # this assertion would fail after 3 seconds
288
300
  ```
289
301
 
302
+ ### Use return for newline
303
+
304
+ Use return for newline tells ttytest2 to use return ('//r') instead of newline ('//n') for methods like send_line.
305
+
306
+ Some line readers may interpreter return and newline differently, so this can be useful in those cases.
307
+
308
+ You can still send newline via send_newline when this is enabled.
309
+
310
+ You can also send return by itself with send_return.
311
+
312
+ ``` ruby
313
+ @tty = TTYtest::new_terminal('', use_return_for_newline: true) # specified to use return in place of newline
314
+
315
+ @tty.send_line('hello, world!') # will have return sent after 'hello, world!'
316
+ @tty.use_return_for_newline = false
317
+ @tty.assert_row(0, 'echo Hello, world') # will have newline sent after 'echo Hello, world'
318
+ ```
319
+
290
320
  ## Troubleshooting
291
321
 
292
322
  You can use the method rows to get all rows of the terminal as an array, of use the method capture to get the contents of the terminal window. This can be useful when troubleshooting.
@@ -320,6 +350,8 @@ If you are using ttyest2 to test your CLI, using sh is easier than bash because
320
350
 
321
351
  If you are using ttytest2 to test your shell, using assertions like `assert_row_like`, `assert_row_starts_with`, and `assert_row_ends_with` are going to be extremely helpful, especially if trying to test your shell in different environments or using a docker container.
322
352
 
353
+ Most line readers use '\n' for newline, but some may interpret it differently and expect '\r'.
354
+
323
355
  ## Docker
324
356
 
325
357
  Easy to use from Docker. Add this to your dockerfile to get started.
Binary file
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ # Column Assertions for ttytest2.
5
+ module ColumnAssertions
6
+ # Asserts the contents of a single column match the value expected
7
+ # @param [Integer] col_number the column (starting from 0) to test against
8
+ # @param [String] expected the expected value of the column. Any trailing whitespace is ignored
9
+ # @raise [MatchError] if the column doesn't match exactly
10
+ def assert_column(col_number, expected)
11
+ validate(col_number)
12
+ expected = expected.rstrip
13
+
14
+ rows.each_with_index do |row, i|
15
+ break if row.nil?
16
+
17
+ next if row[col_number] == expected[i]
18
+
19
+ raise MatchError,
20
+ "expected column #{col_number} to be #{expected.inspect}\n
21
+ Entire screen:\n#{self}"
22
+ end
23
+ end
24
+
25
+ # Asserts the specified column is empty
26
+ # @param [Integer] col_number the column (starting from 0) to test against
27
+ # @raise [MatchError] if the column isn't empty
28
+ def assert_column_is_empty(col_number)
29
+ validate(col_number)
30
+
31
+ rows.each do |row|
32
+ break if row.nil?
33
+
34
+ next if row == '' || row.length < col_number + 1 || row[col_number] == ' '
35
+
36
+ raise MatchError,
37
+ "expected column #{col_number} to be empty\nEntire screen:\n#{self}"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ # Cursor Assertions for ttytest2.
5
+ module CursorAssertions
6
+ # Asserts that the cursor is in the expected position
7
+ # @param [Integer] x cursor x (row) position, starting from 0
8
+ # @param [Integer] y cursor y (column) position, starting from 0
9
+ # @raise [MatchError] if the cursor position doesn't match
10
+ def assert_cursor_position(x, y)
11
+ expected = [x, y]
12
+ actual = [cursor_x, cursor_y]
13
+
14
+ return if actual == expected
15
+
16
+ raise MatchError,
17
+ "expected cursor to be at #{expected.inspect} but was at #{get_inspection(actual)}\nEntire screen:\n#{self}"
18
+ end
19
+
20
+ # Asserts the cursor is currently visible
21
+ # @raise [MatchError] if the cursor is hidden
22
+ def assert_cursor_visible
23
+ return if cursor_visible?
24
+
25
+ raise MatchError, "expected cursor to be visible was hidden\nEntire screen:\n#{self}"
26
+ end
27
+
28
+ # Asserts the cursor is currently hidden
29
+ # @raise [MatchError] if the cursor is visible
30
+ def assert_cursor_hidden
31
+ return if cursor_hidden?
32
+
33
+ raise MatchError, "expected cursor to be hidden was visible\nEntire screen:\n#{self}"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ # File Assertions for ttytest2.
5
+ module FileAssertions
6
+ # Asserts the specified file exists
7
+ # @param [String] file_path the path to the file
8
+ # @raise [MatchError] if the file is not found or is a directory/symlink
9
+ def assert_file_exists(file_path)
10
+ raise file_not_found_error(file_path) unless File.exist?(file_path)
11
+ raise file_is_dir_error(file_path) unless File.file?(file_path)
12
+ end
13
+
14
+ # Asserts the specified file does not exists
15
+ # @param [String] file_path the path to the file
16
+ # @raise [MatchError] if the file is found or is a directory/symlink
17
+ def assert_file_doesnt_exist(file_path)
18
+ return unless File.exist?(file_path) || File.file?(file_path)
19
+
20
+ raise MatchError,
21
+ "File with path #{file_path} was found or is a directory when it was asserted it did not exist.\nEntire screen:\n#{self}"
22
+ end
23
+
24
+ # Asserts the specified file contains the passed in string value
25
+ # @param [String] file_path the path to the file
26
+ # @param [String] needle the value to search for in the file
27
+ # @raise [MatchError] if the file does not contain value in variable needle
28
+ def assert_file_contains(file_path, needle)
29
+ raise file_not_found_error(file_path) unless File.exist?(file_path)
30
+ raise file_is_dir_error(file_path) unless File.file?(file_path)
31
+
32
+ file_contains = false
33
+ File.foreach(file_path) do |line|
34
+ if line.include?(needle)
35
+ file_contains = true
36
+ break
37
+ end
38
+ end
39
+ return if file_contains
40
+
41
+ raise MatchError,
42
+ "File with path #{file_path} did not contain #{needle}.\nEntire screen:\n#{self}"
43
+ end
44
+ alias assert_file_like assert_file_contains
45
+
46
+ # Asserts the specified file has the permissions specified
47
+ # @param [String] file_path the path to the file
48
+ # @param [String] permissions the expected permissions of the file (in form '644' or '775')
49
+ # @raise [MatchError] if the file has different permissions than specified
50
+ def assert_file_has_permissions(file_path, permissions)
51
+ raise file_not_found_error(file_path) unless File.exist?(file_path)
52
+ raise file_is_dir_error(file_path) unless File.file?(file_path)
53
+
54
+ file_mode = File.stat(file_path).mode
55
+ perms_octal = format('%o', file_mode)[-3...]
56
+ return if perms_octal == permissions
57
+
58
+ raise MatchError,
59
+ "File had permissions #{perms_octal}, not #{permissions} as expected.\n Entire screen:\n#{self}"
60
+ end
61
+
62
+ # Asserts the specified file has line count specified
63
+ # @param [String] file_path the path to the file
64
+ # @param [String] expected_count the expected line count of the file
65
+ # @raise [MatchError] if the file has a different line count than specified
66
+ def assert_file_has_line_count(file_path, expected_count)
67
+ raise file_not_found_error(file_path) unless File.exist?(file_path)
68
+ raise file_is_dir_error(file_path) unless File.file?(file_path)
69
+
70
+ actual_count = File.foreach(file_path).count
71
+ return if actual_count == expected_count
72
+
73
+ raise MatchError,
74
+ "File had #{actual_count} lines, not #{expected_count} lines as expected.\nEntire screen:\n#{self}"
75
+ end
76
+
77
+ private
78
+
79
+ def file_not_found_error(file_path)
80
+ raise MatchError,
81
+ "File with path #{file_path} was not found when asserted it did exist.\nEntire screen:\n#{self}"
82
+ end
83
+
84
+ def file_is_dir_error(file_path)
85
+ raise MatchError,
86
+ "File with path #{file_path} is a directory.\nEntire screen:\n#{self}"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ # Row Assertions for ttytest2.
5
+ module RowAssertions
6
+ # Asserts the contents of a single row match the value expected
7
+ # @param [Integer] row_number the row (starting from 0) to test against
8
+ # @param [String] expected the expected value of the row. Any trailing whitespace is ignored
9
+ # @raise [MatchError] if the row doesn't match exactly
10
+ def assert_row(row_number, expected)
11
+ validate(row_number)
12
+ expected = expected.rstrip
13
+ actual = row(row_number)
14
+
15
+ return if !actual.nil? && actual == expected
16
+
17
+ raise MatchError,
18
+ "expected row #{row_number} to be #{expected.inspect} but got #{get_inspection(actual)}\n
19
+ Entire screen:\n#{self}"
20
+ end
21
+ alias assert_line assert_row
22
+
23
+ # Asserts the specified row is empty
24
+ # @param [Integer] row_number the row (starting from 0) to test against
25
+ # @raise [MatchError] if the row isn't empty
26
+ def assert_row_is_empty(row_number)
27
+ validate(row_number)
28
+ actual = row(row_number)
29
+
30
+ return if actual == ''
31
+
32
+ raise MatchError,
33
+ "expected row #{row_number} to be empty but got #{get_inspection(actual)}\nEntire screen:\n#{self}"
34
+ end
35
+ alias assert_line_is_empty assert_row_is_empty
36
+
37
+ # Asserts the contents of a single row contains the expected string at a specific position
38
+ # @param [Integer] row_number the row (starting from 0) to test against
39
+ # @param [Integer] column_start the column position to start comparing expected against
40
+ # @param [Integer] columns_end the column position to end comparing expected against
41
+ # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
42
+ # @raise [MatchError] if the row doesn't match
43
+ def assert_row_at(row_number, column_start, column_end, expected)
44
+ validate(row_number)
45
+ expected = expected.rstrip
46
+ actual = row(row_number)
47
+ column_end += 1
48
+
49
+ return if !actual.nil? && actual[column_start, column_end].eql?(expected)
50
+
51
+ inspection = get_inspection_bounded(actual, column_start, column_end)
52
+
53
+ raise MatchError,
54
+ "expected row #{row_number} to contain #{expected[column_start,
55
+ column_end]} at #{column_start}-#{column_end} and got #{inspection}\nEntire screen:\n#{self}"
56
+ end
57
+ alias assert_line_at assert_row_at
58
+
59
+ # Asserts the contents of a single row contains the value expected
60
+ # @param [Integer] row_number the row (starting from 0) to test against
61
+ # @param [String] expected the expected value contained in the row. Any trailing whitespace is ignored
62
+ # @raise [MatchError] if the row doesn't match
63
+ def assert_row_like(row_number, expected)
64
+ validate(row_number)
65
+ expected = expected.rstrip
66
+ actual = row(row_number)
67
+
68
+ return if !actual.nil? && actual.include?(expected)
69
+
70
+ raise MatchError,
71
+ "expected row #{row_number} to be like #{expected.inspect} but got #{get_inspection(actual)}\nEntire screen:\n#{self}"
72
+ end
73
+ alias assert_row_contains assert_row_like
74
+ alias assert_line_contains assert_row_like
75
+ alias assert_line_like assert_row_like
76
+
77
+ # Asserts the contents of a single row starts with expected string
78
+ # @param [Integer] row_number the row (starting from 0) to test against
79
+ # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
80
+ # @raise [MatchError] if the row doesn't match
81
+ def assert_row_starts_with(row_number, expected)
82
+ validate(row_number)
83
+ expected = expected.rstrip
84
+ actual = row(row_number)
85
+
86
+ return if !actual.nil? && actual.start_with?(expected)
87
+
88
+ raise MatchError,
89
+ "expected row #{row_number} to start with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
90
+ end
91
+ alias assert_line_starts_with assert_row_starts_with
92
+
93
+ # Asserts the contents of a single row end with expected
94
+ # @param [Integer] row_number the row (starting from 0) to test against
95
+ # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
96
+ # @raise [MatchError] if the row doesn't match
97
+ def assert_row_ends_with(row_number, expected)
98
+ validate(row_number)
99
+ expected = expected.rstrip
100
+ actual = row(row_number)
101
+
102
+ return if !actual.nil? && actual.end_with?(expected)
103
+
104
+ raise MatchError,
105
+ "expected row #{row_number} to end with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
106
+ end
107
+ alias assert_line_ends_with assert_row_ends_with
108
+
109
+ # Asserts the contents of a single row match against the passed in regular expression
110
+ # @param [Integer] row_number the row (starting from 0) to test against
111
+ # @param [String] regexp_str the regular expression as a string that will be used to match with.
112
+ # @raise [MatchError] if the row doesn't match against the regular expression
113
+ def assert_row_regexp(row_number, regexp_str)
114
+ validate(row_number)
115
+ regexp = Regexp.new(regexp_str)
116
+ actual = row(row_number)
117
+
118
+ return if !actual.nil? && actual.match?(regexp)
119
+
120
+ raise MatchError,
121
+ "expected row #{row_number} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual)}\nEntire screen:\n#{self}"
122
+ end
123
+ alias assert_line_regexp assert_row_regexp
124
+
125
+ # Asserts the contents of a multiple rows each match against the passed in regular expression
126
+ # @param [Integer] row_start the row (starting from 0) to test against
127
+ # @param [Integer] row_end the last row to test against
128
+ # @param [String] regexp_str the regular expression as a string that will be used to match with.
129
+ # @raise [MatchError] if the row doesn't match against the regular expression
130
+ def assert_rows_each_match_regexp(row_start, row_end, regexp_str)
131
+ validate(row_end)
132
+ regexp = Regexp.new(regexp_str)
133
+ row_end += 1 if row_end.zero?
134
+
135
+ rows.slice(row_start, row_end).each_with_index do |actual_row, index|
136
+ next if !actual_row.nil? && actual_row.match?(regexp)
137
+
138
+ raise MatchError,
139
+ "expected row #{index} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual_row)}\nEntire screen:\n#{self}"
140
+ end
141
+ end
142
+ alias assert_lines_each_match_regexp assert_rows_each_match_regexp
143
+ end
144
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ # Screen Assertions for ttytest2.
5
+ module ScreenAssertions
6
+ # Asserts the full contents of the terminal
7
+ # @param [String] expected the full expected contents of the terminal. Trailing whitespace on each line is ignored
8
+ # @raise [MatchError] if the terminal doesn't match the expected content
9
+ def assert_contents(expected)
10
+ matched, diff = get_diff(expected, rows)
11
+
12
+ return if matched
13
+
14
+ raise MatchError,
15
+ "screen did not match expected content:\n--- expected\n+++ actual\n#{diff.join("\n")}"
16
+ end
17
+ alias assert_matches assert_contents
18
+ alias assert_screen assert_contents
19
+
20
+ # Asserts the contents of the terminal at specified rows
21
+ # @param [String] expected the expected contents of the terminal at specified rows. Trailing whitespace on each line is ignored
22
+ # @raise [MatchError] if the terminal doesn't match the expected content
23
+ def assert_contents_at(row_start, row_end, expected)
24
+ validate(row_end)
25
+ row_end += 1 if row_end.zero?
26
+
27
+ matched, diff = get_diff(expected, rows.slice(row_start, row_end))
28
+
29
+ return if matched
30
+
31
+ raise MatchError,
32
+ "screen did not match expected content:\n--- expected\n+++ actual\n#{diff.join("\n")}"
33
+ end
34
+ alias assert_matches_at assert_contents_at
35
+ alias assert_rows assert_contents_at
36
+
37
+ # Asserts the contents of the screen include the passed in string
38
+ # @param [String] expected the string value expected to be found in the screen contents
39
+ # @raise [MatchError] if the screen does not contain the expected value
40
+ def assert_contents_include(expected)
41
+ found = false
42
+ rows.each do |row|
43
+ found = true if row.include?(expected)
44
+ end
45
+ return if found
46
+
47
+ raise MatchError,
48
+ "Expected screen contents to include #{expected}, but it was not found.\nEntire screen:\n#{self}"
49
+ end
50
+ alias assert_screen_includes assert_contents_include
51
+
52
+ # Asserts the contents of the screen are empty
53
+ # @raise [MatchError] if the screen is not empty
54
+ def assert_contents_empty
55
+ return if rows.all? { |s| s.to_s.empty? }
56
+
57
+ raise MatchError,
58
+ "Expected screen to be empty, but found content.\nEntire screen:\n#{self}"
59
+ end
60
+ alias assert_screen_empty assert_contents_empty
61
+
62
+ # Asserts the contents of the screen as a single string match the passed in regular expression
63
+ # @param [String] regexp_str the regular expression as a string that will be used to match with
64
+ # @raise [MatchError] if the screen as a string doesn't match against the regular expression
65
+ def assert_contents_match_regexp(regexp_str)
66
+ regexp = Regexp.new(regexp_str)
67
+ screen = capture.to_s
68
+
69
+ return if !screen.nil? && screen.match?(regexp)
70
+
71
+ raise MatchError,
72
+ "Expected screen contents to match regexp #{regexp_str} but they did not\nEntire screen:\n#{self}"
73
+ end
74
+ alias assert_screen_matches_regexp assert_contents_match_regexp
75
+ end
76
+ end
@@ -1,316 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ttytest/assertions/file_assertions'
4
+ require 'ttytest/assertions/row_assertions'
5
+ require 'ttytest/assertions/column_assertions'
6
+ require 'ttytest/assertions/screen_assertions'
7
+ require 'ttytest/assertions/cursor_assertions'
8
+
3
9
  module TTYtest
4
10
  # Assertions for ttytest2.
5
11
  module Assertions
6
- # Asserts the contents of a single row match the value expected
7
- # @param [Integer] row_number the row (starting from 0) to test against
8
- # @param [String] expected the expected value of the row. Any trailing whitespace is ignored
9
- # @raise [MatchError] if the row doesn't match exactly
10
- def assert_row(row_number, expected)
11
- validate(row_number)
12
- expected = expected.rstrip
13
- actual = row(row_number)
14
-
15
- return if !actual.nil? && actual == expected
16
-
17
- raise MatchError,
18
- "expected row #{row_number} to be #{expected.inspect} but got #{get_inspection(actual)}\n
19
- Entire screen:\n#{self}"
20
- end
21
- alias assert_line assert_row
22
-
23
- # Asserts the specified row is empty
24
- # @param [Integer] row_number the row (starting from 0) to test against
25
- # @raise [MatchError] if the row isn't empty
26
- def assert_row_is_empty(row_number)
27
- validate(row_number)
28
- actual = row(row_number)
29
-
30
- return if actual == ''
31
-
32
- raise MatchError,
33
- "expected row #{row_number} to be empty but got #{get_inspection(actual)}\nEntire screen:\n#{self}"
34
- end
35
- alias assert_line_is_empty assert_row_is_empty
36
-
37
- # Asserts the contents of a single row contains the expected string at a specific position
38
- # @param [Integer] row_number the row (starting from 0) to test against
39
- # @param [Integer] column_start the column position to start comparing expected against
40
- # @param [Integer] columns_end the column position to end comparing expected against
41
- # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
42
- # @raise [MatchError] if the row doesn't match
43
- def assert_row_at(row_number, column_start, column_end, expected)
44
- validate(row_number)
45
- expected = expected.rstrip
46
- actual = row(row_number)
47
- column_end += 1
48
-
49
- return if !actual.nil? && actual[column_start, column_end].eql?(expected)
50
-
51
- inspection = get_inspection_bounded(actual, column_start, column_end)
52
-
53
- raise MatchError,
54
- "expected row #{row_number} to contain #{expected[column_start,
55
- column_end]} at #{column_start}-#{column_end} and got #{inspection}\nEntire screen:\n#{self}"
56
- end
57
- alias assert_line_at assert_row_at
58
-
59
- # Asserts the contents of a single row contains the value expected
60
- # @param [Integer] row_number the row (starting from 0) to test against
61
- # @param [String] expected the expected value contained in the row. Any trailing whitespace is ignored
62
- # @raise [MatchError] if the row doesn't match
63
- def assert_row_like(row_number, expected)
64
- validate(row_number)
65
- expected = expected.rstrip
66
- actual = row(row_number)
67
-
68
- return if !actual.nil? && actual.include?(expected)
69
-
70
- raise MatchError,
71
- "expected row #{row_number} to be like #{expected.inspect} but got #{get_inspection(actual)}\nEntire screen:\n#{self}"
72
- end
73
- alias assert_row_contains assert_row_like
74
- alias assert_line_contains assert_row_like
75
- alias assert_line_like assert_row_like
76
-
77
- # Asserts the contents of a single row starts with expected string
78
- # @param [Integer] row_number the row (starting from 0) to test against
79
- # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
80
- # @raise [MatchError] if the row doesn't match
81
- def assert_row_starts_with(row_number, expected)
82
- validate(row_number)
83
- expected = expected.rstrip
84
- actual = row(row_number)
85
-
86
- return if !actual.nil? && actual.start_with?(expected)
87
-
88
- raise MatchError,
89
- "expected row #{row_number} to start with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
90
- end
91
- alias assert_line_starts_with assert_row_starts_with
92
-
93
- # Asserts the contents of a single row end with expected
94
- # @param [Integer] row_number the row (starting from 0) to test against
95
- # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
96
- # @raise [MatchError] if the row doesn't match
97
- def assert_row_ends_with(row_number, expected)
98
- validate(row_number)
99
- expected = expected.rstrip
100
- actual = row(row_number)
101
-
102
- return if !actual.nil? && actual.end_with?(expected)
103
-
104
- raise MatchError,
105
- "expected row #{row_number} to end with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
106
- end
107
- alias assert_line_ends_with assert_row_ends_with
108
-
109
- # Asserts the contents of a single row match against the passed in regular expression
110
- # @param [Integer] row_number the row (starting from 0) to test against
111
- # @param [String] regexp_str the regular expression as a string that will be used to match with.
112
- # @raise [MatchError] if the row doesn't match against the regular expression
113
- def assert_row_regexp(row_number, regexp_str)
114
- validate(row_number)
115
- regexp = Regexp.new(regexp_str)
116
- actual = row(row_number)
117
-
118
- return if !actual.nil? && actual.match?(regexp)
119
-
120
- raise MatchError,
121
- "expected row #{row_number} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual)}\nEntire screen:\n#{self}"
122
- end
123
- alias assert_line_regexp assert_row_regexp
124
-
125
- # Asserts the contents of a multiple rows each match against the passed in regular expression
126
- # @param [Integer] row_start the row (starting from 0) to test against
127
- # @param [Integer] row_end the last row to test against
128
- # @param [String] regexp_str the regular expression as a string that will be used to match with.
129
- # @raise [MatchError] if the row doesn't match against the regular expression
130
- def assert_rows_each_match_regexp(row_start, row_end, regexp_str)
131
- validate(row_end)
132
- regexp = Regexp.new(regexp_str)
133
- row_end += 1 if row_end.zero?
134
-
135
- rows.slice(row_start, row_end).each_with_index do |actual_row, index|
136
- next if !actual_row.nil? && actual_row.match?(regexp)
137
-
138
- raise MatchError,
139
- "expected row #{index} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual_row)}\nEntire screen:\n#{self}"
140
- end
141
- end
142
- alias assert_lines_each_match_regexp assert_rows_each_match_regexp
143
-
144
- # Asserts that the cursor is in the expected position
145
- # @param [Integer] x cursor x (row) position, starting from 0
146
- # @param [Integer] y cursor y (column) position, starting from 0
147
- # @raise [MatchError] if the cursor position doesn't match
148
- def assert_cursor_position(x, y)
149
- expected = [x, y]
150
- actual = [cursor_x, cursor_y]
151
-
152
- return if actual == expected
153
-
154
- raise MatchError,
155
- "expected cursor to be at #{expected.inspect} but was at #{get_inspection(actual)}\nEntire screen:\n#{self}"
156
- end
157
-
158
- # Asserts the cursor is currently visible
159
- # @raise [MatchError] if the cursor is hidden
160
- def assert_cursor_visible
161
- return if cursor_visible?
162
-
163
- raise MatchError, "expected cursor to be visible was hidden\nEntire screen:\n#{self}"
164
- end
165
-
166
- # Asserts the cursor is currently hidden
167
- # @raise [MatchError] if the cursor is visible
168
- def assert_cursor_hidden
169
- return if cursor_hidden?
170
-
171
- raise MatchError, "expected cursor to be hidden was visible\nEntire screen:\n#{self}"
172
- end
173
-
174
- # Asserts the full contents of the terminal
175
- # @param [String] expected the full expected contents of the terminal. Trailing whitespace on each line is ignored
176
- # @raise [MatchError] if the terminal doesn't match the expected content
177
- def assert_contents(expected)
178
- matched, diff = get_diff(expected, rows)
179
-
180
- return if matched
181
-
182
- raise MatchError,
183
- "screen did not match expected content:\n--- expected\n+++ actual\n#{diff.join("\n")}"
184
- end
185
- alias assert_matches assert_contents
186
- alias assert_screen assert_contents
187
-
188
- # Asserts the contents of the terminal at specified rows
189
- # @param [String] expected the expected contents of the terminal at specified rows. Trailing whitespace on each line is ignored
190
- # @raise [MatchError] if the terminal doesn't match the expected content
191
- def assert_contents_at(row_start, row_end, expected)
192
- validate(row_end)
193
- row_end += 1 if row_end.zero?
194
-
195
- matched, diff = get_diff(expected, rows.slice(row_start, row_end))
196
-
197
- return if matched
198
-
199
- raise MatchError,
200
- "screen did not match expected content:\n--- expected\n+++ actual\n#{diff.join("\n")}"
201
- end
202
- alias assert_matches_at assert_contents_at
203
- alias assert_rows assert_contents_at
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
247
- def assert_file_exists(file_path)
248
- raise file_not_found_error(file_path) unless File.exist?(file_path)
249
- raise file_is_dir_error(file_path) unless File.file?(file_path)
250
- end
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
255
- def assert_file_doesnt_exist(file_path)
256
- return unless File.exist?(file_path) || File.file?(file_path)
257
-
258
- raise MatchError,
259
- "File with path #{file_path} was found or is a directory when it was asserted it did not exist.\nEntire screen:\n#{self}"
260
- end
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
266
- def assert_file_contains(file_path, needle)
267
- raise file_not_found_error(file_path) unless File.exist?(file_path)
268
- raise file_is_dir_error(file_path) unless File.file?(file_path)
269
-
270
- file_contains = false
271
- File.foreach(file_path) do |line|
272
- if line.include?(needle)
273
- file_contains = true
274
- break
275
- end
276
- end
277
- return if file_contains
278
-
279
- raise MatchError,
280
- "File with path #{file_path} did not contain #{needle}.\nEntire screen:\n#{self}"
281
- end
282
- alias assert_file_like assert_file_contains
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
288
- def assert_file_has_permissions(file_path, permissions)
289
- raise file_not_found_error(file_path) unless File.exist?(file_path)
290
- raise file_is_dir_error(file_path) unless File.file?(file_path)
291
-
292
- file_mode = File.stat(file_path).mode
293
- perms_octal = format('%o', file_mode)[-3...]
294
- return if perms_octal == permissions
295
-
296
- raise MatchError,
297
- "File had permissions #{perms_octal}, not #{permissions} as expected.\n Entire screen:\n#{self}"
298
- end
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
12
+ include TTYtest::FileAssertions
13
+ include TTYtest::RowAssertions
14
+ include TTYtest::ColumnAssertions
15
+ include TTYtest::ScreenAssertions
16
+ include TTYtest::CursorAssertions
314
17
 
315
18
  METHODS = public_instance_methods
316
19
 
@@ -358,15 +61,5 @@ module TTYtest
358
61
 
359
62
  [matched, diff]
360
63
  end
361
-
362
- def file_not_found_error(file_path)
363
- raise MatchError,
364
- "File with path #{file_path} was not found when asserted it did exist.\nEntire screen:\n#{self}"
365
- end
366
-
367
- def file_is_dir_error(file_path)
368
- raise MatchError,
369
- "File with path #{file_path} is a directory.\nEntire screen:\n#{self}"
370
- end
371
64
  end
372
65
  end
@@ -9,7 +9,7 @@ module TTYtest
9
9
  class Capture
10
10
  include TTYtest::Assertions
11
11
 
12
- attr_reader :cursor_x, :cursor_y, :width, :height
12
+ attr_reader :cursor_x, :cursor_y, :width, :height, :max_wait_time, :use_return_for_newline
13
13
 
14
14
  # Used internally by drivers when called by {Terminal#capture}
15
15
  # @api private
@@ -16,7 +16,8 @@ module TTYtest
16
16
  SHIFT_ENTER = 11.chr # \v
17
17
  FORM_FEED = 12.chr # \f
18
18
  CTRLL = 12.chr
19
- CARRIAGE_RETURN = 13.chr # \r
19
+ CARRIAGE_RETURN = 13.chr # \r, left for backwards compat
20
+ RETURN = 13.chr # \r, same as CARRIAGE_RETURN
20
21
  CTRLU = 21.chr
21
22
  CTRLW = 23.chr
22
23
  ESCAPE = 27.chr # ^[ or /033 or /e
@@ -9,14 +9,15 @@ module TTYtest
9
9
  class Terminal
10
10
  extend Forwardable
11
11
 
12
- attr_accessor :max_wait_time
12
+ attr_accessor :max_wait_time, :use_return_for_newline
13
13
 
14
14
  # @api private
15
15
  # @see TTYtest.new_terminal, use this or other new_* methods instead.
16
16
  # Internal constructor.
17
- def initialize(driver_terminal)
17
+ def initialize(driver_terminal, max_wait_time, use_return_for_newline)
18
+ @max_wait_time = max_wait_time
19
+ @use_return_for_newline = use_return_for_newline
18
20
  @driver_terminal = driver_terminal
19
- @max_wait_time = TTYtest.default_max_wait_time
20
21
  end
21
22
 
22
23
  # @!method send_keys(*keys)
@@ -67,17 +68,21 @@ module TTYtest
67
68
  # @param [Integer] sleep_time the amount of time to sleep after sending the line
68
69
 
69
70
  # @!method send_newline
70
- # Simulate typing enter by sending newline character to the terminal.
71
+ # Simulate typing enter by sending newline (ALT + enter) character to the terminal.
72
+ # Many line readers interpreter newline as return, but in some cases you may have to use send_return.
71
73
 
72
74
  # @!method send_newlines(number_of_times)
73
- # Simulates sending newline the specified number of times.
75
+ # Simulates sending newline (ALT + enter) the specified number of times.
76
+ # Many line readers interpreter newline as return, but in some cases you may have to use send_return.
74
77
  # @param [Integer] number_of_times number of times to send newline
75
78
 
76
- # @!method send_enter
79
+ # @!method send_return
77
80
  # Simulate typing enter by sending newline character to the terminal.
81
+ # Many line readers interpreter newline as return, but in some cases you may have to use send_return.
78
82
 
79
- # @!method send_enters(number_of_times)
83
+ # @!method send_returns(number_of_times)
80
84
  # Simulates sending newline the specified number of times.
85
+ # Many line readers interpreter newline as return, but in some cases you may have to use send_return.
81
86
  # @param [Integer] number of times to send newline
82
87
 
83
88
  # @!method send_delete
@@ -142,6 +147,13 @@ module TTYtest
142
147
  # Simulates typing in the Escape (ESC) key in the terminal the specified number of times.
143
148
  # @param [Integer] number of times to send escape
144
149
 
150
+ # @!method send_tab
151
+ # Simulates typing in the Tab (\t) key in the terminal.
152
+
153
+ # @!method send_tabs(number_of_times)
154
+ # Simulates typing in the Tab (\t) key in the terminal the specified number of times.
155
+ # @param [Integer] number of times to send tab
156
+
145
157
  # @!method capture
146
158
  # Capture represents the current state of the terminal.
147
159
  # @return [Capture] The current state of the terminal when called
@@ -152,11 +164,13 @@ module TTYtest
152
164
  :send_line_exact, :send_lines_exact,
153
165
  :send_newline, :send_newlines,
154
166
  :send_enter, :send_enters,
167
+ :send_return, :send_returns,
155
168
  :send_delete, :send_deletes,
156
169
  :send_backspace, :send_backspaces,
157
170
  :send_left_arrow, :send_left_arrows, :send_right_arrow, :send_right_arrows,
158
171
  :send_down_arrow, :send_down_arrows, :send_up_arrow, :send_up_arrows,
159
172
  :send_keys_exact, :send_home, :send_end, :send_clear, :send_escape, :send_escapes,
173
+ :send_tab, :send_tabs,
160
174
  :capture
161
175
 
162
176
  # @!method print
@@ -31,21 +31,22 @@ module TTYtest
31
31
  @config_file_path = config_file_path
32
32
  end
33
33
 
34
- def new_terminal(cmd, width: 80, height: 24)
34
+ def new_terminal(cmd, width: 80, height: 24, max_wait_time: 2, use_return_for_newline: false)
35
35
  cmd = "#{cmd}\n#{SLEEP_INFINITY}"
36
36
 
37
37
  session_name = "ttytest-#{SecureRandom.uuid}"
38
38
  tmux(*%W[-f #{@config_file_path} new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
39
- session = Session.new(self, session_name)
40
- Terminal.new(session)
39
+ session = Session.new(self, session_name, use_return_for_newline)
40
+ Terminal.new(session, max_wait_time, use_return_for_newline)
41
41
  end
42
42
 
43
43
  def new_default_sh_terminal
44
- new_terminal(%(PS1='$ ' /bin/sh), width: 80, height: 24)
44
+ new_terminal(%(PS1='$ ' /bin/sh), width: 80, height: 24, max_wait_time: 2, use_return_for_newline: false)
45
45
  end
46
46
 
47
- def new_sh_terminal(width: 80, height: 24)
48
- new_terminal(%(PS1='$ ' /bin/sh), width: width, height: height)
47
+ def new_sh_terminal(width: 80, height: 24, max_wait_time: 2, use_return_for_newline: false)
48
+ new_terminal(%(PS1='$ ' /bin/sh), width: width, height: height, max_wait_time: max_wait_time,
49
+ use_return_for_newline: use_return_for_newline)
49
50
  end
50
51
 
51
52
  # @api private
@@ -5,10 +5,11 @@ module TTYtest
5
5
  # represents a tmux session and how to send output to the current tmux session
6
6
  class Session
7
7
  # @api private
8
- def initialize(driver, name)
8
+ def initialize(driver, name, use_return_for_newline)
9
9
  @id = SecureRandom.uuid
10
10
  @driver = driver
11
11
  @name = name
12
+ @use_return_for_newline = use_return_for_newline
12
13
 
13
14
  ObjectSpace.define_finalizer(@id, proc {
14
15
  begin
@@ -58,6 +59,10 @@ module TTYtest
58
59
  # Send line to tmux, no need to worry about newline character
59
60
  def send_line(line)
60
61
  send_keys_one_at_a_time(line)
62
+ if @use_return_for_newline
63
+ send_return unless ['\n', '\r'].include?(line[-1])
64
+ return
65
+ end
61
66
  send_newline unless line[-1] == '\n'
62
67
  end
63
68
 
@@ -75,6 +80,10 @@ module TTYtest
75
80
 
76
81
  def send_line_exact(line)
77
82
  send_keys_exact(line)
83
+ if @use_return_for_newline
84
+ send_return unless ['\n', '\r'].include?(line[-1])
85
+ return
86
+ end
78
87
  send_newline unless line[-1] == '\n'
79
88
  end
80
89
 
@@ -100,7 +109,6 @@ module TTYtest
100
109
  def send_newline
101
110
  driver.tmux(*%W[send-keys -t #{name} -l], %(\n))
102
111
  end
103
- alias send_enter send_newline
104
112
 
105
113
  def send_newlines(number_of_times)
106
114
  while number_of_times.positive?
@@ -108,7 +116,17 @@ module TTYtest
108
116
  number_of_times -= 1
109
117
  end
110
118
  end
111
- alias send_enters send_newlines
119
+
120
+ def send_return
121
+ driver.tmux(*%W[send-keys -t #{name} -l], %(\r))
122
+ end
123
+
124
+ def send_returns(number_of_times)
125
+ while number_of_times.positive?
126
+ send_return
127
+ number_of_times -= 1
128
+ end
129
+ end
112
130
 
113
131
  def send_delete
114
132
  send_keys_exact(%(DC))
@@ -194,6 +212,10 @@ module TTYtest
194
212
 
195
213
  def send_clear
196
214
  send_keys_one_at_a_time(TTYtest::CLEAR)
215
+ if @use_return_for_newline
216
+ send_return
217
+ return
218
+ end
197
219
  send_newline
198
220
  end
199
221
 
@@ -208,6 +230,17 @@ module TTYtest
208
230
  end
209
231
  end
210
232
 
233
+ def send_tab
234
+ send_keys_exact(TTYtest::TAB)
235
+ end
236
+
237
+ def send_tabs(number_of_times)
238
+ while number_of_times.positive?
239
+ send_tab
240
+ number_of_times -= 1
241
+ end
242
+ end
243
+
211
244
  private
212
245
 
213
246
  attr_reader :driver, :name
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTYtest
4
- VERSION = '1.1.0'
4
+ VERSION = '1.3.0'
5
5
  end
data/lib/ttytest.rb CHANGED
@@ -16,30 +16,38 @@ module TTYtest
16
16
  # @param [String] command a valid shell command to run
17
17
  # @param [Integer] width width of the new terminal
18
18
  # @param [Integer] height height of the new terminal
19
+ # @param [Integer] max_wait_time max time to wait for screen to update before an assertion fails
20
+ # @param [bool] use_return_for_newline use return instead of newline for functions like send_line
19
21
  # @return [Terminal] a new terminal running the specified command
20
22
 
21
23
  # @!method new_default_sh_terminal
22
24
  # Create a new terminal using '/bin/sh' with width: 80 and height: 24.
23
25
  # Useful for Unixes.
26
+ # @return [Terminal] a new sh terminal
24
27
 
25
28
  # @!method new_sh_terminal(width: 80, height: 24)
26
29
  # Create a new terminal using '/bin/sh' with ability to set width and height.
27
30
  # Useful for Unixes.
31
+ # @param [Integer] max_wait_time max time to wait for screen to update before an assertion fails
32
+ # @param [bool] use_return_for_newline use return instead of newline for functions like send_line
33
+ # @return [Terminal] a new sh terminal with specified width and height
34
+
28
35
  def_delegators :driver
29
36
 
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)
37
+ def new_terminal(cmd, width: 80, height: 24, max_wait_time: 2, use_return_for_newline: false)
38
+ # @max_wait_time = max_wait_time
39
+ # @use_return_for_newline = use_return_for_newline
40
+ driver.new_terminal(cmd, width: width, height: height, max_wait_time: max_wait_time,
41
+ use_return_for_newline: use_return_for_newline)
33
42
  end
34
43
 
35
- def new_default_sh_terminal(max_wait_time: 2)
36
- @max_wait_time = max_wait_time
44
+ def new_default_sh_terminal
37
45
  driver.new_default_sh_terminal
38
46
  end
39
47
 
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)
48
+ def new_sh_terminal(width: 80, height: 24, max_wait_time: 2, use_return_for_newline: false)
49
+ driver.new_sh_terminal(width: width, height: height, max_wait_time: max_wait_time,
50
+ use_return_for_newline: use_return_for_newline)
43
51
  end
44
52
  end
45
53
 
data/notes.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  to push new version to github
2
- git tag v1.1.0
2
+ git tag v1.3.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.1.0.gem
7
+ gem push ttytest2-1.3.0.gem
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.1.0
4
+ version: 1.3.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-08-06 00:00:00.000000000 Z
11
+ date: 2025-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -95,8 +95,14 @@ files:
95
95
  - README.md
96
96
  - Rakefile
97
97
  - examples/integration_tests.rb
98
+ - images/ttytest2.png
98
99
  - lib/ttytest.rb
99
100
  - lib/ttytest/assertions.rb
101
+ - lib/ttytest/assertions/column_assertions.rb
102
+ - lib/ttytest/assertions/cursor_assertions.rb
103
+ - lib/ttytest/assertions/file_assertions.rb
104
+ - lib/ttytest/assertions/row_assertions.rb
105
+ - lib/ttytest/assertions/screen_assertions.rb
100
106
  - lib/ttytest/capture.rb
101
107
  - lib/ttytest/constants.rb
102
108
  - lib/ttytest/terminal.rb