Skip to content

Commit 35ac2a6

Browse files
committed
Fix tests
1 parent ec2c564 commit 35ac2a6

File tree

3 files changed

+159
-119
lines changed

3 files changed

+159
-119
lines changed

lib/tailwindcss/commands.rb

+5-7
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,13 @@ def rails_css_compressor?
4242
defined?(Rails) && Rails&.application&.config&.assets&.css_compressor.present?
4343
end
4444

45-
def engines_tailwindcss_roots
45+
def engines_roots
4646
return [] unless defined?(Rails)
47+
return [] unless Rails.application&.config&.tailwindcss_rails&.engines
4748

48-
Rails::Engine.subclasses.select do |engine|
49+
Rails::Engine.descendants.select do |engine|
4950
begin
50-
spec = Gem::Specification.find_by_name(engine.engine_name)
51-
spec.dependencies.any? { |d| d.name == 'tailwindcss-rails' }
52-
rescue Gem::MissingSpecError
53-
false
51+
engine.engine_name.in?(Rails.application.config.tailwindcss_rails.engines)
5452
end
5553
end.map do |engine|
5654
[
@@ -61,7 +59,7 @@ def engines_tailwindcss_roots
6159
end
6260

6361
def with_dynamic_input
64-
engine_roots = Tailwindcss::Commands.engines_tailwindcss_roots
62+
engine_roots = Tailwindcss::Commands.engines_roots
6563
if engine_roots.any?
6664
Tempfile.create('tailwind.css') do |file|
6765
file.write(engine_roots.map { |root| "@import \"#{root}\";" }.join("\n"))

lib/tailwindcss/engine.rb

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ class Engine < ::Rails::Engine
55
config.tailwindcss_rails = ActiveSupport::OrderedOptions.new
66
config.tailwindcss_rails.engines = []
77

8+
initializer 'tailwindcss.load_hook' do |app|
9+
ActiveSupport.run_load_hooks(:tailwindcss_rails, app)
10+
end
11+
812
initializer "tailwindcss.disable_generator_stylesheets" do
913
Rails.application.config.generators.stylesheets = false
1014
end

test/lib/tailwindcss/commands_test.rb

+150-112
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
require "test_helper"
2+
require "ostruct"
23
require "minitest/mock"
34

45
class Tailwindcss::CommandsTest < ActiveSupport::TestCase
5-
attr_accessor :executable
6+
attr_accessor :executable, :original_rails, :tmp_dir
67

7-
def setup
8-
super
8+
setup do
9+
@tmp_dir = Dir.mktmpdir
10+
@original_rails = Object.const_get(:Rails) if Object.const_defined?(:Rails)
911
@executable = Tailwindcss::Ruby.executable
1012
end
1113

14+
teardown do
15+
FileUtils.rm_rf(@tmp_dir)
16+
Tailwindcss::Commands.remove_tempfile! if Tailwindcss::Commands.class_variable_defined?(:@@tempfile)
17+
restore_rails_constant
18+
end
19+
1220
test ".compile_command" do
1321
Rails.stub(:root, File) do # Rails.root won't work in this test suite
1422
actual = Tailwindcss::Commands.compile_command("app/assets/tailwind/application.css")
@@ -127,138 +135,168 @@ def setup
127135
end
128136
end
129137

130-
test ".engines_tailwindcss_roots when there are no engines" do
131-
Rails.stub(:root, Pathname.new("/dummy")) do
132-
Rails::Engine.stub(:subclasses, []) do
133-
assert_empty Tailwindcss::Commands.engines_tailwindcss_roots
134-
end
138+
test ".engines_roots when Rails is not defined" do
139+
Object.send(:remove_const, :Rails) if Object.const_defined?(:Rails)
140+
assert_empty Tailwindcss::Commands.engines_roots
141+
end
142+
143+
test ".engines_roots when no engines are configured" do
144+
with_rails_app do
145+
assert_empty Tailwindcss::Commands.engines_roots
135146
end
136147
end
137148

138-
test ".engines_tailwindcss_roots when there are engines" do
139-
Dir.mktmpdir do |tmpdir|
140-
root = Pathname.new(tmpdir)
141-
142-
# Create multiple engines
143-
engine_root1 = root.join('engine1')
144-
engine_root2 = root.join('engine2')
145-
engine_root3 = root.join('engine3')
146-
FileUtils.mkdir_p(engine_root1)
147-
FileUtils.mkdir_p(engine_root2)
148-
FileUtils.mkdir_p(engine_root3)
149-
150-
engine1 = Class.new(Rails::Engine) do
151-
define_singleton_method(:engine_name) { "test_engine1" }
152-
define_singleton_method(:root) { engine_root1 }
153-
end
149+
test ".engines_roots when there are engines" do
150+
within_engine_configs do |engine1, engine2, engine3|
151+
roots = Tailwindcss::Commands.engines_roots
152+
153+
assert_equal 2, roots.size
154+
assert_includes roots, engine1.css_path.to_s
155+
assert_includes roots, engine2.css_path.to_s
156+
refute_includes roots, engine3.css_path.to_s
157+
end
158+
end
159+
160+
test ".with_dynamic_input yields tempfile path when engines exist" do
161+
within_engine_configs do |engine1, engine2|
162+
Tailwindcss::Commands.with_dynamic_input do |css_path|
163+
assert_match(/tailwind\.css/, css_path)
164+
assert File.exist?(css_path)
154165

155-
engine2 = Class.new(Rails::Engine) do
156-
define_singleton_method(:engine_name) { "test_engine2" }
157-
define_singleton_method(:root) { engine_root2 }
166+
content = File.read(css_path)
167+
assert_match %r{@import "#{engine1.css_path}";}, content
168+
assert_match %r{@import "#{engine2.css_path}";}, content
169+
assert_match %r{@import "#{Rails.root.join('app/assets/tailwind/application.css')}";}, content
158170
end
171+
end
172+
end
159173

160-
engine3 = Class.new(Rails::Engine) do
161-
define_singleton_method(:engine_name) { "test_engine3" }
162-
define_singleton_method(:root) { engine_root3 }
174+
test ".with_dynamic_input yields application.css path when no engines" do
175+
with_rails_app do
176+
expected_path = Rails.root.join("app/assets/tailwind/application.css").to_s
177+
Tailwindcss::Commands.with_dynamic_input do |css_path|
178+
assert_equal expected_path, css_path
163179
end
180+
end
181+
end
164182

165-
# Create mock specs for engines
166-
spec1 = Minitest::Mock.new
167-
spec1.expect(:dependencies, [Gem::Dependency.new("tailwindcss-rails")])
168-
169-
spec2 = Minitest::Mock.new
170-
spec2.expect(:dependencies, [Gem::Dependency.new("tailwindcss-rails")])
171-
172-
spec3 = Minitest::Mock.new
173-
spec3.expect(:dependencies, [])
174-
175-
# Set up file structure
176-
# Engine 1: CSS in engine root
177-
engine1_css = engine_root1.join("app/assets/tailwind/test_engine1/application.css")
178-
FileUtils.mkdir_p(File.dirname(engine1_css))
179-
FileUtils.touch(engine1_css)
180-
181-
# Engine 2: CSS in Rails root
182-
engine2_css = root.join("app/assets/tailwind/test_engine2/application.css")
183-
FileUtils.mkdir_p(File.dirname(engine2_css))
184-
FileUtils.touch(engine2_css)
185-
186-
# Engine 3: CsS in engine root, but no tailwindcss-rails dependency
187-
engine3_css = engine_root2.join("app/assets/tailwind/test_engine3/application.css")
188-
FileUtils.mkdir_p(File.dirname(engine3_css))
189-
FileUtils.touch(engine3_css)
190-
191-
find_by_name_results = {
192-
"test_engine1" => spec1,
193-
"test_engine2" => spec2,
194-
"test_engine3" => spec3,
195-
}
196-
197-
Gem::Specification.stub(:find_by_name, ->(name) { find_by_name_results[name] }) do
198-
Rails.stub(:root, root) do
199-
Rails::Engine.stub(:subclasses, [engine1, engine2]) do
200-
roots = Tailwindcss::Commands.engines_tailwindcss_roots
201-
202-
assert_equal 2, roots.size
203-
assert_includes roots, engine1_css.to_s
204-
assert_includes roots, engine2_css.to_s
205-
assert_not_includes roots, engine3_css.to_s
206-
end
183+
test "engines can be configured via tailwindcss_rails.engines" do
184+
with_rails_app do
185+
# Create a test engine
186+
test_engine = Class.new(Rails::Engine) do
187+
def self.engine_name
188+
"test_engine"
189+
end
190+
191+
def self.root
192+
Pathname.new(Dir.mktmpdir)
207193
end
208194
end
209195

210-
spec1.verify
211-
spec2.verify
196+
# Create CSS file for the engine
197+
engine_css_path = test_engine.root.join("app/assets/tailwind/test_engine/application.css")
198+
FileUtils.mkdir_p(File.dirname(engine_css_path))
199+
FileUtils.touch(engine_css_path)
200+
201+
# Create application-level CSS file
202+
app_css_path = Rails.root.join("app/assets/tailwind/test_engine/application.css")
203+
FileUtils.mkdir_p(File.dirname(app_css_path))
204+
FileUtils.touch(app_css_path)
205+
206+
# Register the engine
207+
Rails::Engine.descendants << test_engine
208+
209+
# Store the hook for later execution
210+
hook = nil
211+
ActiveSupport.on_load(:tailwindcss_rails) do
212+
hook = self
213+
Rails.application.config.tailwindcss_rails.engines << "test_engine"
214+
end
215+
216+
# Trigger the hook manually
217+
ActiveSupport.run_load_hooks(:tailwindcss_rails, hook)
218+
219+
# Verify the engine is included in roots
220+
roots = Tailwindcss::Commands.engines_roots
221+
assert_equal 1, roots.size
222+
assert_includes roots, app_css_path.to_s
223+
ensure
224+
FileUtils.rm_rf(test_engine.root) if defined?(test_engine)
225+
FileUtils.rm_rf(File.dirname(app_css_path)) if defined?(app_css_path)
212226
end
213227
end
214228

215-
test ".with_dynamic_input when there are no engines" do
216-
Dir.mktmpdir do |tmpdir|
217-
root = Pathname.new(tmpdir)
218-
input_path = root.join("app/assets/tailwind/application.css").to_s
229+
private
230+
def with_rails_app
231+
Object.send(:remove_const, :Rails) if Object.const_defined?(:Rails)
232+
Object.const_set(:Rails, setup_mock_rails)
233+
yield
234+
end
235+
236+
def setup_mock_rails
237+
mock_engine = Class.new do
238+
class << self
239+
attr_accessor :engine_name, :root
219240

220-
Rails.stub(:root, root) do
221-
Tailwindcss::Commands.stub(:engines_tailwindcss_roots, []) do
222-
Tailwindcss::Commands.with_dynamic_input do |actual|
223-
assert_equal input_path, actual
241+
def descendants
242+
@descendants ||= []
224243
end
225244
end
226245
end
227-
end
228-
end
229246

230-
test ".with_dynamic_input when there are engines" do
231-
Dir.mktmpdir do |tmpdir|
232-
root = Pathname.new(tmpdir)
233-
input_path = root.join("app/assets/tailwind/application.css").to_s
247+
mock_rails = Class.new do
248+
class << self
249+
attr_accessor :root, :application
234250

235-
# Create necessary files
236-
FileUtils.mkdir_p(File.dirname(input_path))
237-
FileUtils.touch(input_path)
251+
def const_get(const_name)
252+
return Engine if const_name == :Engine
253+
super
254+
end
255+
end
256+
end
238257

239-
# Create engine CSS file
240-
engine_css_path = root.join("app/assets/tailwind/test_engine/application.css")
241-
FileUtils.mkdir_p(File.dirname(engine_css_path))
242-
FileUtils.touch(engine_css_path)
258+
mock_rails.const_set(:Engine, mock_engine)
259+
mock_rails.root = Pathname.new(@tmp_dir)
260+
mock_rails.application = OpenStruct.new(
261+
config: OpenStruct.new(
262+
tailwindcss_rails: OpenStruct.new(engines: []),
263+
assets: OpenStruct.new(css_compressor: nil)
264+
)
265+
)
266+
mock_rails
267+
end
243268

244-
Rails.stub(:root, root) do
245-
Tailwindcss::Commands.stub(:engines_tailwindcss_roots, [engine_css_path.to_s]) do
246-
Tailwindcss::Commands.with_dynamic_input do |actual|
247-
temp_path = Pathname.new(actual)
248-
refute_equal input_path, temp_path.to_s # input path should be different
249-
assert_match(/tailwind\.css/, temp_path.basename.to_s) # should use temp file
250-
assert_includes [Dir.tmpdir, '/tmp'], temp_path.dirname.to_s # should be in temp directory
251-
252-
# Check temp file contents
253-
temp_content = File.read(actual)
254-
expected_content = <<~CSS
255-
@import "#{engine_css_path}";
256-
@import "#{input_path}";
257-
CSS
258-
assert_equal expected_content.strip, temp_content.strip
259-
end
269+
def restore_rails_constant
270+
Object.send(:remove_const, :Rails) if Object.const_defined?(:Rails)
271+
Object.const_set(:Rails, @original_rails) if @original_rails
272+
end
273+
274+
def within_engine_configs
275+
engine_configs = create_test_engines
276+
with_rails_app do
277+
Rails.application.config.tailwindcss_rails.engines = %w[test_engine1 test_engine2]
278+
279+
# Create and register mock engine classes
280+
engine_configs.each do |config|
281+
engine_class = Class.new(Rails::Engine)
282+
engine_class.engine_name = config.name
283+
engine_class.root = Pathname.new(config.root)
284+
Rails::Engine.descendants << engine_class
260285
end
286+
287+
yield(*engine_configs)
288+
end
289+
end
290+
291+
def create_test_engines
292+
[1, 2, 3].map do |i|
293+
engine = OpenStruct.new
294+
engine.name = "test_engine#{i}"
295+
engine.root = File.join(@tmp_dir, "engine#{i}")
296+
engine.css_path = File.join(@tmp_dir, "app/assets/tailwind/test_engine#{i}/application.css")
297+
FileUtils.mkdir_p(File.dirname(engine.css_path))
298+
FileUtils.touch(engine.css_path)
299+
engine
261300
end
262301
end
263-
end
264302
end

0 commit comments

Comments
 (0)