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 +4 -4
- data/README.md +86 -65
- data/lib/ttytest/capture.rb +14 -12
- data/lib/ttytest/matchers.rb +59 -20
- data/lib/ttytest/terminal.rb +19 -4
- data/lib/ttytest/tmux/session.rb +11 -0
- data/lib/ttytest/version.rb +1 -1
- data/lib/ttytest.rb +1 -0
- data/notes.txt +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1878cb00bec4cb2ad2213c6659212cbef7dfc0c1dab31e445e07b1fcbf088e7
|
4
|
+
data.tar.gz: 2298de4d8674e11cf0c23e520176ca3aa9a33ce1ec75e24e712be7a269b79840
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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)
|
5
|
+
ttytest2 is a fork and a drop-in replacement for [ttytest](https://github.com/jhawthorn/ttytest).
|
6
6
|
|
7
|
-
It works by
|
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. [
|
17
|
+
3. [Initializing](#initializing)
|
18
18
|
4. [Assertions](#assertions)
|
19
19
|
5. [Output](#output)
|
20
|
-
6. [
|
20
|
+
6. [Configurables](#configurables)
|
21
21
|
7. [Troubleshooting](#troubleshooting)
|
22
|
-
8. [
|
23
|
-
9. [
|
24
|
-
10. [
|
25
|
-
11. [
|
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
|
-
|
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
|
-
|
96
|
+
## Assertions
|
98
97
|
|
99
|
-
The main way to use
|
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
|
-
|
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
|
-
|
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
|
-
* `
|
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
|
-
* `
|
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(
|
197
|
-
@tty.send_keys_exact(
|
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
|
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
|
|
data/lib/ttytest/capture.rb
CHANGED
@@ -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
|
5
|
-
# @attr_reader [Integer] width
|
6
|
-
# @attr_reader [Integer] height
|
7
|
-
# @attr_reader [Integer] cursor_x
|
8
|
-
# @attr_reader [Integer] cursor_y
|
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
|
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]
|
31
|
-
# @return [String]
|
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]
|
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
|
data/lib/ttytest/matchers.rb
CHANGED
@@ -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
|
18
|
-
|
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,
|
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 #{
|
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
|
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
|
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
|
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
|
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
|
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
|
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))
|
data/lib/ttytest/terminal.rb
CHANGED
@@ -5,14 +5,15 @@ require 'ttytest/matchers'
|
|
5
5
|
require 'ttytest/capture'
|
6
6
|
|
7
7
|
module TTYtest
|
8
|
-
# @attr [Integer] max_wait_time
|
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]
|
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,
|
data/lib/ttytest/tmux/session.rb
CHANGED
@@ -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)
|
data/lib/ttytest/version.rb
CHANGED
data/lib/ttytest.rb
CHANGED
data/notes.txt
CHANGED
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.
|
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-
|
11
|
+
date: 2025-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|