easytest 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a0b3705ffc7039bfb7ac442d22bce3abf4f6cbb4a100b2f88a637b658d462e3
4
- data.tar.gz: f578262b1621be8e67811a048c3ce0b6c1fb592a82c482a0a945d7b97e221f0c
3
+ metadata.gz: 9a953eabbcc6eb08f30507561d26309fd305983f9f6be7f0c2e2e1a1ce3410b5
4
+ data.tar.gz: 86ae0173c242a2069944836dcf48795960f29af50f3bb62c41aae2f079cc86df
5
5
  SHA512:
6
- metadata.gz: fe5a6d95407ebccecd912cc2ef7624f449e304662004db4b361be58ccd0ec0a758ed380c8af5efc6632a1172e7acac8e6361274274fa33d6daa0a835e592affb
7
- data.tar.gz: 406ab02728e1d3b58fa18bae5122f5af0691a72c8aaa5568b4d3d572d750419aaea8af9e2b18b15ab03aa24097b75370d1c16bd5bc220af5956820fb399ada3d
6
+ metadata.gz: aeabfbd63843ff8edb06d2929f65c20c6a748da8f85f2d60b67dc87b7e446776cb76f524ae6ca48aae0601846a9f4da48b1d961f71f32b502697cff9a98e09e5
7
+ data.tar.gz: 87f634738fa05e63a1ac0e544965b15b72b6e66b44eccf806102894318f042eb286890cf1c559815c6681f958ec499e06fcbddae2fdee989c76430a7ff6460c2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.9.0
6
+
7
+ - Add watch mode (`--watch`).
8
+ - Improve type coverage.
9
+
10
+ ## 0.8.1
11
+
12
+ - Improve type-checking.
13
+
5
14
  ## 0.8.0
6
15
 
7
16
  - Add `before` and `after` hooks.
data/README.md CHANGED
@@ -27,7 +27,7 @@ You can read more about Easytest on the [official website](https://ybiquitous.gi
27
27
 
28
28
  ## Usage
29
29
 
30
- Here is a very easy example.
30
+ This section explains easy usage.
31
31
 
32
32
  First, put `test/addition_test.rb` as below:
33
33
 
@@ -126,3 +126,13 @@ test "addition"
126
126
  ```
127
127
 
128
128
  To-do cases will be reported as "todo".
129
+
130
+ ### Watch
131
+
132
+ If you want to run tests immediately when changing code, specify the `--watch` option:
133
+
134
+ ```console
135
+ $ easytest --watch
136
+ ```
137
+
138
+ This *watch* mode is useful during development.
data/lib/easytest/case.rb CHANGED
@@ -9,24 +9,20 @@ module Easytest
9
9
  alias skipped? skipped
10
10
  alias only? only
11
11
 
12
- def initialize(name:, skipped: false, only: false, &block)
12
+ def initialize(name:, skipped: false, only: false, block: nil)
13
13
  @name = name
14
- @file = caller_locations(3, 1).first.absolute_path
14
+ @file = (caller_locations(3, 1)&.first&.absolute_path or raise)
15
15
  @block = block
16
16
  @skipped = skipped
17
17
  @only = only
18
18
  end
19
19
 
20
- def todo?
21
- block.nil?
22
- end
23
-
24
20
  def skip!
25
21
  @skipped = true
26
22
  end
27
23
 
28
24
  def run
29
- return [:todo, Reporter.new(name).report_todo] if todo?
25
+ return [:todo, Reporter.new(name).report_todo] unless block
30
26
  return [:skipped, Reporter.new(name).report_skip] if skipped?
31
27
 
32
28
  block.call
data/lib/easytest/cli.rb CHANGED
@@ -8,9 +8,14 @@ module Easytest
8
8
  FATAL = 2
9
9
 
10
10
  def run
11
- exit_code = parse_options
11
+ exit_code = parse_argv
12
12
  return exit_code if exit_code
13
13
 
14
+ if watch?
15
+ watch_files
16
+ return SUCCESS
17
+ end
18
+
14
19
  Easytest.start
15
20
  setup
16
21
  successful = Easytest.run
@@ -21,33 +26,41 @@ module Easytest
21
26
 
22
27
  attr_reader :argv
23
28
  attr_reader :start_time
29
+ attr_reader :options
30
+ attr_reader :parser
24
31
 
25
32
  def initialize(argv)
26
33
  @argv = argv
27
34
  @start_time = Time.now
35
+ @options = {}
36
+ @parser = init_parser
28
37
  end
29
38
 
30
- def parse_options
31
- options = {}
39
+ def init_parser
40
+ OptionParser.new do |op|
41
+ op.program_name = "easytest"
42
+ op.version = "#{op.program_name} #{Easytest::VERSION}"
32
43
 
33
- parser = OptionParser.new do |p|
34
- p.program_name = "easytest"
35
- p.version = "#{p.program_name} #{Easytest::VERSION}"
44
+ op.on "-w", "--watch" do
45
+ options[:watch] = true
46
+ end
36
47
 
37
- p.on "--help" do
48
+ op.on "--help" do
38
49
  options[:help] = true
39
50
  end
40
51
 
41
- p.on "--version" do
52
+ op.on "--version" do
42
53
  options[:version] = true
43
54
  end
44
55
  end
56
+ end
45
57
 
58
+ def parse_argv
46
59
  begin
47
60
  parser.parse!(argv)
48
61
 
49
62
  if options[:help]
50
- puts help(parser)
63
+ puts help
51
64
  return SUCCESS
52
65
  end
53
66
 
@@ -63,37 +76,73 @@ module Easytest
63
76
  end
64
77
  end
65
78
 
66
- def help(parser)
79
+ def help
80
+ program = Rainbow(parser.program_name).green
81
+ heading = ->(text) { Rainbow(text).bright }
82
+ secondary = ->(text) { Rainbow(text).dimgray }
83
+ option = ->(text) { Rainbow(text).yellow }
84
+ prompt = ->() { Rainbow("$").cyan }
85
+
67
86
  <<~MSG
68
- #{Rainbow("USAGE").bright}
69
- #{parser.program_name} [options] [<file, directory, or pattern>...]
87
+ #{heading["USAGE"]}
88
+ #{program} [options] [<file, directory, or pattern>...]
70
89
 
71
- #{Rainbow("OPTIONS").bright}
72
- --help Show help
73
- --version Show version
90
+ #{heading["OPTIONS"]}
91
+ #{option["-w, --watch"]} Watch file changes and rerun test
92
+ #{option["--help"]} Show help
93
+ #{option["--version"]} Show version
74
94
 
75
- #{Rainbow("EXAMPLES").bright}
76
- # Run all tests (test/**/*_test.rb)
77
- $ easytest
95
+ #{heading["EXAMPLES"]}
96
+ #{secondary["# Run all tests (test/**/*_test.rb)"]}
97
+ #{prompt[]} #{program}
78
98
 
79
- # Run only test files
80
- $ easytest test/example_test.rb
99
+ #{secondary["# Run only test files"]}
100
+ #{prompt[]} #{program} test/example_test.rb
81
101
 
82
- # Run only test files in specified directories
83
- $ easytest test/example
102
+ #{secondary["# Run only test files in given directories"]}
103
+ #{prompt[]} #{program} test/example
84
104
 
85
- # Run only test files that matches specified patterns
86
- $ easytest example
105
+ #{secondary["# Run only test files that matches given patterns"]}
106
+ #{prompt[]} #{program} example
87
107
  MSG
88
108
  end
89
109
 
90
110
  def setup
91
- Dir.glob(Easytest.test_files_location)
92
- .map { |file| File.absolute_path(file) }
111
+ test_files
93
112
  .filter do |file|
94
113
  argv.empty? || argv.any? { |pattern| file.include?(pattern) }
95
114
  end
96
115
  .each { |test_file| load test_file }
97
116
  end
117
+
118
+ def test_files
119
+ Dir.glob(Easytest.test_files_location).map do |file|
120
+ File.absolute_path(file)
121
+ end
122
+ end
123
+
124
+ def watch?
125
+ options[:watch] == true
126
+ end
127
+
128
+ def watch_files
129
+ require "listen"
130
+ listener = Listen.to(Easytest.test_dir, only: /_test\.rb$/) do |modified, added|
131
+ Easytest.start(no_tests_forbidden: false)
132
+ test_files.intersection(modified + added).each do |file|
133
+ load(file)
134
+ end
135
+ Easytest.run
136
+ end
137
+ listener.start
138
+
139
+ begin
140
+ puts "Start watching \"#{Easytest.test_files_location}\" changed. Press Ctrl-C to stop."
141
+ sleep
142
+ rescue Interrupt
143
+ puts
144
+ puts "Stopped watching."
145
+ end
146
+ end
98
147
  end
99
148
  end
data/lib/easytest/dsl.rb CHANGED
@@ -3,25 +3,25 @@ module Easytest
3
3
  refine Kernel do
4
4
  def test(name, &block)
5
5
  Utils.raise_if_no_test_name(name, method: "test")
6
- Easytest.add_case Case.new(name: name, &block)
6
+ Easytest.add_case Case.new(name: name, block: block)
7
7
  end
8
8
 
9
9
  def before(&block)
10
- Easytest.add_hook Hook.new(type: :before, &block)
10
+ Easytest.add_hook Hook.new(type: :before, block: block)
11
11
  end
12
12
 
13
13
  def after(&block)
14
- Easytest.add_hook Hook.new(type: :after, &block)
14
+ Easytest.add_hook Hook.new(type: :after, block: block)
15
15
  end
16
16
 
17
17
  def skip(name, &block)
18
18
  Utils.raise_if_no_test_name(name, method: "skip")
19
- Easytest.add_case Case.new(name: name, skipped: true, &block)
19
+ Easytest.add_case Case.new(name: name, skipped: true, block: block)
20
20
  end
21
21
 
22
22
  def only(name, &block)
23
23
  Utils.raise_if_no_test_name(name, method: "only")
24
- Easytest.add_case Case.new(name: name, only: true, &block)
24
+ Easytest.add_case Case.new(name: name, only: true, block: block)
25
25
  end
26
26
 
27
27
  def expect(actual = nil, &block)
data/lib/easytest/hook.rb CHANGED
@@ -4,10 +4,10 @@ module Easytest
4
4
  attr_reader :type
5
5
  attr_reader :block
6
6
 
7
- def initialize(type:, &block)
7
+ def initialize(type:, block:)
8
8
  raise ArgumentError, "" unless [:before, :after].include?(type)
9
9
 
10
- @file = caller_locations(3, 1).first.absolute_path
10
+ @file = (caller_locations(3, 1)&.first&.absolute_path or raise)
11
11
  @type = type
12
12
  @block = block
13
13
  end
@@ -22,6 +22,8 @@ module Easytest
22
22
  raise_match_error unless matched
23
23
  end
24
24
 
25
+ private
26
+
25
27
  def message
26
28
  raise NotImplementedError
27
29
  end
@@ -2,7 +2,7 @@ module Easytest
2
2
  module Matcher
3
3
  class ContainExactly < Base
4
4
  def match?
5
- (actual.to_a - expected).empty?
5
+ (Array(actual) - Array(expected)).empty?
6
6
  end
7
7
 
8
8
  def message
@@ -3,9 +3,9 @@ module Easytest
3
3
  class Raise < Base
4
4
  attr_reader :with_message
5
5
 
6
- def initialize(**kwargs)
7
- @with_message = kwargs.delete(:with_message)
8
- super(**kwargs)
6
+ def initialize(actual:, expected:, negate:, with_message:)
7
+ super(actual: actual, expected: expected, negate: negate)
8
+ @with_message = with_message
9
9
  end
10
10
 
11
11
  def match?
@@ -51,14 +51,15 @@ module Easytest
51
51
  end
52
52
 
53
53
  def find_location(error)
54
- location = error.backtrace_locations.find do |loc|
55
- loc.path.end_with?("_test.rb")
54
+ location = error.backtrace_locations&.find do |loc|
55
+ loc.path&.end_with?("_test.rb")
56
56
  end
57
57
  location or raise "Not found test location from #{error.inspect}"
58
58
  end
59
59
 
60
60
  def format_location(location)
61
- path = Pathname(location.absolute_path).relative_path_from(Dir.pwd)
61
+ absolute_path = location.absolute_path or raise
62
+ path = Pathname(absolute_path).relative_path_from(Dir.pwd)
62
63
  "# #{path}:#{location.lineno}:in `#{location.label}'"
63
64
  end
64
65
  end
@@ -1,21 +1,5 @@
1
1
  module Easytest
2
2
  class Runner
3
- attr_reader :start_time
4
- attr_accessor :passed_count
5
- attr_accessor :failed_count
6
- attr_accessor :skipped_count
7
- attr_accessor :todo_count
8
- attr_accessor :file_count
9
-
10
- def initialize(start_time: Time.now)
11
- @start_time = start_time
12
- @passed_count = 0
13
- @failed_count = 0
14
- @skipped_count = 0
15
- @todo_count = 0
16
- @file_count = 0
17
- end
18
-
19
3
  def run
20
4
  include_only_case = cases.any?(&:only?)
21
5
  hooks_by_file = hooks.group_by(&:file)
@@ -27,6 +11,7 @@ module Easytest
27
11
  before_hooks = hooks.filter(&:before?)
28
12
  after_hooks = hooks.filter(&:after?)
29
13
 
14
+ # @type var reports: Array[[Symbol, String]]
30
15
  reports = []
31
16
 
32
17
  cases_per_file.each do |c|
@@ -34,12 +19,13 @@ module Easytest
34
19
  c.skip!
35
20
  end
36
21
 
37
- begin
38
- before_hooks.each { |hook| hook.run(c) }
39
- result, report = c.run
40
- ensure
41
- after_hooks.each { |hook| hook.run(c) }
42
- end
22
+ result, report =
23
+ begin
24
+ before_hooks.each { |hook| hook.run(c) }
25
+ c.run
26
+ ensure
27
+ after_hooks.each { |hook| hook.run(c) }
28
+ end
43
29
 
44
30
  case result
45
31
  when :passed
@@ -69,12 +55,7 @@ module Easytest
69
55
  end
70
56
 
71
57
  if no_tests?
72
- $stderr.puts <<~MSG
73
- #{Rainbow("Oops. No tests found!").red.bright}
74
-
75
- #{Rainbow("Put `#{Easytest.test_files_location}` files to include at least one test case.").red}
76
- #{Rainbow("Or specify a pattern that matches an existing test file.").red}
77
- MSG
58
+ print_error_for_no_tests if no_tests_forbidden?
78
59
  false
79
60
  else
80
61
  puts ""
@@ -102,6 +83,26 @@ module Easytest
102
83
 
103
84
  private
104
85
 
86
+ attr_reader :start_time
87
+ attr_reader :no_tests_forbidden
88
+ alias no_tests_forbidden? no_tests_forbidden
89
+
90
+ attr_accessor :passed_count
91
+ attr_accessor :failed_count
92
+ attr_accessor :skipped_count
93
+ attr_accessor :todo_count
94
+ attr_accessor :file_count
95
+
96
+ def initialize(start_time: Time.now, no_tests_forbidden: true)
97
+ @start_time = start_time
98
+ @no_tests_forbidden = no_tests_forbidden
99
+ @passed_count = 0
100
+ @failed_count = 0
101
+ @skipped_count = 0
102
+ @todo_count = 0
103
+ @file_count = 0
104
+ end
105
+
105
106
  def total_count
106
107
  passed_count + failed_count + skipped_count + todo_count
107
108
  end
@@ -114,6 +115,15 @@ module Easytest
114
115
  total_count == 0
115
116
  end
116
117
 
118
+ def print_error_for_no_tests
119
+ $stderr.puts <<~MSG
120
+ #{Rainbow("Oops. No tests found!").red.bright}
121
+
122
+ #{Rainbow("Put `#{Easytest.test_files_location}` files to include at least one test case.").red}
123
+ #{Rainbow("Or specify a pattern that matches an existing test file.").red}
124
+ MSG
125
+ end
126
+
117
127
  def print_reports(reports)
118
128
  unexecuted = [:skipped, :todo]
119
129
  indent = " "
@@ -133,6 +143,7 @@ module Easytest
133
143
  end
134
144
 
135
145
  def summary
146
+ # @type var list: Array[String]
136
147
  list = []
137
148
 
138
149
  if failed_count > 0
@@ -1,3 +1,3 @@
1
1
  module Easytest
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
data/lib/easytest.rb CHANGED
@@ -32,8 +32,8 @@ require_relative "easytest/matcher/satisfy"
32
32
  require_relative "easytest/matcher/true"
33
33
 
34
34
  module Easytest
35
- def self.start
36
- @runner = Runner.new
35
+ def self.start(no_tests_forbidden: true)
36
+ @runner = Runner.new(no_tests_forbidden: no_tests_forbidden)
37
37
  end
38
38
 
39
39
  def self.add_case(new_case)
@@ -48,7 +48,11 @@ module Easytest
48
48
  @runner.run
49
49
  end
50
50
 
51
+ def self.test_dir
52
+ "test"
53
+ end
54
+
51
55
  def self.test_files_location
52
- "test/**/*_test.rb"
56
+ "#{test_dir}/**/*_test.rb"
53
57
  end
54
58
  end
data/sig/easytest.rbs CHANGED
@@ -9,8 +9,8 @@ module Easytest
9
9
 
10
10
  # An expectation in test cases.
11
11
  class Expectation
12
- # Negate an expectation.
13
- def not: () -> instance
12
+ # Negate this expectation.
13
+ def not: () -> Expectation
14
14
 
15
15
  # Expect to equal the given object.
16
16
  def to_eq: (untyped expected) -> void
@@ -56,7 +56,7 @@ module Easytest
56
56
  def to_satisfy: () { (untyped actual) -> bool } -> void
57
57
 
58
58
  # Expect to raise the given exception or an exception with the given message.
59
- def to_raise: (Class expected, ?(String | Regexp) with_message) -> void
59
+ def to_raise: (Class expected, ?(String | Regexp)? with_message) -> void
60
60
  | ((String | Regexp) expected) -> void
61
61
 
62
62
  # Expect to raise nothing.
@@ -65,8 +65,8 @@ module Easytest
65
65
 
66
66
  # A test case.
67
67
  class Case
68
- # Return a test case name.
69
- def name: () -> String
68
+ # A test case name.
69
+ attr_reader name: String
70
70
  end
71
71
  end
72
72
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easytest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masafumi Koba
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-12 00:00:00.000000000 Z
11
+ date: 2022-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: listen
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.7'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rainbow
15
29
  requirement: !ruby/object:Gem::Requirement