Skip to content

Commit db59dbb

Browse files
committed
feat: migrate test suite to Quickdraw test runner
- Convert all 13 test files from Minitest class-based style to quickdraw describe/test blocks with 335/335 tests passing - Replace stub_any_instance with define_method/restore pattern - Fix __dir__ relative path issue by using absolute paths in Dir.glob - Fix Rack::Test session memoization across tests by resetting in setup - Fix rake not in PATH by using RAKE_BIN constant - Patch Quickdraw::Context#run to support setup/teardown per test - Use Class.new for ActionView::TestCase subclasses to bypass inherited hook name error in anonymous class contexts - Disable Lint/Void and NilComparison rubocop cops for test files since quickdraw uses expect(x) == y as assertions https://claude.ai/code/session_012jCro5Dfxc7kx3AogS7HS4
1 parent 51e3320 commit db59dbb

20 files changed

Lines changed: 701 additions & 654 deletions

.claude/worktrees/agent-aa68e59d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 51e3320d33f5ce9d63cbfe5acdfcd37a7280a583

.claude/worktrees/agent-af2969c3

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 51e3320d33f5ce9d63cbfe5acdfcd37a7280a583

.rubocop.yml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ inherit_from: .rubocop_todo.yml
33
require:
44
- standard
55
- rubocop-performance
6-
- rubocop-minitest
76

87
inherit_gem:
98
standard: config/ruby-3.0.yml
@@ -164,12 +163,21 @@ Security/YAMLLoad:
164163
Style/MutableConstant:
165164
Enabled: false
166165

167-
Minitest/MultipleAssertions:
168-
Enabled: false
166+
# Quickdraw uses `expect(x) == y` as an assertion method — rubocop
167+
# incorrectly treats the `==` return value as unused in void context.
168+
Lint/Void:
169+
Exclude:
170+
- "test/**/*_test.rb"
169171

170-
Minitest/AssertRaisesWithRegexpArgument:
171-
Enabled: false
172+
Style/NilComparison:
173+
Exclude:
174+
- "test/**/*_test.rb"
172175

173-
Minitest/UselessAssertion:
176+
Style/NonNilCheck:
174177
Exclude:
175-
- test/helper_test.rb
178+
- "test/**/*_test.rb"
179+
180+
Lint/UselessAssignment:
181+
Exclude:
182+
- "test/**/*_test.rb"
183+

Gemfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ gemspec path: "./vite_plugin_legacy"
1010

1111
group :development, :test do
1212
gem "benchmark-ips"
13+
gem "minitest", "~> 5.0" # provides Object#stub for block-scoped stubs
14+
gem "quickdraw", "~> 0.1"
1315
gem "rubocop"
14-
gem "rubocop-minitest"
1516
gem "rubocop-performance"
1617
gem "standard", require: false
1718
end

Gemfile.lock

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ GEM
101101
ast (2.4.2)
102102
base64 (0.3.0)
103103
benchmark-ips (2.14.0)
104-
bigdecimal (4.1.0)
104+
bigdecimal (4.1.1)
105105
builder (3.3.0)
106106
byebug (11.1.3)
107107
coderay (1.1.3)
@@ -181,11 +181,12 @@ GEM
181181
psych (5.3.1)
182182
date
183183
stringio
184+
quickdraw (0.1.0)
184185
racc (1.8.1)
185-
rack (3.2.5)
186+
rack (3.2.6)
186187
rack-proxy (0.7.7)
187188
rack
188-
rack-session (2.1.1)
189+
rack-session (2.1.2)
189190
base64 (>= 0.1.0)
190191
rack (>= 3.0.0)
191192
rack-test (2.2.0)
@@ -242,9 +243,6 @@ GEM
242243
unicode-display_width (>= 2.4.0, < 4.0)
243244
rubocop-ast (1.37.0)
244245
parser (>= 3.3.1.0)
245-
rubocop-minitest (0.36.0)
246-
rubocop (>= 1.61, < 2.0)
247-
rubocop-ast (>= 1.31.1, < 2.0)
248246
rubocop-performance (1.23.1)
249247
rubocop (>= 1.48.1, < 2.0)
250248
rubocop-ast (>= 1.31.1, < 2.0)
@@ -292,10 +290,10 @@ DEPENDENCIES
292290
minitest-reporters (~> 1.4)
293291
minitest-stub_any_instance (~> 1.0)
294292
pry-byebug (~> 3.9)
293+
quickdraw (~> 0.1)
295294
rails
296295
rake (~> 13.0)
297296
rubocop
298-
rubocop-minitest
299297
rubocop-performance
300298
simplecov (< 0.23)
301299
spring (~> 2.1)

Rakefile

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,50 @@
11
# frozen_string_literal: true
22

3-
require "rake/testtask"
3+
RAKE_BIN = File.expand_path("bin/rake", __dir__)
44

5-
Rake::TestTask.new do |t|
6-
t.libs << "test"
7-
t.test_files = FileList["test/**/*_test.rb"]
8-
t.verbose = true
5+
task :test do
6+
$LOAD_PATH.unshift(File.expand_path("test", __dir__))
7+
require "quickdraw"
8+
require_relative "test/test_helper"
9+
10+
# Patch runner to detect _test.rb files in backtraces (quickdraw normally expects .test.rb)
11+
Quickdraw::Runner.prepend(Module.new do
12+
def failure!(path, &message)
13+
location = caller_locations.drop_while { |l| !l.path.match?(/[_\.]test\.rb/) }
14+
@failures << [message, location, path]
15+
Kernel.print "🔴 "
16+
end
17+
end)
18+
19+
test_files = Dir.glob(File.expand_path("test/**/*_test.rb", __dir__)).reject { |f|
20+
f.include?("/test_app/") || f.include?("/mounted_app/")
21+
}.sort
22+
23+
result = Quickdraw::Runner.new
24+
25+
test_files.each do |file|
26+
klass = Class.new(Quickdraw::Context) do
27+
class_eval(File.read(file), file, 1)
28+
end
29+
klass.run(result, [File.basename(file)])
30+
end
31+
32+
puts
33+
puts "#{result.successes.count} passed, #{result.failures.count} failed in #{test_files.count} files"
34+
35+
if result.failures.any?
36+
puts
37+
result.failures.each do |(message, location, path)|
38+
puts "FAILED: #{path.join(" > ")}"
39+
if location&.any?
40+
loc = location.first
41+
puts " at #{loc.path}:#{loc.lineno}"
42+
end
43+
puts " #{message.call}"
44+
puts
45+
end
46+
exit 1
47+
end
948
end
1049

1150
task default: :test

test/builder_test.rb

Lines changed: 51 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# frozen_string_literal: true
22

33
require "test_helper"
4-
require "open3"
54

6-
class BuilderTest < ViteRuby::Test
5+
describe "Builder" do
76
delegate :builder, :manifest, to: "ViteRuby.instance"
87

98
def last_build
@@ -25,105 +24,103 @@ def vite_env(*vars)
2524
ViteRuby.config.to_env(*vars)
2625
end
2726

28-
def test_custom_environment_variables
29-
assert_nil vite_env["FOO"]
27+
def stub_runner(errors: "", success: errors.empty?, &block)
28+
args = ["stdout", errors, MockProcessStatus.new(success: success)]
29+
ViteRuby::IO.stub(:capture, args, &block)
30+
end
31+
32+
test "custom environment variables" do
33+
expect(vite_env["FOO"]) == nil
3034
ViteRuby.env["FOO"] = "BAR"
3135

32-
assert_equal "BAR", vite_env["FOO"]
33-
assert_equal "OTHER", vite_env("FOO" => "OTHER")["FOO"]
36+
expect(vite_env["FOO"]) == "BAR"
37+
expect(vite_env("FOO" => "OTHER")["FOO"]) == "OTHER"
3438
end
3539

36-
def test_freshness
37-
assert_predicate last_build, :stale?
38-
refute_predicate last_build, :fresh?
40+
test "freshness" do
41+
expect(last_build).to_be(:stale?)
42+
expect(last_build).not_to_be(:fresh?)
3943
end
4044

41-
def test_freshness_on_build_success
42-
assert_predicate last_build, :stale?
45+
test "freshness on build success" do
46+
expect(last_build).to_be(:stale?)
4347
stub_runner(success: true) {
44-
assert builder.build
45-
assert last_build.success
46-
assert_empty last_build.errors
47-
assert_predicate last_build, :fresh?
48-
assert last_build.digest
49-
assert last_build.timestamp
48+
assert(builder.build)
49+
assert(last_build.success)
50+
expect(last_build.errors).to_be(:empty?)
51+
expect(last_build).to_be(:fresh?)
52+
assert(last_build.digest)
53+
assert(last_build.timestamp)
5054

5155
refresh_config(auto_build: true)
5256

53-
refute_nil manifest.send(:lookup, "app.css")
57+
expect(manifest.send(:lookup, "app.css")) != nil
5458
}
5559
end
5660

57-
def test_freshness_on_build_fail
58-
assert_predicate last_build, :stale?
61+
test "freshness on build fail" do
62+
expect(last_build).to_be(:stale?)
5963
error_message = "SyntaxError: Hero.jsx: Unexpected token (6:6)"
6064
stub_runner(errors: error_message) {
61-
refute builder.build
62-
refute last_build.success
63-
assert_equal last_build.errors, error_message
64-
assert_predicate last_build, :fresh?
65-
assert last_build.digest
66-
assert last_build.timestamp
65+
refute(builder.build)
66+
refute(last_build.success)
67+
expect(last_build.errors) == error_message
68+
expect(last_build).to_be(:fresh?)
69+
assert(last_build.digest)
70+
assert(last_build.timestamp)
6771

6872
refresh_config(auto_build: true)
6973

70-
assert_nil manifest.send(:lookup, "app.css")
74+
expect(manifest.send(:lookup, "app.css")) == nil
7175
}
7276
end
7377

74-
def test_last_build_path
75-
assert_equal builder.send(:last_build_path, ssr: false).basename.to_s, "last-build-#{ViteRuby.config.mode}.json"
76-
assert_equal builder.send(:last_build_path, ssr: true).basename.to_s, "last-ssr-build-#{ViteRuby.config.mode}.json"
78+
test "last build path" do
79+
expect(builder.send(:last_build_path, ssr: false).basename.to_s) == "last-build-#{ViteRuby.config.mode}.json"
80+
expect(builder.send(:last_build_path, ssr: true).basename.to_s) == "last-ssr-build-#{ViteRuby.config.mode}.json"
7781
end
7882

79-
def test_watched_files_digest
83+
test "watched files digest" do
8084
previous_digest = ViteRuby.digest
8185
refresh_config
8286

83-
assert_equal previous_digest, ViteRuby.digest
87+
expect(ViteRuby.digest) == previous_digest
8488
end
8589

86-
def test_external_env_variables
87-
assert_equal "production", vite_env["VITE_RUBY_MODE"]
88-
assert_equal Rails.root.to_s, vite_env["VITE_RUBY_ROOT"]
90+
test "external env variables" do
91+
expect(vite_env["VITE_RUBY_MODE"]) == "production"
92+
expect(vite_env["VITE_RUBY_ROOT"]) == Rails.root.to_s
8993

9094
ENV["VITE_RUBY_MODE"] = "foo.bar"
9195
ENV["VITE_RUBY_ROOT"] = "/baz"
9296
refresh_config
9397

94-
assert_equal "foo.bar", vite_env["VITE_RUBY_MODE"]
95-
assert_equal "/baz", vite_env["VITE_RUBY_ROOT"]
98+
expect(vite_env["VITE_RUBY_MODE"]) == "foo.bar"
99+
expect(vite_env["VITE_RUBY_ROOT"]) == "/baz"
96100
ensure
97101
ENV.delete("VITE_RUBY_MODE")
98102
ENV.delete("VITE_RUBY_ROOT")
99103
refresh_config
100104
end
101105

102-
def test_missing_executable
106+
test "missing executable" do
103107
refresh_config(vite_bin_path: "none/vite")
104108

105-
# It fails because we stub the File.exist? check, so the binary is missing.
106-
error = assert_raises(ViteRuby::MissingExecutableError) {
109+
# It fails because we stub File.exist? so the binary appears missing.
110+
expect {
107111
File.stub(:exist?, true) { builder.build }
108-
}
109-
110-
assert_match "The vite binary is not available.", error.message
112+
}.to_raise(ViteRuby::MissingExecutableError) do |error|
113+
expect(error.message).to_include("The vite binary is not available.")
114+
end
111115

112116
# The provided binary does not exist, so it uses the default strategy.
113-
stub_runner(success: true) { assert builder.build }
117+
stub_runner(success: true) { assert(builder.build) }
114118
end
115119

116-
def test_build_cache
120+
test "build cache" do
117121
build = ViteRuby::Build.from_previous(Pathname.new(__FILE__), "digest")
118122

119-
assert_equal("never", build.timestamp)
120-
assert_predicate(build, :retry_failed?)
121-
end
122-
123-
private
124-
125-
def stub_runner(errors: "", success: errors.empty?, &block)
126-
args = ["stdout", errors, MockProcessStatus.new(success: success)]
127-
ViteRuby::IO.stub(:capture, args, &block)
123+
expect(build.timestamp) == "never"
124+
expect(build).to_be(:retry_failed?)
128125
end
129126
end

0 commit comments

Comments
 (0)