easytest 0.8.1 → 0.10.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: baf1f087a7353d74e8aa021313fd2f00c161af6cf5deb96552a5283e368f947f
4
- data.tar.gz: 9ccc8f17a4911d3bc77abdd3916e4344e2965047ffe7d47d6794d88e6d8dd754
3
+ metadata.gz: 8b46bba7f2013706b78807e3ccda09b44711259717bda01d99d3610ee63436cb
4
+ data.tar.gz: 3231effd59cc137da79ce59eeb1db2acb5bccf58d951f69f4f6ab7ee38f58859
5
5
  SHA512:
6
- metadata.gz: 7d907a3edc843501e6d20b408f358ca5cb0ec19b57a6ee7760577bbceff1fa078635209f439c0b5424ee72e2141c4a54ba51a8bb54c6a823406b55fd21246bd3
7
- data.tar.gz: 7c01539bb3762b24f27f5483e86837a05844b8495c6bb9450557d610e978a9011e8c9012c39bb8c7288d7fd6296b9257583298e8c2255cf74492d271b9b83012
6
+ metadata.gz: eabd062a3db5062f77ea92a9316d148709af73e3f49d1ed8100eec9229273cc92a37a95fc6ebe671d487e0ecec4f7c38dc34347deb9a0d14c83a263488886115
7
+ data.tar.gz: 285b5df16fdd009212d0890fb3df87c9a8aba76f1019713060aca6e349beacb91591a6ddacafcf4b5d72bf120348c0017537e140938dcc6e6313d9e883db77f3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.10.0
6
+
7
+ - **Breaking:** Change refinement to `extend` in an anonymous module. The usage should change as follows:
8
+ ```diff
9
+ -using Easytest::DSL
10
+ +extend Easytest::DSL
11
+ ```
12
+ - **Breaking:** Drop support for Ruby 2.7 (End-of-Life on March 31, 2023)
13
+ - Remove extra whitespaces from console output.
14
+
15
+ ## 0.9.0
16
+
17
+ - Add watch mode (`--watch`).
18
+ - Improve type coverage.
19
+
5
20
  ## 0.8.1
6
21
 
7
22
  - Improve type-checking.
data/README.md CHANGED
@@ -27,14 +27,14 @@ 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
 
34
34
  ```ruby
35
35
  require "easytest"
36
36
 
37
- using Easytest::DSL
37
+ extend Easytest::DSL
38
38
 
39
39
  test "addition" do
40
40
  expect(1 + 2).to_eq 2
@@ -54,8 +54,8 @@ $ easytest
54
54
  # test/addition_test.rb:6:in `block in <top (required)>'
55
55
 
56
56
 
57
- Tests: 1 failed, 0 passed, 1 total (1 files)
58
- Time: 0.00087 seconds
57
+ Tests: 1 failed, 0 passed, 1 total (1 files)
58
+ Time: 0.00087 seconds
59
59
  ```
60
60
 
61
61
  Oops. Let's fix the failure:
@@ -71,8 +71,8 @@ Then, run it again:
71
71
  $ easytest
72
72
  PASS test/addition_test.rb
73
73
 
74
- Tests: 1 passed, 1 total (1 files)
75
- Time: 0.00077 seconds
74
+ Tests: 1 passed, 1 total (1 files)
75
+ Time: 0.00077 seconds
76
76
  ```
77
77
 
78
78
  The test now passes! 🎉
@@ -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
+ ```shell
135
+ easytest --watch
136
+ ```
137
+
138
+ This *watch* mode is useful during development.
data/lib/easytest/case.rb CHANGED
@@ -9,7 +9,7 @@ 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
14
  @file = (caller_locations(3, 1)&.first&.absolute_path or raise)
15
15
  @block = block
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,75 @@ 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
- .each { |test_file| load test_file }
115
+ .each { load_test_file(_1) }
116
+ end
117
+
118
+ def load_test_file(file)
119
+ load(file, true)
120
+ end
121
+
122
+ def test_files
123
+ Dir.glob(Easytest.test_files_location).map do |file|
124
+ File.absolute_path(file)
125
+ end
126
+ end
127
+
128
+ def watch?
129
+ options[:watch] == true
130
+ end
131
+
132
+ def watch_files
133
+ require "listen"
134
+ listener = Listen.to(Easytest.test_dir, only: /_test\.rb$/) do |modified, added|
135
+ Easytest.start(no_tests_forbidden: false)
136
+ test_files.intersection(modified + added).each { load_test_file(_1) }
137
+ Easytest.run
138
+ end
139
+ listener.start
140
+
141
+ begin
142
+ puts "Start watching \"#{Easytest.test_files_location}\" changed. Press Ctrl-C to stop."
143
+ sleep
144
+ rescue Interrupt
145
+ puts
146
+ puts "Stopped watching."
147
+ end
97
148
  end
98
149
  end
99
150
  end
data/lib/easytest/dsl.rb CHANGED
@@ -1,32 +1,30 @@
1
1
  module Easytest
2
2
  module DSL
3
- refine Kernel do
4
- def test(name, &block)
5
- Utils.raise_if_no_test_name(name, method: "test")
6
- Easytest.add_case Case.new(name: name, &block)
7
- end
3
+ def test(name, &block)
4
+ Utils.raise_if_no_test_name(name, method: "test")
5
+ Easytest.add_case Case.new(name: name, block: block)
6
+ end
8
7
 
9
- def before(&block)
10
- Easytest.add_hook Hook.new(type: :before, &block)
11
- end
8
+ def before(&block)
9
+ Easytest.add_hook Hook.new(type: :before, block: block)
10
+ end
12
11
 
13
- def after(&block)
14
- Easytest.add_hook Hook.new(type: :after, &block)
15
- end
12
+ def after(&block)
13
+ Easytest.add_hook Hook.new(type: :after, block: block)
14
+ end
16
15
 
17
- def skip(name, &block)
18
- Utils.raise_if_no_test_name(name, method: "skip")
19
- Easytest.add_case Case.new(name: name, skipped: true, &block)
20
- end
16
+ def skip(name, &block)
17
+ Utils.raise_if_no_test_name(name, method: "skip")
18
+ Easytest.add_case Case.new(name: name, skipped: true, block: block)
19
+ end
21
20
 
22
- def only(name, &block)
23
- Utils.raise_if_no_test_name(name, method: "only")
24
- Easytest.add_case Case.new(name: name, only: true, &block)
25
- end
21
+ def only(name, &block)
22
+ Utils.raise_if_no_test_name(name, method: "only")
23
+ Easytest.add_case Case.new(name: name, only: true, block: block)
24
+ end
26
25
 
27
- def expect(actual = nil, &block)
28
- Expectation.new(actual, &block)
29
- end
26
+ def expect(actual = nil, &block)
27
+ Expectation.new(actual, &block)
30
28
  end
31
29
  end
32
30
  end
data/lib/easytest/hook.rb CHANGED
@@ -4,7 +4,7 @@ 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
10
  @file = (caller_locations(3, 1)&.first&.absolute_path or raise)
@@ -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
@@ -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,17 +55,12 @@ 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 ""
81
- puts " #{Rainbow('Tests:').bright} #{summary}"
82
- puts " #{Rainbow('Time:').bright} #{elapsed_time.round(5)} seconds"
62
+ puts "#{Rainbow('Tests:').bright} #{summary}"
63
+ puts "#{Rainbow('Time:').bright} #{elapsed_time.round(5)} seconds"
83
64
  all_passed?
84
65
  end
85
66
  end
@@ -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.1"
2
+ VERSION = "0.10.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
@@ -5,6 +5,24 @@ module Easytest
5
5
 
6
6
  # Define methods for the Easytest DSL.
7
7
  module DSL
8
+ # Define a test case. If omitting a block, the case is considered a to-do.
9
+ def test: (String name) ?{ () -> void } -> void
10
+
11
+ # Run the given block before each test case.
12
+ def before: () { (Easytest::Case case) -> void } -> void
13
+
14
+ # Run the given block after each test case.
15
+ def after: () { (Easytest::Case case) -> void } -> void
16
+
17
+ # Skip the given test case.
18
+ def skip: (String name) { () -> void } -> void
19
+
20
+ # Run only the given test case.
21
+ def only: (String name) { () -> void } -> void
22
+
23
+ # Expect the given object or block to satisfy some criterion.
24
+ def expect: (untyped actual) -> Easytest::Expectation
25
+ | { () -> void } -> Easytest::Expectation
8
26
  end
9
27
 
10
28
  # An expectation in test cases.
@@ -69,24 +87,3 @@ module Easytest
69
87
  attr_reader name: String
70
88
  end
71
89
  end
72
-
73
- module Kernel
74
- # TODO: Can we avoid `RBS::DuplicatedMethodDefinitionError` "::Kernel#test has duplicated definitions"?
75
- # def test: (String name) ?{ () -> void } -> void
76
-
77
- # Run the given block before each test case.
78
- def before: () { (Easytest::Case case) -> void } -> void
79
-
80
- # Run the given block after each test case.
81
- def after: () { (Easytest::Case case) -> void } -> void
82
-
83
- # Skip the given test case.
84
- def skip: (String name) { () -> void } -> void
85
-
86
- # Run only the given test case.
87
- def only: (String name) { () -> void } -> void
88
-
89
- # Expect the given object or block to satisfy some criterion.
90
- def expect: (untyped actual) -> Easytest::Expectation
91
- | { () -> void } -> Easytest::Expectation
92
- end
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.1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masafumi Koba
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-13 00:00:00.000000000 Z
11
+ date: 2023-09-14 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
@@ -73,7 +87,7 @@ metadata:
73
87
  source_code_uri: https://github.com/ybiquitous/easytest
74
88
  changelog_uri: https://github.com/ybiquitous/easytest/blob/main/CHANGELOG.md
75
89
  rubygems_mfa_required: 'true'
76
- post_install_message:
90
+ post_install_message:
77
91
  rdoc_options: []
78
92
  require_paths:
79
93
  - lib
@@ -81,15 +95,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
95
  requirements:
82
96
  - - ">="
83
97
  - !ruby/object:Gem::Version
84
- version: '2.7'
98
+ version: '3.0'
85
99
  required_rubygems_version: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - ">="
88
102
  - !ruby/object:Gem::Version
89
103
  version: '0'
90
104
  requirements: []
91
- rubygems_version: 3.3.7
92
- signing_key:
105
+ rubygems_version: 3.4.10
106
+ signing_key:
93
107
  specification_version: 4
94
108
  summary: Easy testing for Ruby.
95
109
  test_files: []