ttytest2 0.7.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3c87da11ef189a8f5c57cb248f6b141d00d43a64d27f62a2b5d00fb40974c765
4
+ data.tar.gz: 4dbc8af44a477b2c755c9653eb9411946447a295c1186f6f12a709b84dec3ee9
5
+ SHA512:
6
+ metadata.gz: 15737416cb6956c0dc04c07d77ba8970f06a38da74321bb888bb3565d60ef04e28b8b1bf1bf7304af3452a3c480ff32998ffcf339f00848782c5251ad0c3b606
7
+ data.tar.gz: c969a721dc64fec69889482759664b3c7fdba5585292663b470a8903b39336f2e12b6da353f3c40b6080d1c4f1b59e7e35b0c8deb046097dfe59ef5ce1179bc4
@@ -0,0 +1,18 @@
1
+ name: Test
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - name: Set up Ruby 3.2.3
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.2.3
14
+ bundler-cache: true
15
+ - name: Build and test with Rake
16
+ run: |
17
+ bundle install --jobs 4 --retry 3
18
+ bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ Gemfile.lock
2
+ pkg/
3
+ .yardoc/
4
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # ttytest2
2
+
3
+ TTYtest2 is an acceptance test framework for interactive console applications. It's like [capybara](https://github.com/teamcapybara/capybara) for the terminal.
4
+
5
+ Forked from https://github.com/jhawthorn/ttytest, because I had some features I needed for my own project.
6
+
7
+ It works by running commands inside a tmux session, capturing the pane, and comparing the content. The assertions will wait a specified amount of time (default 2 seconds) for the expected content to appear.
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/ttytest.svg)](https://rubygems.org/gems/ttytest)
10
+
11
+ ## Minimum Requirements
12
+
13
+ * tmux >= 1.8
14
+ * Ruby >= 3.2.3
15
+
16
+ ## Usage
17
+
18
+ ### Example
19
+
20
+ ``` ruby
21
+ @tty = TTYtest.new_terminal(%{PS1='$ ' /bin/sh}, width: 80, height: 24)
22
+ @tty.assert_row(0, '$')
23
+ @tty.assert_cursor_position(x: 2, y: 0)
24
+
25
+ @tty.send_keys(%{echo "Hello, world"\n})
26
+
27
+ @tty.assert_contents <<TTY
28
+ $ echo "Hello, world"
29
+ Hello, world
30
+ $
31
+ TTY
32
+ @tty.assert_cursor_position(x: 2, y: 2)
33
+
34
+ p @tty.rows # => ["$ echo \"Hello, world\"", "Hello, world", "$", "", "", "", ...]
35
+ ```
36
+
37
+ ### Assertions
38
+
39
+ The main way to use TTYtest is through assertions. When called on a `TTYtest::Terminal`, each of these will be retried (for up to 2 seconds by default).
40
+
41
+ Available assertions:
42
+ * `assert_row(row_number, expected_text)`
43
+ * `assert_row_like(row_number, expected_text)`
44
+ * `assert_cursor_position(x: x, y: y)`
45
+ * `assert_cursor_visible`
46
+ * `assert_cursor_hidden`
47
+ * `assert_contents(lines_of_terminal)`
48
+
49
+ ## Docker
50
+
51
+ Easy to use from Docker. Add this to your dockerfile to get started.
52
+
53
+ ``` dockerfile
54
+ RUN apt update && \
55
+ apt install gcc make ruby tmux -y && \
56
+ gem install ttytest2
57
+ ```
58
+
59
+ ## TravisCI
60
+
61
+ TTYtest can run on [TravisCI](https://travis-ci.org/), but the version of tmux made available with their default ubuntu 12.04 environment is too old. However the TravisCI ubuntu 14.04 "trusty" image provides tmux 1.8, which works great.
62
+
63
+ Ensure the following is in your `.travis.yml` (see [this project's .travis.yml](./.travis.yml) for an example)
64
+
65
+ ``` yaml
66
+ dist: trusty
67
+ addons:
68
+ apt:
69
+ packages:
70
+ - tmux
71
+ ```
72
+
73
+ ## Contributing
74
+
75
+ Bug reports and pull requests are welcome on GitHub at https://github.com/a-eski/ttytest2.
76
+
77
+ ## License
78
+
79
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'yard'
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << 'test'
7
+ t.libs << 'lib'
8
+ t.test_files = FileList['test/**/*_test.rb']
9
+ end
10
+
11
+ YARD::Rake::YardocTask.new do |t|
12
+ t.files = ['lib/**/*.rb']
13
+ end
14
+
15
+ task :console do
16
+ system 'irb -Ilib -rttytest'
17
+ end
18
+
19
+ task default: :test
@@ -0,0 +1,54 @@
1
+ module TTYtest
2
+ # Represents the complete state of a {TTYtest::Terminal} at the time it was captured (contents, cursor position, etc).
3
+ # @attr_reader [Integer] width the number of columns in the captured terminal
4
+ # @attr_reader [Integer] height the number of rows in the captured terminal
5
+ # @attr_reader [Integer] cursor_x the cursor's column (starting at 0) in the captured terminal
6
+ # @attr_reader [Integer] cursor_y the cursor's row (starting at 0) in the captured terminal
7
+ class Capture
8
+ include TTYtest::Matchers
9
+
10
+ attr_reader :cursor_x, :cursor_y, :width, :height
11
+
12
+ # Used internally by drivers when called by {Terminal#capture}
13
+ # @api private
14
+ def initialize(contents, cursor_x: 0, cursor_y: 0, width: nil, height: nil, cursor_visible: true)
15
+ @rows = "#{contents}\nEND".split("\n")[0...-1].map do |row|
16
+ row || ''
17
+ end
18
+ @cursor_x = cursor_x
19
+ @cursor_y = cursor_y
20
+ @width = width
21
+ @height = height
22
+ @cursor_visible = cursor_visible
23
+ end
24
+
25
+ # @return [Array<String>] An array of each row's contend from the captured terminal
26
+ attr_reader :rows
27
+
28
+ # @param [Integer] the row to return
29
+ # @return [String] the content of the row from the captured terminal
30
+ def row(row_number)
31
+ rows[row_number]
32
+ end
33
+
34
+ # @return [true,false] Whether the cursor is visible in the captured terminal
35
+ def cursor_visible?
36
+ @cursor_visible
37
+ end
38
+
39
+ # @return [true,false] Whether the cursor is hidden in the captured terminal
40
+ def cursor_hidden?
41
+ !cursor_visible?
42
+ end
43
+
44
+ # @return [Capture] returns self
45
+ def capture
46
+ self
47
+ end
48
+
49
+ # @return [String] All rows of the captured terminal, separated by newlines
50
+ def to_s
51
+ rows.join("\n")
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,84 @@
1
+ module TTYtest
2
+ module Matchers
3
+ # Asserts the contents of a single row
4
+ # @param [Integer] row_number the row (starting from 0) to test against
5
+ # @param [String] expected the expected value of the row. Any trailing whitespace is ignored
6
+ # @raise [MatchError] if the row doesn't match
7
+ def assert_row(row_number, expected)
8
+ expected = expected.rstrip
9
+ actual = row(row_number)
10
+ return unless actual != expected
11
+
12
+ raise MatchError,
13
+ "expected row #{row_number} to be #{expected.inspect} but got #{actual.inspect}\nEntire screen:\n#{self}"
14
+ end
15
+
16
+ # Asserts the contents of a single row
17
+ # @param [Integer] row_number the row (starting from 0) to test against
18
+ # @param [String] expected the expected value of the row. Any trailing whitespace is ignored
19
+ # @raise [MatchError] if the row doesn't match
20
+ def assert_row_like(row_number, expected)
21
+ raise MatchError, "expected a value for 'expected' but recieved nil" if actual.nil?
22
+
23
+ expected = expected.rstrip
24
+ actual = row(row_number)
25
+ return if actual.include?(expected)
26
+
27
+ raise MatchError,
28
+ "expected row #{row_number} to be like #{expected.inspect} but got #{actual.inspect}\nEntire screen:\n#{self}"
29
+ end
30
+
31
+ # Asserts that the cursor is in the expected position
32
+ # @param [Integer] x cursor x (row) position, starting from 0
33
+ # @param [Integer] y cursor y (column) position, starting from 0
34
+ # @raise [MatchError] if the cursor position doesn't match
35
+ def assert_cursor_position(x, y)
36
+ expected = [x, y]
37
+ actual = [cursor_x, cursor_y]
38
+ return unless actual != expected
39
+
40
+ raise MatchError,
41
+ "expected cursor to be at #{expected.inspect} but was at #{actual.inspect}\nEntire screen:\n#{self}"
42
+ end
43
+
44
+ # @raise [MatchError] if the cursor is hidden
45
+ def assert_cursor_visible
46
+ return if cursor_visible?
47
+
48
+ raise MatchError, "expected cursor to be visible was hidden\nEntire screen:\n#{self}"
49
+ end
50
+
51
+ # @raise [MatchError] if the cursor is visible
52
+ def assert_cursor_hidden
53
+ return if cursor_hidden?
54
+
55
+ raise MatchError, "expected cursor to be hidden was visible\nEntire screen:\n#{self}"
56
+ end
57
+
58
+ # Asserts the full contents of the terminal
59
+ # @param [String] expected the full expected contents of the terminal. Trailing whitespace on each line is ignored
60
+ # @raise [MatchError] if the terminal doesn't match the expected content
61
+ def assert_contents(expected)
62
+ expected_rows = expected.split("\n")
63
+ diff = []
64
+ matched = true
65
+ rows.each_with_index do |actual_row, index|
66
+ expected_row = (expected_rows[index] || '').rstrip
67
+ if actual_row != expected_row
68
+ diff << "-#{expected_row}"
69
+ diff << "+#{actual_row}"
70
+ matched = false
71
+ else
72
+ diff << " #{actual_row}".rstrip
73
+ end
74
+ end
75
+
76
+ return if matched
77
+
78
+ raise MatchError, "screen did not match expected content:\n--- expected\n+++ actual\n#{diff.join("\n")}"
79
+ end
80
+ alias assert_matches assert_contents
81
+
82
+ METHODS = public_instance_methods
83
+ end
84
+ end
@@ -0,0 +1,75 @@
1
+ require 'forwardable'
2
+ require 'ttytest/matchers'
3
+ require 'ttytest/capture'
4
+
5
+ module TTYtest
6
+ # @attr [Integer] max_wait_time the maximum amount of time (in seconds) to retry assertions before failing.
7
+ class Terminal
8
+ extend Forwardable
9
+
10
+ attr_accessor :max_wait_time
11
+
12
+ # @api private
13
+ # @see TTYtest.new_terminal
14
+ def initialize(driver_terminal, max_wait_time: nil)
15
+ @driver_terminal = driver_terminal
16
+ @max_wait_time = max_wait_time || TTYtest.default_max_wait_time
17
+ end
18
+
19
+ # @!method send_keys(*keys)
20
+ # Simulate typing keys into the terminal
21
+ # @param [String] keys keys to send to the terminal
22
+ # @!method capture
23
+ # Capture the current state of the terminal
24
+ # @return [Capture] instantaneous state of the terminal when called
25
+ def_delegators :@driver_terminal, :send_keys, :capture
26
+
27
+ # @!method rows
28
+ # @return [Array<String>]
29
+ # @see Capture#rows
30
+ # @!method row(row)
31
+ # @return [String]
32
+ # @see Capture#row
33
+ # @!method width
34
+ # @see Capture#width
35
+ # @return [Integer]
36
+ # @!method height
37
+ # @see Capture#height
38
+ # @return [Integer]
39
+ # @!method cursor_x
40
+ # @see Capture#cursor_x
41
+ # @return [Integer]
42
+ # @!method cursor_y
43
+ # @see Capture#cursor_y
44
+ # @return [Integer]
45
+ # @!method cursor_visible?
46
+ # @see Capture#cursor_visible?
47
+ # @return [true,false]
48
+ # @!method cursor_hidden?
49
+ # @see Capture#cursor_hidden?
50
+ # @return [true,false]
51
+ def_delegators :capture, :rows, :row, :width, :height, :cursor_x, :cursor_y, :cursor_visible?, :cursor_hidden?
52
+
53
+ TTYtest::Matchers::METHODS.each do |matcher_name|
54
+ define_method matcher_name do |*args|
55
+ synchronize do
56
+ capture.public_send(matcher_name, *args)
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def synchronize(seconds = max_wait_time)
64
+ start_time = Time.now
65
+ begin
66
+ yield
67
+ rescue MatchError => e
68
+ raise e if (Time.now - start_time) >= seconds
69
+
70
+ sleep 0.05
71
+ retry
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'securerandom'
5
+
6
+ require 'ttytest/terminal'
7
+ require 'ttytest/tmux/session'
8
+
9
+ module TTYtest
10
+ module Tmux
11
+ class Driver
12
+ COMMAND = 'tmux'
13
+ SOCKET_NAME = 'ttytest'
14
+ REQUIRED_TMUX_VERSION = '1.8'
15
+ DEFAULT_CONFING_FILE_PATH = File.expand_path('tmux.conf', __dir__)
16
+ SLEEP_INFINITY = 'read x < /dev/fd/1'
17
+
18
+ class TmuxError < StandardError; end
19
+
20
+ def initialize(
21
+ debug: false,
22
+ command: COMMAND,
23
+ socket_name: SOCKET_NAME,
24
+ config_file_path: DEFAULT_CONFING_FILE_PATH
25
+ )
26
+ @debug = debug
27
+ @tmux_cmd = command
28
+ @socket_name = socket_name
29
+ @config_file_path = config_file_path
30
+ end
31
+
32
+ def new_terminal(cmd, width: 80, height: 24)
33
+ cmd = "#{cmd}\n#{SLEEP_INFINITY}"
34
+
35
+ session_name = "ttytest-#{SecureRandom.uuid}"
36
+ tmux(*%W[-f #{@config_file_path} new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
37
+ session = Session.new(self, session_name)
38
+ Terminal.new(session)
39
+ end
40
+
41
+ # @api private
42
+ def tmux(*args)
43
+ ensure_available
44
+ puts "tmux(#{args.inspect[1...-1]})" if debug?
45
+
46
+ stdout, stderr, status = Open3.capture3(@tmux_cmd, '-L', SOCKET_NAME, *args)
47
+ raise TmuxError, "tmux(#{args.inspect[1...-1]}) failed\n#{stderr}" unless status.success?
48
+
49
+ stdout
50
+ end
51
+
52
+ def available?
53
+ return false unless tmux_version
54
+
55
+ @available ||= (Gem::Version.new(tmux_version) >= Gem::Version.new(REQUIRED_TMUX_VERSION))
56
+ end
57
+
58
+ private
59
+
60
+ def debug?
61
+ @debug
62
+ end
63
+
64
+ def ensure_available
65
+ return if available?
66
+ raise TmuxError, 'Running `tmux -V` to determine version failed. Is tmux installed?' unless tmux_version
67
+
68
+ raise TmuxError, "tmux version #{tmux_version} does not meet requirement >= #{REQUIRED_TMUX_VERSION}"
69
+ end
70
+
71
+ def tmux_version
72
+ @tmux_version ||= `#{@tmux_cmd} -V`[/tmux (\d+.\d+)/, 1]
73
+ rescue Errno::ENOENT
74
+ nil
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ module Tmux
5
+ class Session
6
+ # @api private
7
+ def initialize(driver, name)
8
+ @driver = driver
9
+ @name = name
10
+
11
+ ObjectSpace.define_finalizer(self, self.class.finalize(driver, name))
12
+ end
13
+
14
+ # @api private
15
+ def self.finalize(driver, name)
16
+ proc { driver.tmux(*%W[kill-session -t #{name}]) }
17
+ end
18
+
19
+ def capture
20
+ contents = driver.tmux(*%W[capture-pane -t #{name} -p])
21
+ str = driver.tmux(*%W[display-message -t #{name} -p
22
+ #\{cursor_x},#\{cursor_y},#\{cursor_flag},#\{pane_width},#\{pane_height},#\{pane_dead},#\{pane_dead_status},])
23
+ x, y, cursor_flag, width, height, pane_dead, pane_dead_status, _newline = str.split(',')
24
+
25
+ if pane_dead == '1'
26
+ raise Driver::TmuxError,
27
+ "Tmux pane has died\nCommand exited with status: #{pane_dead_status}\nEntire screen:\n#{contents}"
28
+ end
29
+
30
+ TTYtest::Capture.new(
31
+ contents.chomp("\n"),
32
+ cursor_x: x.to_i,
33
+ cursor_y: y.to_i,
34
+ width: width.to_i,
35
+ height: height.to_i,
36
+ cursor_visible: (cursor_flag != '0')
37
+ )
38
+ end
39
+
40
+ def send_keys(*keys)
41
+ driver.tmux(*%W[send-keys -t #{name} -l], *keys)
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :driver, :name
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ set -g status off
2
+ set -g remain-on-exit on
3
+ set -g default-shell /bin/sh
@@ -0,0 +1,3 @@
1
+ module TTYtest
2
+ VERSION = '0.7.0'.freeze
3
+ end
data/lib/ttytest.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'forwardable'
2
+ require 'ttytest/tmux/driver'
3
+ require 'ttytest/tmux/session'
4
+
5
+ module TTYtest
6
+ class << self
7
+ attr_accessor :driver, :default_max_wait_time
8
+
9
+ extend Forwardable
10
+ # @!method new_terminal(command, width: 80, height: 24)
11
+ # Create a new terminal through the current driver.
12
+ # @param [String] command a valid shell command to run
13
+ # @param [Integer] width width of the new terminal
14
+ # @param [Integer] height height of the new terminal
15
+ # @return [Terminal] a new terminal running the specified command
16
+ def_delegators :driver, :new_terminal
17
+ end
18
+
19
+ class MatchError < StandardError; end
20
+
21
+ self.driver = TTYtest::Tmux::Driver.new
22
+ self.default_max_wait_time = 2
23
+ end
data/notes.txt ADDED
@@ -0,0 +1,7 @@
1
+ to push new version to github,
2
+ git tag v0.7.0
3
+ git push origin --tags
4
+
5
+ to push new version to rubygems.org
6
+ gem build ttytest2.gemspec
7
+ gem push ttytest2-0.7.0.gem
data/ttytest2.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'ttytest/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'ttytest2'
7
+ spec.version = TTYtest::VERSION
8
+ spec.authors = ['Alex Eski']
9
+ spec.email = ['alexeski@gmail.com']
10
+
11
+ spec.summary = 'ttytest2 is an integration test framework for interactive tty applications. Based on TTYtest!'
12
+ spec.description = 'ttytest2 allows running shell and/or cli applications inside of tmux and then making assertions on the output.'
13
+ spec.homepage = 'https://github.com/a-eski/ttytest2'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.required_ruby_version = '>= 3.2.3'
24
+
25
+ spec.add_development_dependency 'bundler'
26
+ spec.add_development_dependency 'minitest', '~> 5.0'
27
+ spec.add_development_dependency 'rake', '~> 13.0'
28
+ spec.add_development_dependency 'yard'
29
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ttytest2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Eski
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-11-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: ttytest2 allows running shell and/or cli applications inside of tmux
70
+ and then making assertions on the output.
71
+ email:
72
+ - alexeski@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".github/workflows/test.yml"
78
+ - ".gitignore"
79
+ - Gemfile
80
+ - README.md
81
+ - Rakefile
82
+ - lib/ttytest.rb
83
+ - lib/ttytest/capture.rb
84
+ - lib/ttytest/matchers.rb
85
+ - lib/ttytest/terminal.rb
86
+ - lib/ttytest/tmux/driver.rb
87
+ - lib/ttytest/tmux/session.rb
88
+ - lib/ttytest/tmux/tmux.conf
89
+ - lib/ttytest/version.rb
90
+ - notes.txt
91
+ - ttytest2.gemspec
92
+ homepage: https://github.com/a-eski/ttytest2
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 3.2.3
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.4.20
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: ttytest2 is an integration test framework for interactive tty applications.
115
+ Based on TTYtest!
116
+ test_files: []