easytest 0.8.0 → 0.9.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
  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