ttytest2 1.0.2 → 1.0.4

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: 9b9538d393117d0cc417fd097ce965cc0c84612e9c8c244a7462072568ae1f42
4
- data.tar.gz: c88ea611163de40b6047faea181a573e767e6e68a3644143a0e4e00dc3401448
3
+ metadata.gz: b1878cb00bec4cb2ad2213c6659212cbef7dfc0c1dab31e445e07b1fcbf088e7
4
+ data.tar.gz: 2298de4d8674e11cf0c23e520176ca3aa9a33ce1ec75e24e712be7a269b79840
5
5
  SHA512:
6
- metadata.gz: 86fa0a0862a158102d08c9d05df218927dee91bf49c73176d1f7979136c626b10496e02af55085f30e336492d1aad3884da8690d908868ed3bfb8b92f179172e
7
- data.tar.gz: f2a16dcae9a58b970659e7a5181ba4dfad50b106bdf5efb057cc1ff3ccccc3712c7d7ded8edb49ed10102279244e7059ebd4c02b5d66940fa888fde4bc279fb6
6
+ metadata.gz: 73d59c8dbfab01db1c6305b4f3ad6c8f6a44698c83cf969516a3accf33422005b18377d1a809d883834b8e0b11fdd729096460c90de7742417c65f9bd5c79303
7
+ data.tar.gz: '09c46d90a2776c1fb36d0b896b46c4b1d6b537382b41eb33784326cf99134bf68eac9f89e3dd910a16c3938d188ea1c3a66674e36b8387729f09d4a4b0216e24'
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # ttytest2
2
2
 
3
- ttytest2 is an acceptance test framework for interactive console applications. It's like [capybara](https://github.com/teamcapybara/capybara) for the terminal.
3
+ ttytest2 is a user acceptance test framework for CLI & shell applications.
4
4
 
5
- ttytest2 is a fork and a drop-in replacement for [ttytest](https://github.com/jhawthorn/ttytest), because I had some features I needed for my own project.
5
+ ttytest2 is a fork and a drop-in replacement for [ttytest](https://github.com/jhawthorn/ttytest).
6
6
 
7
- It works by running commands inside a tmux session, capturing the pane, and comparing the content to your assertions.
7
+ It works by creating a tmux session behind the scenes, running the specified commands, capturing the pane, and then comparing the actual content to the expected content based on the assertions made.
8
8
 
9
9
  The assertions will wait a specified amount of time (configurable, default 2 seconds) for the expected content to appear.
10
10
 
@@ -14,16 +14,15 @@ The assertions will wait a specified amount of time (configurable, default 2 sec
14
14
 
15
15
  1. [Minimum Requirements](#minimum-requirements)
16
16
  2. [Usage](#usage)
17
- 3. [Simple Example](#simple-example)
17
+ 3. [Initializing](#initializing)
18
18
  4. [Assertions](#assertions)
19
19
  5. [Output](#output)
20
- 6. [Output Helpers](#output-helpers)
20
+ 6. [Configurables](#configurables)
21
21
  7. [Troubleshooting](#troubleshooting)
22
- 8. [Constants](#constants)
23
- 9. [Tips](#tips)
24
- 10. [Docker](#docker)
25
- 11. [Contributing](#contributing)
26
- 12. [License](#license)
22
+ 8. [Tips](#tips)
23
+ 9. [Docker](#docker)
24
+ 10. [Contributing](#contributing)
25
+ 11. [License](#license)
27
26
 
28
27
  ## Minimum Requirements
29
28
 
@@ -69,7 +68,7 @@ TTY
69
68
  # $
70
69
  ```
71
70
 
72
- ### Initializing
71
+ ## Initializing
73
72
 
74
73
  Call one of these methods to initialize an instance of ttytest2.
75
74
 
@@ -94,13 +93,19 @@ require 'ttytest'
94
93
  @tty = TTYtest.new_terminal('/bin/bash', width: 80, height: 24)
95
94
  ```
96
95
 
97
- ### Assertions
96
+ ## Assertions
98
97
 
99
- The main way to use TTYtest is through assertions.
98
+ The main way to use ttytest2 is through assertions.
100
99
 
101
- Assertions will be retried for up to 2 seconds when called through TTYtest::Terminal.
100
+ Assertions will be retried for up to 2 seconds when called through TTYtest::Terminal, unless you specify otherwise (see [Configurables](#configurables)).
102
101
 
103
- Available assertions:
102
+ ### Aliases
103
+
104
+ Some assertions have aliases, like `assert_matches_at` has the alias `assert_rows`.
105
+
106
+ If you are reading this on github, the ruby docs accessible from [RubyDoc.Info](https://www.rubydoc.info/gems/ttytest2/) document all of the aliases.
107
+
108
+ ### Available Assertions
104
109
 
105
110
  * `assert_row(row_number, expected_text)`
106
111
 
@@ -126,12 +131,28 @@ Available assertions:
126
131
 
127
132
  * `assert_contents_at(row_start, row_end, expected_text)`
128
133
 
129
- ### Output
134
+ ## Output
130
135
 
131
136
  You can send output to the terminal with the following calls.
132
137
 
133
138
  Note: Most of the time send_line has the best ergonomics.
134
139
 
140
+ ### Base Functions
141
+
142
+ These functions form the basis of interacting with the tmux pane. They power all other functions, but you can use them directly when needed.
143
+
144
+ * `send_keys(output)`: for canonical shells/CLI's (or multi-character keys for noncanonical shells/CLI's).
145
+
146
+ * `send_keys_one_at_a_time(output)`: for noncanonical shells/CLI's.
147
+
148
+ * `send_keys_exact(output)`: sends keys as is, exactly. Also useful for sending tmux specific keys (any supported tmux send-keys arguments like: DC for delete, Escape for ESC, etc.)
149
+
150
+ ### Ergonomic Functions
151
+
152
+ The base functions work great, but these functions build upon the base functions to provide more functionality and better ergonomics in most cases.
153
+
154
+ For example, `send_line(line)` makes sure that the enter key (newline character) is sent after the `line` so you don't have to worry about adding it to `line` or calling send_newline after.
155
+
135
156
  * `send_line(line)`: simulate typing in a command in the terminal and hitting enter!
136
157
 
137
158
  * `send_line_then_sleep(line, sleep_time)`: simulate typing in a command in the terminal and hitting enter, then wait for sleep_time seconds.
@@ -142,11 +163,9 @@ Note: Most of the time send_line has the best ergonomics.
142
163
 
143
164
  * `send_line_then_sleep_and_repeat(lines, sleep_time)`: for each line in lines, simulate sending the line and hitting enter, then sleep before sending the next line.
144
165
 
145
- * `send_keys(output)`: for canonical shells/CLI's (or multi-character keys for noncanonical shells/CLI's).
146
-
147
- * `send_keys_one_at_a_time(output)`: for noncanonical shells/CLI's.
166
+ * `send_line_exact`: send line exactly as is to tmux. Certain special characters may not work with send_line. You can also include tmux send-keys arguments like DC for delete, etc.
148
167
 
149
- * `send_keys_exact(output)`: for sending tmux specific keys (any supported send-keys arguments like: DC for delete, Escape for ESC, etc.)
168
+ * `send_lines_exact`: send lines exactly are they are to tmux. Similar semantics to send_line_exact.
150
169
 
151
170
  ### Output Helpers
152
171
 
@@ -161,7 +180,7 @@ Helper functions to make sending output easier! They use the methods above under
161
180
  * `send_backspaces(number_of_times)` # equivalent to calling send_backspace number_of_times
162
181
 
163
182
  * `send_delete` # simulate hitting delete, equivalent to calling send_keys_exact(%(DC))
164
- * `send_deletes` # equivalent to calling send_delete number_of_times
183
+ * `send_deletes(number_of_times)` # equivalent to calling send_delete number_of_times
165
184
 
166
185
  * `send_right_arrow`
167
186
  * `send_right_arrows(number_of_times)`
@@ -181,7 +200,7 @@ Helper functions to make sending output easier! They use the methods above under
181
200
  * `send_clear` # clear the screen by sending clear ascii code
182
201
 
183
202
  * `send_escape`
184
- * `send_escapes`
203
+ * `send_escapes(number_of_times)`
185
204
 
186
205
  ### F keys?
187
206
 
@@ -193,48 +212,8 @@ Send F keys like F1, F2, etc. as shown below:
193
212
  @tty.send_keys_exact(%(F1))
194
213
  @tty.send_keys_exact(%(F2))
195
214
  # ...
196
- @tty.send_keys_exact(%(F11))
197
- @tty.send_keys_exact(%(F12))
198
- ```
199
-
200
- ### Configurables
201
-
202
- Currently the only configuration for ttytest2 is max wait time.
203
-
204
- Max wait time represents the amount of time in seconds that ttytest2 will keep retrying an assertion before failing.
205
-
206
- You can configure max wait time as shown below.
207
-
208
- ``` ruby
209
- @tty = TTYtest::new_terminal('')
210
- @tty.max_wait_time = 1 # sets the max wait time to 1 second
211
-
212
- @tty.assert_row(0, 'echo Hello, world') # this assertion would fail after 1 second
213
- ```
214
-
215
- ### Troubleshooting
216
-
217
- 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.
218
-
219
- ``` ruby
220
- @tty = TTYtest.new_terminal(%(PS1='$ ' /bin/sh), width: 80, height: 7)
221
- @tty.send_line('echo "Hello, world"'))
222
-
223
- # you can use @tty.rows to access the entire pane, split by line into an array.
224
- @tty.print_rows # prints out the contents of the terminal as an array:
225
- # ["$ echo \"Hello, world\"", "Hello, world", "$", "", "", "", ""]
226
-
227
- # if you want to programatically access the rows, you can do so using @tty.rows
228
- p @tty.rows # is equivalent to above statement @tty.print_rows
229
-
230
- # you can use @tty.capture to access the entire pane.
231
- @tty.print # prints out the contents of the terminal:
232
- # $ echo "Hello, world"
233
- # Hello, world
234
- # $
235
-
236
- # if you want to programatically access the entire pane, you can do so using @tty.capture
237
- p "\n#{@tty.capture}" # is equivalent to above statement @tty.print
215
+ @tty.send_keys_exact('F11')
216
+ @tty.send_keys_exact('F12')
238
217
  ```
239
218
 
240
219
  ### Constants
@@ -274,11 +253,53 @@ There are some commonly used keys available as constants to make interacting wit
274
253
  TTYtest::CLEAR # clear the screen
275
254
  ```
276
255
 
256
+ ## Configurables
257
+
258
+ Currently the only configuration for ttytest2 is max wait time.
259
+
260
+ Max wait time represents the amount of time in seconds that ttytest2 will keep retrying an assertion before failing.
261
+
262
+ You can configure max wait time as shown below.
263
+
264
+ ``` ruby
265
+ @tty = TTYtest::new_terminal('')
266
+ @tty.max_wait_time = 1 # sets the max wait time to 1 second
267
+
268
+ @tty.assert_row(0, 'echo Hello, world') # this assertion would fail after 1 second
269
+ ```
270
+
271
+ ## Troubleshooting
272
+
273
+ 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.
274
+
275
+ ``` ruby
276
+ @tty = TTYtest.new_terminal(%(PS1='$ ' /bin/sh), width: 80, height: 7)
277
+ @tty.send_line('echo "Hello, world"'))
278
+
279
+ # If you want to print the current terminal rows as an array of lines, you can use @tty.print_rows.
280
+ @tty.print_rows # prints out the contents of the terminal as an array:
281
+ # ["$ echo \"Hello, world\"", "Hello, world", "$", "", "", "", ""]
282
+
283
+ # you can use @tty.rows to access the entire pane, split by line into an array.
284
+ # if you want to programatically access the rows, you can do so using @tty.rows.
285
+ p @tty.rows # this is equivalent to above statement @tty.print_rows.
286
+
287
+ # If you want to print the current terminal contents, you can use @tty.print.
288
+ @tty.print # prints out the contents of the terminal:
289
+ # $ echo "Hello, world"
290
+ # Hello, world
291
+ # $
292
+
293
+ # you can use @tty.capture to access the entire pane.
294
+ # if you want to programatically access the entire pane, you can do so using @tty.capture.
295
+ p "\n#{@tty.capture}" # this is equivalent to above statement @tty.print
296
+ ```
297
+
277
298
  ## Tips
278
299
 
279
300
  If you are using ttyest2 to test your CLI, using sh is easier than bash because you don't have to worry about user, current working directory, etc. as shown in the examples.
280
301
 
281
- 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.
302
+ 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.
282
303
 
283
304
  ## Docker
284
305
 
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTYtest
4
- # Represents the complete state of a {TTYtest::Terminal} at the time it was captured (contents, cursor position, etc).
5
- # @attr_reader [Integer] width the number of columns in the captured terminal
6
- # @attr_reader [Integer] height the number of rows in the captured terminal
7
- # @attr_reader [Integer] cursor_x the cursor's column (starting at 0) in the captured terminal
8
- # @attr_reader [Integer] cursor_y the cursor's row (starting at 0) in the captured terminal
4
+ # Represents the complete state of a {TTYtest::Terminal} at the time it was captured, including contents, cursor position, etc.
5
+ # @attr_reader [Integer] width The number of columns in the captured terminal.
6
+ # @attr_reader [Integer] height The number of rows in the captured terminal.
7
+ # @attr_reader [Integer] cursor_x The cursor's column (starting at 0) in the captured terminal.
8
+ # @attr_reader [Integer] cursor_y The cursor's row (starting at 0) in the captured terminal.
9
9
  class Capture
10
10
  include TTYtest::Matchers
11
11
 
@@ -24,39 +24,41 @@ module TTYtest
24
24
  @cursor_visible = cursor_visible
25
25
  end
26
26
 
27
- # @return [Array<String>] An array of each row's contend from the captured terminal
27
+ # @return [Array<String>] An array of each row's contents from the captured terminal.
28
28
  attr_reader :rows
29
29
 
30
- # @param [Integer] the row to return
31
- # @return [String] the content of the row from the captured terminal
30
+ # @param [Integer] The row to return
31
+ # @return [String] The content of the row from the captured terminal.
32
32
  def row(row_number)
33
33
  rows[row_number]
34
34
  end
35
35
 
36
- # @return [true,false] Whether the cursor is visible in the captured terminal
36
+ # @return [true,false] Whether the cursor is visible in the captured terminal.
37
37
  def cursor_visible?
38
38
  @cursor_visible
39
39
  end
40
40
 
41
- # @return [true,false] Whether the cursor is hidden in the captured terminal
41
+ # @return [true,false] Whether the cursor is hidden in the captured terminal.
42
42
  def cursor_hidden?
43
43
  !cursor_visible?
44
44
  end
45
45
 
46
- # @return [Capture] returns self
46
+ # @return [Capture] Returns self
47
47
  def capture
48
48
  self
49
49
  end
50
50
 
51
+ # Prints out the current terminal contents.
51
52
  def print
52
53
  puts "\n#{self}"
53
54
  end
54
55
 
56
+ # Prints out the current terminal contents as an array of strings separated by lines.
55
57
  def print_rows
56
58
  p rows
57
59
  end
58
60
 
59
- # @return [String] All rows of the captured terminal, separated by newlines
61
+ # @return [String] All rows of the captured terminal, separated by newlines.
60
62
  def to_s
61
63
  rows.join("\n")
62
64
  end
@@ -3,33 +3,61 @@
3
3
  module TTYtest
4
4
  # Assertions for ttytest2.
5
5
  module Matchers
6
+ def get_inspection(actual)
7
+ if actual.nil?
8
+ 'nil'
9
+ else
10
+ actual.inspect
11
+ end
12
+ end
13
+
14
+ def get_inspection_bounded(actual, column_start, column_end)
15
+ if actual.nil?
16
+ 'nil'
17
+ else
18
+ actual[column_start, column_end]
19
+ end
20
+ end
21
+
22
+ def validate(row)
23
+ return if @height.nil?
24
+ return unless row >= @height
25
+
26
+ raise MatchError,
27
+ "row is at #{row}, which is greater than set height #{height}, so assertions will fail. If intentional, set height larger or break apart tests.\n
28
+ Entire screen:\n#{self}"
29
+ end
30
+
6
31
  # Asserts the contents of a single row match the value expected
7
32
  # @param [Integer] row_number the row (starting from 0) to test against
8
33
  # @param [String] expected the expected value of the row. Any trailing whitespace is ignored
9
34
  # @raise [MatchError] if the row doesn't match exactly
10
35
  def assert_row(row_number, expected)
36
+ validate(row_number)
11
37
  expected = expected.rstrip
12
38
  actual = row(row_number)
13
39
 
14
- return if actual == expected
40
+ return if !actual.nil? && actual == expected
15
41
 
16
42
  raise MatchError,
17
- "expected row #{row_number} to be #{expected.inspect} but got #{actual.inspect}\nEntire screen:\n#{self}"
18
- end
19
-
20
- def assert_last_row(expected)
43
+ "expected row #{row_number} to be #{expected.inspect} but got #{get_inspection(actual)}\n
44
+ Entire screen:\n#{self}"
21
45
  end
46
+ alias assert_line assert_row
22
47
 
23
48
  # Asserts the specified row is empty
24
49
  # @param [Integer] row_number the row (starting from 0) to test against
25
50
  # @raise [MatchError] if the row isn't empty
26
51
  def assert_row_is_empty(row_number)
52
+ validate(row_number)
27
53
  actual = row(row_number)
28
54
 
29
55
  return if actual == ''
30
56
 
31
- raise MatchError, "expected row #{row_number} to be empty but got #{actual.inspect}\nEntire screen:\n#{self}"
57
+ raise MatchError,
58
+ "expected row #{row_number} to be empty but got #{get_inspection(actual)}\nEntire screen:\n#{self}"
32
59
  end
60
+ alias assert_line_is_empty assert_row_is_empty
33
61
 
34
62
  # Asserts the contents of a single row contains the expected string at a specific position
35
63
  # @param [Integer] row_number the row (starting from 0) to test against
@@ -38,45 +66,52 @@ module TTYtest
38
66
  # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
39
67
  # @raise [MatchError] if the row doesn't match
40
68
  def assert_row_at(row_number, column_start, column_end, expected)
69
+ validate(row_number)
41
70
  expected = expected.rstrip
42
71
  actual = row(row_number)
43
72
  column_end += 1
44
73
 
45
- return if actual[column_start, column_end].eql?(expected)
74
+ return if !actual.nil? && actual[column_start, column_end].eql?(expected)
75
+
76
+ inspection = get_inspection_bounded(actual, column_start, column_end)
46
77
 
47
78
  raise MatchError,
48
79
  "expected row #{row_number} to contain #{expected[column_start,
49
- column_end]} at #{column_start}-#{column_end} and got #{actual[column_start,
50
- column_end]}\nEntire screen:\n#{self}"
80
+ column_end]} at #{column_start}-#{column_end} and got #{inspection}\nEntire screen:\n#{self}"
51
81
  end
82
+ alias assert_line_at assert_row_at
52
83
 
53
84
  # Asserts the contents of a single row contains the value expected
54
85
  # @param [Integer] row_number the row (starting from 0) to test against
55
86
  # @param [String] expected the expected value contained in the row. Any trailing whitespace is ignored
56
87
  # @raise [MatchError] if the row doesn't match
57
88
  def assert_row_like(row_number, expected)
89
+ validate(row_number)
58
90
  expected = expected.rstrip
59
91
  actual = row(row_number)
60
92
 
61
- return if actual.include?(expected)
93
+ return if !actual.nil? && actual.include?(expected)
62
94
 
63
95
  raise MatchError,
64
- "expected row #{row_number} to be like #{expected.inspect} but got #{actual.inspect}\nEntire screen:\n#{self}"
96
+ "expected row #{row_number} to be like #{expected.inspect} but got #{get_inspection(actual)}\nEntire screen:\n#{self}"
65
97
  end
66
98
  alias assert_row_contains assert_row_like
99
+ alias assert_line_contains assert_row_like
100
+ alias assert_line_like assert_row_like
67
101
 
68
102
  # Asserts the contents of a single row starts with expected string
69
103
  # @param [Integer] row_number the row (starting from 0) to test against
70
104
  # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
71
105
  # @raise [MatchError] if the row doesn't match
72
106
  def assert_row_starts_with(row_number, expected)
107
+ validate(row_number)
73
108
  expected = expected.rstrip
74
109
  actual = row(row_number)
75
110
 
76
- return if actual.start_with?(expected)
111
+ return if !actual.nil? && actual.start_with?(expected)
77
112
 
78
113
  raise MatchError,
79
- "expected row #{row_number} to start with #{expected.inspect} and got #{actual.inspect}\nEntire screen:\n#{self}"
114
+ "expected row #{row_number} to start with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
80
115
  end
81
116
 
82
117
  # Asserts the contents of a single row end with expected
@@ -84,13 +119,14 @@ module TTYtest
84
119
  # @param [String] expected the expected value that the row starts with. Any trailing whitespace is ignored
85
120
  # @raise [MatchError] if the row doesn't match
86
121
  def assert_row_ends_with(row_number, expected)
122
+ validate(row_number)
87
123
  expected = expected.rstrip
88
124
  actual = row(row_number)
89
125
 
90
- return if actual.end_with?(expected)
126
+ return if !actual.nil? && actual.end_with?(expected)
91
127
 
92
128
  raise MatchError,
93
- "expected row #{row_number} to end with #{expected.inspect} and got #{actual.inspect}\nEntire screen:\n#{self}"
129
+ "expected row #{row_number} to end with #{expected.inspect} and got #{get_inspection(actual)}\nEntire screen:\n#{self}"
94
130
  end
95
131
 
96
132
  # Asserts the contents of a single row match against the passed in regular expression
@@ -98,13 +134,14 @@ module TTYtest
98
134
  # @param [String] regexp_str the regular expression as a string that will be used to match with.
99
135
  # @raise [MatchError] if the row doesn't match against the regular expression
100
136
  def assert_row_regexp(row_number, regexp_str)
137
+ validate(row_number)
101
138
  regexp = Regexp.new(regexp_str)
102
139
  actual = row(row_number)
103
140
 
104
- return if actual.match?(regexp)
141
+ return if !actual.nil? && actual.match?(regexp)
105
142
 
106
143
  raise MatchError,
107
- "expected row #{row_number} to match regexp #{regexp_str} but it did not. Row value #{actual.inspect}\nEntire screen:\n#{self}"
144
+ "expected row #{row_number} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual)}\nEntire screen:\n#{self}"
108
145
  end
109
146
 
110
147
  # Asserts the contents of a multiple rows each match against the passed in regular expression
@@ -113,14 +150,15 @@ module TTYtest
113
150
  # @param [String] regexp_str the regular expression as a string that will be used to match with.
114
151
  # @raise [MatchError] if the row doesn't match against the regular expression
115
152
  def assert_rows_each_match_regexp(row_start, row_end, regexp_str)
153
+ validate(row_end)
116
154
  regexp = Regexp.new(regexp_str)
117
155
  row_end += 1 if row_end.zero?
118
156
 
119
157
  rows.slice(row_start, row_end).each_with_index do |actual_row, index|
120
- next if actual_row.match?(regexp)
158
+ next if !actual_row.nil? && actual_row.match?(regexp)
121
159
 
122
160
  raise MatchError,
123
- "expected row #{index} to match regexp #{regexp_str} but it did not. Row value #{actual_row.inspect}\nEntire screen:\n#{self}"
161
+ "expected row #{index} to match regexp #{regexp_str} but it did not. Row value #{get_inspection(actual_row)}\nEntire screen:\n#{self}"
124
162
  end
125
163
  end
126
164
 
@@ -135,7 +173,7 @@ module TTYtest
135
173
  return if actual == expected
136
174
 
137
175
  raise MatchError,
138
- "expected cursor to be at #{expected.inspect} but was at #{actual.inspect}\nEntire screen:\n#{self}"
176
+ "expected cursor to be at #{expected.inspect} but was at #{get_inspection(actual)}\nEntire screen:\n#{self}"
139
177
  end
140
178
 
141
179
  # @raise [MatchError] if the cursor is hidden
@@ -188,6 +226,7 @@ module TTYtest
188
226
  # @param [String] expected the expected contents of the terminal at specified rows. Trailing whitespace on each line is ignored
189
227
  # @raise [MatchError] if the terminal doesn't match the expected content
190
228
  def assert_contents_at(row_start, row_end, expected)
229
+ validate(row_end)
191
230
  row_end += 1 if row_end.zero?
192
231
 
193
232
  matched, diff = matched(expected, rows.slice(row_start, row_end))
@@ -5,14 +5,15 @@ require 'ttytest/matchers'
5
5
  require 'ttytest/capture'
6
6
 
7
7
  module TTYtest
8
- # @attr [Integer] max_wait_time the maximum amount of time (in seconds) to retry assertions before failing.
8
+ # @attr [Integer] max_wait_time The maximum amount of time (in seconds) to retry an assertion before failing and raising a MatchError.
9
9
  class Terminal
10
10
  extend Forwardable
11
11
 
12
12
  attr_accessor :max_wait_time
13
13
 
14
14
  # @api private
15
- # @see TTYtest.new_terminal
15
+ # @see TTYtest.new_terminal, use this or other new_* methods instead.
16
+ # Internal constructor.
16
17
  def initialize(driver_terminal)
17
18
  @driver_terminal = driver_terminal
18
19
  @max_wait_time = TTYtest.default_max_wait_time
@@ -29,6 +30,7 @@ module TTYtest
29
30
  # @!method send_line(line)
30
31
  # Simulate sending a line to the terminal and hitting enter.
31
32
  # Can send multiline input, but if you want to send several commands at once, see send_lines(lines)
33
+ # If no newline is at the the line, it will be appended automatically.
32
34
  # @param [String] line the line to send to the terminal
33
35
 
34
36
  # @!method send_line_then_sleep(line, sleep_time)
@@ -38,6 +40,18 @@ module TTYtest
38
40
 
39
41
  # @!method send_lines(lines)
40
42
  # Simulate sending a multiple lines to the terminal and hitting enter after each line.
43
+ # If no newline is at the end of any of the lines, it will be appended automatically.
44
+ # @param [String] lines array of lines to send to the terminal
45
+
46
+ # @!method send_line_exact(line)
47
+ # Simulate sending a line exactly as is to the terminal and hitting enter.
48
+ # Can send multiline input, but if you want to send several commands at once, see send_lines(lines)
49
+ # If no newline is at the the line, it will be appended automatically.
50
+ # @param [String] line the line to send to the terminal
51
+
52
+ # @!method send_lines_exact(lines)
53
+ # Simulate sending a multiple lines exactly as is to the terminal and hitting enter after each line.
54
+ # If no newline is at the end of any of the lines, it will be appended automatically.
41
55
  # @param [String] lines array of lines to send to the terminal
42
56
 
43
57
  # @!method send_lines_then_sleep(lines, sleep_time)
@@ -129,12 +143,13 @@ module TTYtest
129
143
  # @param [Integer] number of times to send escape
130
144
 
131
145
  # @!method capture
132
- # Capture the current state of the terminal
133
- # @return [Capture] instantaneous state of the terminal when called
146
+ # Capture represents the current state of the terminal.
147
+ # @return [Capture] The current state of the terminal when called
134
148
  def_delegators :@driver_terminal,
135
149
  :send_keys, :send_keys_one_at_a_time,
136
150
  :send_line, :send_line_then_sleep,
137
151
  :send_lines, :send_lines_then_sleep, :send_line_then_sleep_and_repeat,
152
+ :send_line_exact, :send_lines_exact,
138
153
  :send_newline, :send_newlines,
139
154
  :send_enter, :send_enters,
140
155
  :send_delete, :send_deletes,
@@ -73,6 +73,17 @@ module TTYtest
73
73
  end
74
74
  end
75
75
 
76
+ def send_line_exact(line)
77
+ send_keys_exact(line)
78
+ send_newline unless line[-1] == '\n'
79
+ end
80
+
81
+ def send_lines_exact(*lines)
82
+ lines.each do |line|
83
+ send_line_exact(line)
84
+ end
85
+ end
86
+
76
87
  def send_lines_then_sleep(*lines, sleep_time)
77
88
  lines.each do |line|
78
89
  send_line(line)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTYtest
4
- VERSION = '1.0.2'
4
+ VERSION = '1.0.4'
5
5
  end
data/lib/ttytest.rb CHANGED
@@ -28,6 +28,7 @@ module TTYtest
28
28
  def_delegators :driver, :new_terminal, :new_default_sh_terminal, :new_sh_terminal
29
29
  end
30
30
 
31
+ # The error type raised when an assertion fails.
31
32
  class MatchError < StandardError; end
32
33
 
33
34
  self.driver = TTYtest::Tmux::Driver.new
data/notes.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  to push new version to github
2
- git tag v1.0.2
2
+ git tag v1.0.4
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.2.gem
7
+ gem push ttytest2-1.0.4.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.0.2
4
+ version: 1.0.4
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-05-11 00:00:00.000000000 Z
11
+ date: 2025-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler