hanami-minitest 3.0.0.rc1

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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +30 -0
  3. data/LICENSE +20 -0
  4. data/README.md +183 -0
  5. data/hanami-minitest.gemspec +39 -0
  6. data/lib/hanami/minitest/commands.rb +167 -0
  7. data/lib/hanami/minitest/feature_test.rb +31 -0
  8. data/lib/hanami/minitest/generators/action.rb +61 -0
  9. data/lib/hanami/minitest/generators/mailer.rb +62 -0
  10. data/lib/hanami/minitest/generators/operation.rb +61 -0
  11. data/lib/hanami/minitest/generators/part.rb +122 -0
  12. data/lib/hanami/minitest/generators/slice.rb +47 -0
  13. data/lib/hanami/minitest/generators/templates/gemfile +5 -0
  14. data/lib/hanami/minitest/generators/templates/gemfile_db +8 -0
  15. data/lib/hanami/minitest/generators/templates/gitignore +1 -0
  16. data/lib/hanami/minitest/generators/templates/helper.rb +11 -0
  17. data/lib/hanami/minitest/generators/templates/request.rb +13 -0
  18. data/lib/hanami/minitest/generators/templates/support_db.rb +18 -0
  19. data/lib/hanami/minitest/generators/templates/support_db_cleaning.rb +71 -0
  20. data/lib/hanami/minitest/generators/templates/support_features.rb +5 -0
  21. data/lib/hanami/minitest/generators/templates/support_minitest.rb +5 -0
  22. data/lib/hanami/minitest/generators/templates/support_operations.rb +8 -0
  23. data/lib/hanami/minitest/generators/templates/support_requests.rb +5 -0
  24. data/lib/hanami/minitest/rake_tasks.rb +13 -0
  25. data/lib/hanami/minitest/request_test.rb +27 -0
  26. data/lib/hanami/minitest/test.rb +96 -0
  27. data/lib/hanami/minitest/version.rb +10 -0
  28. data/lib/hanami/minitest.rb +60 -0
  29. data/lib/hanami-minitest.rb +12 -0
  30. metadata +145 -0
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/ruby_file_generator"
4
+
5
+ module Hanami
6
+ module Minitest
7
+ module Generators
8
+ # @since 2.1.0
9
+ # @api private
10
+ class Part
11
+ # @since 2.1.0
12
+ # @api private
13
+ def initialize(fs:, inflector:)
14
+ @fs = fs
15
+ @inflector = inflector
16
+ end
17
+
18
+ # @since 2.1.0
19
+ # @api private
20
+ def call(app, slice, name)
21
+ camelized_app_name = inflector.camelize(app)
22
+ camelized_slice_name = slice ? inflector.camelize(slice) : nil
23
+ camelized_name = inflector.camelize(name)
24
+ underscored_name = inflector.underscore(name)
25
+
26
+ if slice
27
+ generate_for_slice(slice, camelized_app_name, camelized_slice_name, camelized_name, underscored_name)
28
+ else
29
+ generate_for_app(camelized_app_name, camelized_name, underscored_name)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # @since 2.1.0
36
+ # @api private
37
+ attr_reader :fs, :inflector
38
+
39
+ # @since 2.1.0
40
+ # @api private
41
+ def generate_for_slice(slice, camelized_app_name, camelized_slice_name, camelized_name, underscored_name)
42
+ generate_base_part_for_app(camelized_app_name)
43
+ generate_base_part_for_slice(slice, camelized_slice_name)
44
+
45
+ fs.write(
46
+ "test/slices/#{slice}/views/parts/#{underscored_name}_test.rb",
47
+ part_test_content(camelized_slice_name, camelized_name)
48
+ )
49
+ end
50
+
51
+ # @since 2.1.0
52
+ # @api private
53
+ def generate_for_app(camelized_app_name, camelized_name, underscored_name)
54
+ generate_base_part_for_app(camelized_app_name)
55
+
56
+ fs.write(
57
+ "test/views/parts/#{underscored_name}_test.rb",
58
+ part_test_content(camelized_app_name, camelized_name)
59
+ )
60
+ end
61
+
62
+ # @since 2.1.0
63
+ # @api private
64
+ def generate_base_part_for_app(camelized_app_name)
65
+ path = fs.join("test", "views", "part_test.rb")
66
+ return if fs.exist?(path)
67
+
68
+ fs.write(path, base_part_test_content(camelized_app_name))
69
+ end
70
+
71
+ # @since 2.1.0
72
+ # @api private
73
+ def generate_base_part_for_slice(slice, camelized_slice_name)
74
+ path = "test/slices/#{slice}/views/part_test.rb"
75
+ return if fs.exist?(path)
76
+
77
+ fs.write(path, base_part_test_content(camelized_slice_name))
78
+ end
79
+
80
+ # @since 2.1.0
81
+ # @api private
82
+ def base_part_test_content(camelized_namespace)
83
+ Hanami::CLI::RubyFileGenerator.class(
84
+ "PartTest",
85
+ parent_class_name: "Hanami::Minitest::Test",
86
+ header: ["# frozen_string_literal: true", "", 'require "test_helper"'],
87
+ body: [
88
+ "setup do",
89
+ " @value = Object.new",
90
+ " @subject = #{camelized_namespace}::Views::Part.new(value: @value)",
91
+ "end",
92
+ "",
93
+ 'test "works" do',
94
+ " assert_kind_of #{camelized_namespace}::Views::Part, @subject",
95
+ "end"
96
+ ]
97
+ )
98
+ end
99
+
100
+ # @since 2.1.0
101
+ # @api private
102
+ def part_test_content(camelized_namespace, camelized_name)
103
+ Hanami::CLI::RubyFileGenerator.class(
104
+ "#{camelized_name}Test",
105
+ parent_class_name: "Hanami::Minitest::Test",
106
+ header: ["# frozen_string_literal: true", "", 'require "test_helper"'],
107
+ body: [
108
+ "setup do",
109
+ " @value = Object.new",
110
+ " @subject = #{camelized_namespace}::Views::Parts::#{camelized_name}.new(value: @value)",
111
+ "end",
112
+ "",
113
+ 'test "works" do',
114
+ " assert_kind_of #{camelized_namespace}::Views::Parts::#{camelized_name}, @subject",
115
+ "end"
116
+ ]
117
+ )
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/ruby_file_generator"
4
+
5
+ module Hanami
6
+ module Minitest
7
+ module Generators
8
+ # @since 2.0.0
9
+ # @api private
10
+ class Slice
11
+ # @since 2.0.0
12
+ # @api private
13
+ def initialize(fs:, inflector:)
14
+ @fs = fs
15
+ @inflector = inflector
16
+ end
17
+
18
+ # @since 2.0.0
19
+ # @api private
20
+ def call(slice)
21
+ camelized_slice_name = inflector.camelize(slice)
22
+
23
+ fs.write(
24
+ "test/slices/#{slice}/action_test.rb",
25
+ Hanami::CLI::RubyFileGenerator.class(
26
+ "ActionTest",
27
+ parent_class_name: "Hanami::Minitest::Test",
28
+ modules: [camelized_slice_name],
29
+ header: ["# frozen_string_literal: true", "", 'require "test_helper"'],
30
+ body: [
31
+ "test \"pending\" do",
32
+ " skip \"Add tests for #{camelized_slice_name} actions\"",
33
+ "end"
34
+ ]
35
+ )
36
+ )
37
+
38
+ fs.write("test/slices/#{slice}/actions/.keep", "")
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :fs, :inflector
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ group :test do
2
+ # Web integration
3
+ gem "capybara"
4
+ gem "rack-test"
5
+ end
@@ -0,0 +1,8 @@
1
+ group :test do
2
+ # Database
3
+ gem "database_cleaner-sequel"
4
+
5
+ # Web integration
6
+ gem "capybara"
7
+ gem "rack-test"
8
+ end
@@ -0,0 +1 @@
1
+ .test_results/
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ TEST_ROOT = Pathname(__dir__).realpath.freeze
5
+
6
+ ENV["HANAMI_ENV"] ||= "test"
7
+ require "hanami/minitest"
8
+ require "hanami/prepare"
9
+
10
+ require_relative "support/minitest"
11
+ TEST_ROOT.glob("support/**/*.rb").each { |f| require f }
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class RootTest < Hanami::Minitest::RequestTest
6
+ test "not found" do
7
+ get "/"
8
+
9
+ # Generate new action via:
10
+ # `bundle exec hanami generate action home.index --url=/`
11
+ assert_equal 404, last_response.status
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "features"
4
+ require_relative "db/cleaning"
5
+
6
+ module TestSupport
7
+ module DB
8
+ def self.included(mod)
9
+ mod.include DB::Cleaning
10
+ end
11
+
12
+ # Add helper methods to be used by DB tests here.
13
+ end
14
+ end
15
+
16
+ class FeatureTest
17
+ include TestSupport::DB
18
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "database_cleaner/sequel"
4
+
5
+ module TestSupport
6
+ module DB
7
+ module Cleaning
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def db_cleaning_with_truncation!
14
+ @db_cleaning_with_truncation = true
15
+ end
16
+
17
+ def js! = db_cleaning_with_truncation!
18
+ end
19
+
20
+ def setup
21
+ # Clean all databases before the first test
22
+ Cleaning.once do
23
+ Cleaning.all_databases.each do |db|
24
+ DatabaseCleaner[:sequel, db: db].clean_with :truncation, except: ["schema_migrations"]
25
+ end
26
+ end
27
+
28
+ use_truncation = self.class.instance_variable_get(:@db_cleaning_with_truncation)
29
+ strategy = use_truncation ? :truncation : :transaction
30
+
31
+ Cleaning.all_databases.each do |db|
32
+ DatabaseCleaner[:sequel, db: db].strategy = strategy
33
+ DatabaseCleaner[:sequel, db: db].start
34
+ end
35
+
36
+ super
37
+ end
38
+
39
+ def teardown
40
+ Cleaning.all_databases.each do |db|
41
+ DatabaseCleaner[:sequel, db: db].clean
42
+ end
43
+
44
+ super
45
+ end
46
+
47
+ class << self
48
+ def once
49
+ @cleaned_once ||= false
50
+ return if @cleaned_once
51
+
52
+ yield
53
+
54
+ @cleaned_once = true
55
+ end
56
+
57
+ def all_databases
58
+ @all_databases ||= begin
59
+ slices = [Hanami.app] + Hanami.app.slices.with_nested
60
+
61
+ slices.each_with_object([]) { |slice, dbs|
62
+ next unless slice.key?("db.rom")
63
+
64
+ dbs.concat slice["db.rom"].gateways.values.map(&:connection)
65
+ }.uniq
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hanami::Minitest::FeatureTest
4
+ # Add custom feature test helpers here.
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hanami::Minitest::Test
4
+ # Add helper methods to be used by all tests here.
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/monads"
4
+
5
+ class Hanami::Minitest::Test
6
+ # Provide `Success` and `Failure` for testing operation results.
7
+ include Dry::Monads[:result]
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hanami::Minitest::RequestTest
4
+ # Add custom request test helpers here.
5
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ Hanami::CLI::RakeTasks.register_tasks do
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
13
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require_relative "test"
5
+
6
+ module Hanami
7
+ module Minitest
8
+ # Base test class for request tests in Hanami applications.
9
+ #
10
+ # Includes Rack::Test::Methods for making HTTP requests to the application.
11
+ #
12
+ # @since 2.0.0
13
+ # @api public
14
+ class RequestTest < Test
15
+ include Rack::Test::Methods
16
+
17
+ # Defines the app for Rack::Test requests.
18
+ #
19
+ # @return [Hanami::Application] the Hanami application
20
+ # @since 2.0.0
21
+ # @api public
22
+ def app
23
+ Hanami.app
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+
5
+ module Hanami
6
+ module Minitest
7
+ # Base test class for Hanami applications.
8
+ #
9
+ # @since 2.0.0
10
+ # @api public
11
+ class Test < ::Minitest::Test
12
+ # Provides class-level block syntax for defining tests, setup, and teardown.
13
+ #
14
+ # @since 2.2.0
15
+ # @api public
16
+ module ClassMethods
17
+ # Defines a test method using a block.
18
+ #
19
+ # @param desc [String] the test description
20
+ # @yieldparam block the test body
21
+ #
22
+ # @example
23
+ # test "it does something" do
24
+ # assert true
25
+ # end
26
+ #
27
+ # @since 2.2.0
28
+ # @api public
29
+ def test(desc = "anonymous", &block)
30
+ block ||= proc { skip "(no tests defined)" }
31
+ name = :"test_#{desc.gsub(/[^a-zA-Z0-9]+/, "_").gsub(/^_|_$/, "")}"
32
+ raise "#{name} is already defined in #{self}" if method_defined?(name)
33
+
34
+ define_method(name, &block)
35
+ end
36
+
37
+ # Defines a setup method using a block.
38
+ #
39
+ # The block runs after any superclass setup, ensuring the framework is
40
+ # fully initialized before your setup code runs.
41
+ #
42
+ # If you need control over when +super+ is called, define a regular
43
+ # +setup+ method instead:
44
+ #
45
+ # def setup
46
+ # @subject = MyClass.new
47
+ # super
48
+ # end
49
+ #
50
+ # @example
51
+ # setup do
52
+ # @subject = MyClass.new
53
+ # end
54
+ #
55
+ # @since 2.2.0
56
+ # @api public
57
+ def setup(&block)
58
+ define_method(:setup) do
59
+ super()
60
+ instance_exec(&block)
61
+ end
62
+ end
63
+
64
+ # Defines a teardown method using a block.
65
+ #
66
+ # The block runs before any superclass teardown, ensuring your cleanup
67
+ # code runs before the framework tears down (e.g. before Capybara resets
68
+ # its session).
69
+ #
70
+ # If you need control over when +super+ is called, define a regular
71
+ # +teardown+ method instead:
72
+ #
73
+ # def teardown
74
+ # super
75
+ # @subject.close
76
+ # end
77
+ #
78
+ # @example
79
+ # teardown do
80
+ # @subject.close
81
+ # end
82
+ #
83
+ # @since 2.2.0
84
+ # @api public
85
+ def teardown(&block)
86
+ define_method(:teardown) do
87
+ instance_exec(&block)
88
+ super()
89
+ end
90
+ end
91
+ end
92
+
93
+ extend ClassMethods
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Minitest
5
+ # The current hanami-minitest version.
6
+ #
7
+ # @api public
8
+ VERSION = "3.0.0.rc1"
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli"
4
+ require "zeitwerk"
5
+
6
+ # @see Hanami::Minitest
7
+ # @since 2.0.0
8
+ module Hanami
9
+ # Minitest and testing support for Hanami applications.
10
+ #
11
+ # @since 2.0.0
12
+ # @api private
13
+ module Minitest
14
+ # @api private
15
+ def self.gem_loader
16
+ @gem_loader ||= Zeitwerk::Loader.new.tap do |loader|
17
+ root = File.expand_path("..", __dir__)
18
+ loader.tag = "hanami-minitest"
19
+ loader.inflector = Zeitwerk::GemInflector.new("#{root}/hanami-minitest.rb")
20
+ loader.push_dir(root)
21
+ loader.ignore(
22
+ "#{root}/hanami-minitest.rb",
23
+ "#{root}/hanami/minitest/{rake_tasks,version}.rb",
24
+ "#{root}/hanami/minitest/generators/templates"
25
+ )
26
+ end
27
+ end
28
+
29
+ gem_loader.setup
30
+ require_relative "minitest/version"
31
+
32
+ if Hanami::CLI.within_hanami_app?
33
+ require_relative "minitest/commands"
34
+ require_relative "minitest/generators/action"
35
+ require_relative "minitest/generators/mailer"
36
+ require_relative "minitest/generators/operation"
37
+ require_relative "minitest/generators/part"
38
+ require_relative "minitest/generators/slice"
39
+
40
+ Hanami::CLI.before "install", Commands::Install
41
+ Hanami::CLI.after "generate slice", Commands::Generate::Slice
42
+
43
+ if Hanami.bundled?("dry-operation")
44
+ Hanami::CLI.after "generate operation", Commands::Generate::Operation
45
+ end
46
+
47
+ if Hanami.bundled?("hanami-controller")
48
+ Hanami::CLI.after "generate action", Commands::Generate::Action
49
+ end
50
+
51
+ if Hanami.bundled?("hanami-view")
52
+ Hanami::CLI.after "generate part", Commands::Generate::Part
53
+ end
54
+
55
+ if Hanami.bundled?("hanami-mailer")
56
+ Hanami::CLI.after "generate mailer", Commands::Generate::Mailer
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "hanami/minitest"
4
+
5
+ # Load Rake tasks last, after hanami/minitest is fully loaded, to avoid circular requires.
6
+ #
7
+ # When you run `rake test` in a Hanami app, the Rakefile will first require all gems in the `:cli`
8
+ # Bundler group, via Hanami CLI. If we required rake_tasks inside hanami/minitest.rb, that file
9
+ # would still be mid-load when the Rake test runner loads the test files. Since each test file
10
+ # requires test_helper.rb, which itself does `require "hanami/minitest"`, this would otherwise
11
+ # lead to a circular require warning.
12
+ require_relative "hanami/minitest/rake_tasks"