ttytest 0.4.0 → 0.5.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
  SHA1:
3
- metadata.gz: d0175fa2266c1f56f709051162f3419862a24355
4
- data.tar.gz: 949439a6f922cd8b71c8c92e10a9721161c152b7
3
+ metadata.gz: 91adbf5abccf48f1626c908ebbccc8ad13bcc67b
4
+ data.tar.gz: f244bd9c2970b4af1704ac84f6bf1a6fb2b8975e
5
5
  SHA512:
6
- metadata.gz: 8af22aa0fb0f3896c113c7e5cb5d929c82e642c44d93e03f5b75729fc3d1e2a2eb5f796f6e09300f6d21bc4d5bc1b70e6a7c3676e0dd0f5bc4ff7d6e91a52c8d
7
- data.tar.gz: 7871021b99ebef74812db882e8ada619135697c114c6df31c19ed282dec13a155237d6162c467cab6f440887ebbf76d43a93eedd9812d3eef139ca47acf00c65
6
+ metadata.gz: fe2ec46d18232b2e0e16c54d827a8efba1628d114cc4c42a78254a114cfd9eb8e9ec009074d298de95fafb8820a2ee283b816b1cce59ab0f1eea4070b57e0025
7
+ data.tar.gz: 5e23a41408ff1cc4840e33a4049258434b16f17c3614994c4850df1e9adafda0dc9c1cdb57430035b3ebdf1d504b75d43df8301e32a53ebe7d2706ed03fe0f5b
data/.gitignore CHANGED
@@ -1,2 +1,4 @@
1
1
  Gemfile.lock
2
2
  pkg/
3
+ .yardoc/
4
+ doc/
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
- TTYtest is an integration test framework for interactive console applications. It's like [capybara](https://github.com/teamcapybara/capybara) for the terminal.
1
+ TTYtest is an acceptance test framework for interactive console applications. It's like [capybara](https://github.com/teamcapybara/capybara) for the terminal.
2
2
 
3
3
  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.
4
4
 
5
5
  [![Build Status](https://travis-ci.org/jhawthorn/ttytest.svg?branch=master)](https://travis-ci.org/jhawthorn/ttytest)
6
+ [![Gem Version](https://badge.fury.io/rb/ttytest.svg)](https://rubygems.org/gems/ttytest)
6
7
 
7
8
  ## Requirements
8
9
 
@@ -11,6 +12,8 @@ It works by running commands inside a tmux session, capturing the pane, and comp
11
12
 
12
13
  ## Usage
13
14
 
15
+ ### Example
16
+
14
17
  ``` ruby
15
18
  @tty = TTYtest.new_terminal(%{PS1='$ ' /bin/sh}, width: 80, height: 24)
16
19
  @tty.assert_row(0, '$')
@@ -18,8 +21,11 @@ It works by running commands inside a tmux session, capturing the pane, and comp
18
21
 
19
22
  @tty.send_keys(%{echo "Hello, world"\n})
20
23
 
21
- @tty.assert_row(0, '$ echo "Hello, world"')
22
- @tty.assert_row(1, 'Hello, world')
24
+ @tty.assert_contents <<TTY
25
+ $ echo "Hello, world"
26
+ Hello, world
27
+ $
28
+ TTY
23
29
  @tty.assert_cursor_position(x: 2, y: 2)
24
30
 
25
31
  p @tty.rows # => ["$ echo \"Hello, world\"", "Hello, world", "$", "", "", "", ...]
@@ -27,6 +33,17 @@ p @tty.rows # => ["$ echo \"Hello, world\"", "Hello, world", "$", "", "", "", ..
27
33
 
28
34
  See also [fzy's integration test](https://github.com/jhawthorn/fzy/blob/master/test/integration/integration_test.rb) for a full example.
29
35
 
36
+ ### Assertions
37
+
38
+ 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).
39
+
40
+ Available assertions:
41
+ * `assert_row(row_number, expected_text)`
42
+ * `assert_cursor_position(x: x, y: y)`
43
+ * `assert_cursor_visible`
44
+ * `assert_cursor_hidden`
45
+ * `assert_contents(lines_of_terminal)`
46
+
30
47
  ## TravisCI
31
48
 
32
49
  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.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
+ require "yard"
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << "test"
@@ -7,6 +8,10 @@ Rake::TestTask.new(:test) do |t|
7
8
  t.test_files = FileList['test/**/*_test.rb']
8
9
  end
9
10
 
11
+ YARD::Rake::YardocTask.new do |t|
12
+ t.files = ['lib/**/*.rb']
13
+ end
14
+
10
15
  task :console do
11
16
  system 'irb -Ilib -rttytest'
12
17
  end
@@ -8,6 +8,12 @@ module TTYtest
8
8
  attr_accessor :default_max_wait_time
9
9
 
10
10
  extend Forwardable
11
+ # @!method new_terminal(command, width: 80, height: 24)
12
+ # Create a new terminal through the current driver.
13
+ # @param [String] command a valid shell command to run
14
+ # @param [Integer] width width of the new terminal
15
+ # @param [Integer] height height of the new terminal
16
+ # @return [Terminal] a new terminal running the specified command
11
17
  def_delegators :driver, :new_terminal
12
18
  end
13
19
 
@@ -1,10 +1,17 @@
1
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
2
7
  class Capture
3
8
  include TTYtest::Matchers
4
9
 
5
10
  attr_reader :cursor_x, :cursor_y
6
11
  attr_reader :width, :height
7
12
 
13
+ # Used internally by drivers when called by {Terminal#capture}
14
+ # @api private
8
15
  def initialize(contents, cursor_x: 0, cursor_y: 0, width: nil, height: nil, cursor_visible: true)
9
16
  @rows = (contents+"\nEND").split("\n")[0...-1].map do |row|
10
17
  row || ""
@@ -16,26 +23,33 @@ module TTYtest
16
23
  @cursor_visible = cursor_visible
17
24
  end
18
25
 
26
+ # @return [Array<String>] An array of each row's contend from the captured terminal
19
27
  def rows
20
28
  @rows
21
29
  end
22
30
 
23
- def row(row)
24
- rows[row]
31
+ # @param [Integer] the row to return
32
+ # @return [String] the content of the row from the captured terminal
33
+ def row(row_number)
34
+ rows[row_number]
25
35
  end
26
36
 
37
+ # @return [true,false] Whether the cursor is visible in the captured terminal
27
38
  def cursor_visible?
28
39
  @cursor_visible
29
40
  end
30
41
 
42
+ # @return [true,false] Whether the cursor is hidden in the captured terminal
31
43
  def cursor_hidden?
32
44
  !cursor_visible?
33
45
  end
34
46
 
47
+ # @return [Capture] returns self
35
48
  def capture
36
49
  self
37
50
  end
38
51
 
52
+ # @return [String] All rows of the captured terminal, separated by newlines
39
53
  def to_s
40
54
  rows.join("\n")
41
55
  end
@@ -1,12 +1,21 @@
1
1
  module TTYtest
2
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
3
7
  def assert_row(row_number, expected)
8
+ expected = expected.rstrip
4
9
  actual = row(row_number)
5
10
  if actual != expected
6
11
  raise MatchError, "expected row #{row_number} to be #{expected.inspect} but got #{actual.inspect}\nEntire screen:\n#{to_s}"
7
12
  end
8
13
  end
9
14
 
15
+ # Asserts that the cursor is in the expected position
16
+ # @param [Integer] x cursor x (row) position, starting from 0
17
+ # @param [Integer] y cursor y (column) position, starting from 0
18
+ # @raise [MatchError] if the cursor position doesn't match
10
19
  def assert_cursor_position(x:, y:)
11
20
  expected = [x, y]
12
21
  actual = [cursor_x, cursor_y]
@@ -15,19 +24,24 @@ module TTYtest
15
24
  end
16
25
  end
17
26
 
27
+ # @raise [MatchError] if the cursor is hidden
18
28
  def assert_cursor_visible
19
29
  if !cursor_visible?
20
30
  raise MatchError, "expected cursor to be visible was hidden\nEntire screen:\n#{to_s}"
21
31
  end
22
32
  end
23
33
 
34
+ # @raise [MatchError] if the cursor is visible
24
35
  def assert_cursor_hidden
25
36
  if !cursor_hidden?
26
37
  raise MatchError, "expected cursor to be hidden was visible\nEntire screen:\n#{to_s}"
27
38
  end
28
39
  end
29
40
 
30
- def assert_matches(expected)
41
+ # Asserts the full contents of the terminal
42
+ # @param [String] expected the full expected contents of the terminal. Trailing whitespace on each line is ignored
43
+ # @raise [MatchError] if the terminal doesn't match the expected content
44
+ def assert_contents(expected)
31
45
  expected_rows = expected.split("\n")
32
46
  diff = []
33
47
  matched = true
@@ -46,6 +60,7 @@ module TTYtest
46
60
  raise MatchError, "screen did not match expected content:\n--- expected\n+++ actual\n#{diff.join("\n")}"
47
61
  end
48
62
  end
63
+ alias_method :assert_matches, :assert_contents
49
64
 
50
65
  METHODS = public_instance_methods
51
66
  end
@@ -3,20 +3,52 @@ require 'ttytest/matchers'
3
3
  require 'ttytest/capture'
4
4
 
5
5
  module TTYtest
6
+ # @attr [Integer] max_wait_time the maximum amount of time (in seconds) to retry assertions before failing.
6
7
  class Terminal
7
8
  extend Forwardable
8
9
 
10
+ attr_accessor :max_wait_time
11
+
12
+ # @api private
13
+ # @see TTYtest.new_terminal
9
14
  def initialize(driver_terminal, max_wait_time: nil)
10
15
  @driver_terminal = driver_terminal
11
- @max_wait_time = max_wait_time
16
+ @max_wait_time = max_wait_time || TTYtest.default_max_wait_time
12
17
  end
13
18
 
14
- def max_wait_time
15
- @max_wait_time || TTYtest.default_max_wait_time
16
- end
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
17
26
 
18
- def_delegators :@driver_terminal, :send_keys, :send_raw, :capture
19
- def_delegators :capture, :rows, :row, :width, :height, :cursor_visible?, :cursor_hidden?
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?
20
52
 
21
53
  TTYtest::Matchers::METHODS.each do |matcher_name|
22
54
  define_method matcher_name do |*args|
@@ -12,26 +12,33 @@ module TTYtest
12
12
  COMMAND = 'tmux'
13
13
  SOCKET_NAME = 'ttytest'
14
14
  REQUIRED_TMUX_VERSION = '1.8'
15
- CONF_PATH = File.expand_path('../tmux.conf', __FILE__)
15
+ DEFAULT_CONFING_FILE_PATH = File.expand_path('../tmux.conf', __FILE__)
16
16
  SLEEP_INFINITY = 'read x < /dev/fd/1'
17
17
 
18
18
  class TmuxError < StandardError; end
19
19
 
20
- def initialize(debug: false, command: COMMAND, socket_name: SOCKET_NAME)
20
+ def initialize(
21
+ debug: false,
22
+ command: COMMAND,
23
+ socket_name: SOCKET_NAME,
24
+ config_file_path: DEFAULT_CONFING_FILE_PATH
25
+ )
21
26
  @debug = debug
22
27
  @tmux_cmd = command
23
28
  @socket_name = socket_name
29
+ @config_file_path = config_file_path
24
30
  end
25
31
 
26
32
  def new_terminal(cmd, width: 80, height: 24)
27
33
  cmd = "#{cmd}\n#{SLEEP_INFINITY}"
28
34
 
29
35
  session_name = "ttytest-#{SecureRandom.uuid}"
30
- tmux(*%W[-f #{CONF_PATH} new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
36
+ tmux(*%W[-f #{@config_file_path} new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
31
37
  session = Session.new(self, session_name)
32
38
  Terminal.new(session)
33
39
  end
34
40
 
41
+ # @api private
35
42
  def tmux(*args)
36
43
  ensure_available
37
44
  puts "tmux(#{args.inspect[1...-1]})" if debug?
@@ -45,18 +52,18 @@ module TTYtest
45
52
  @available ||= (Gem::Version.new(tmux_version) >= Gem::Version.new(REQUIRED_TMUX_VERSION))
46
53
  end
47
54
 
55
+ private
56
+
48
57
  def debug?
49
58
  @debug
50
59
  end
51
60
 
52
- private
53
-
54
61
  def ensure_available
55
62
  if !available?
56
63
  if !tmux_version
57
- raise TmuxError, "tmux doesn't seem to be unstalled" unless available?
64
+ raise TmuxError, "Running `tmux -V` to determine version failed. Is tmux installed?"
58
65
  else
59
- raise TmuxError, "tmux version #{tmux_version} does not meet requirement >= #{REQUIRED_TMUX_VERSION}" unless available?
66
+ raise TmuxError, "tmux version #{tmux_version} does not meet requirement >= #{REQUIRED_TMUX_VERSION}"
60
67
  end
61
68
  end
62
69
  end
@@ -3,8 +3,7 @@
3
3
  module TTYtest
4
4
  module Tmux
5
5
  class Session
6
- attr_reader :driver, :name
7
-
6
+ # @api private
8
7
  def initialize(driver, name)
9
8
  @driver = driver
10
9
  @name = name
@@ -12,14 +11,20 @@ module TTYtest
12
11
  ObjectSpace.define_finalizer(self, self.class.finalize(driver, name))
13
12
  end
14
13
 
14
+ # @api private
15
15
  def self.finalize(driver, name)
16
16
  proc { driver.tmux(*%W[kill-session -t #{name}]) }
17
17
  end
18
18
 
19
19
  def capture
20
20
  contents = driver.tmux(*%W[capture-pane -t #{name} -p])
21
- str = driver.tmux(*%W[display-message -t #{name} -p #\{cursor_x},#\{cursor_y},#\{cursor_flag},#\{pane_width},#\{pane_height}])
22
- x, y, cursor_flag, width, height = str.split(',')
21
+ str = driver.tmux(*%W[display-message -t #{name} -p #\{cursor_x},#\{cursor_y},#\{cursor_flag},#\{pane_width},#\{pane_height},#\{pane_dead},#\{pane_dead_status},])
22
+ x, y, cursor_flag, width, height, pane_dead, pane_dead_status, _newline = str.split(',')
23
+
24
+ if pane_dead == "1"
25
+ raise Driver::TmuxError, "Tmux pane has died\nCommand exited with status: #{pane_dead_status}\nEntire screen:\n#{contents}"
26
+ end
27
+
23
28
  TTYtest::Capture.new(
24
29
  contents.chomp("\n"),
25
30
  cursor_x: x.to_i,
@@ -33,6 +38,10 @@ module TTYtest
33
38
  def send_keys(*keys)
34
39
  driver.tmux(*%W[send-keys -t #{name} -l], *keys)
35
40
  end
41
+
42
+ private
43
+
44
+ attr_reader :driver, :name
36
45
  end
37
46
  end
38
47
  end
@@ -1 +1,3 @@
1
1
  set -g status off
2
+ set -g remain-on-exit on
3
+ set -g default-shell /bin/sh
@@ -1,3 +1,3 @@
1
1
  module TTYtest
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "bundler", "~> 1.7"
27
27
  spec.add_development_dependency "rake", "~> 10.0"
28
28
  spec.add_development_dependency "minitest", "~> 5.0"
29
+ spec.add_development_dependency "yard"
29
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ttytest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hawthorn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-12-31 00:00:00.000000000 Z
11
+ date: 2017-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.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'
55
69
  description: TTYtest allows running applications inside of a terminal emulator (like
56
70
  tmux) and making assertions on the output.
57
71
  email:
@@ -67,7 +81,6 @@ files:
67
81
  - Rakefile
68
82
  - lib/ttytest.rb
69
83
  - lib/ttytest/capture.rb
70
- - lib/ttytest/dummy.rb
71
84
  - lib/ttytest/matchers.rb
72
85
  - lib/ttytest/terminal.rb
73
86
  - lib/ttytest/tmux/driver.rb
@@ -1,13 +0,0 @@
1
- module TTYtest
2
- class Dummy
3
- attr_accessor :contents
4
-
5
- def initialize
6
- @contents = "\n"*23
7
- end
8
-
9
- def capture
10
- Capture.new(contents)
11
- end
12
- end
13
- end
OSZAR »