jruby-lint 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +12 -0
- data/Guardfile +12 -0
- data/History.txt +4 -0
- data/README.md +62 -0
- data/Rakefile +24 -0
- data/bin/jrlint +5 -0
- data/jruby-lint.gemspec +38 -0
- data/lib/jruby/lint.rb +19 -0
- data/lib/jruby/lint/ast.rb +56 -0
- data/lib/jruby/lint/checkers.rb +24 -0
- data/lib/jruby/lint/checkers/fork_exec.rb +47 -0
- data/lib/jruby/lint/checkers/gem.rb +45 -0
- data/lib/jruby/lint/checkers/gemspec.rb +41 -0
- data/lib/jruby/lint/checkers/object_space.rb +25 -0
- data/lib/jruby/lint/checkers/thread_critical.rb +26 -0
- data/lib/jruby/lint/cli.rb +61 -0
- data/lib/jruby/lint/collectors.rb +75 -0
- data/lib/jruby/lint/collectors/bundler.rb +9 -0
- data/lib/jruby/lint/collectors/gemspec.rb +9 -0
- data/lib/jruby/lint/collectors/rake.rb +9 -0
- data/lib/jruby/lint/collectors/ruby.rb +14 -0
- data/lib/jruby/lint/finding.rb +15 -0
- data/lib/jruby/lint/github.crt +76 -0
- data/lib/jruby/lint/libraries.rb +107 -0
- data/lib/jruby/lint/project.rb +56 -0
- data/lib/jruby/lint/reporters.rb +35 -0
- data/lib/jruby/lint/version.rb +5 -0
- data/spec/fixtures/C-Extension-Alternatives.html +557 -0
- data/spec/jruby/lint/ast_spec.rb +23 -0
- data/spec/jruby/lint/checkers_spec.rb +135 -0
- data/spec/jruby/lint/cli_spec.rb +66 -0
- data/spec/jruby/lint/collectors_spec.rb +34 -0
- data/spec/jruby/lint/finding_spec.rb +31 -0
- data/spec/jruby/lint/libraries_spec.rb +56 -0
- data/spec/jruby/lint/project_spec.rb +77 -0
- data/spec/jruby/lint/reporters_spec.rb +42 -0
- data/spec/jruby/lint/version_spec.rb +6 -0
- data/spec/spec_helper.rb +30 -0
- metadata +194 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::AST::Visitor do
|
4
|
+
Given(:ast) { JRuby.parse(script) }
|
5
|
+
Given(:visitor) { JRuby::Lint::AST::Visitor.new(ast) }
|
6
|
+
|
7
|
+
# RootNode
|
8
|
+
# NewlineNode
|
9
|
+
# FCallOneArgNode |puts|
|
10
|
+
# ArrayNode
|
11
|
+
# StrNode =="hello"
|
12
|
+
Given(:script) { %{puts "hello"} }
|
13
|
+
|
14
|
+
context "visits all nodes" do
|
15
|
+
When { visitor.each_node { @count ||= 0; @count += 1} }
|
16
|
+
Then { @count.should == 5 }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "selects nodes" do
|
20
|
+
When { @nodes = visitor.select {|n| n.node_type == org.jruby.ast.NodeType::STRNODE } }
|
21
|
+
Then { @nodes.size.should == 1 && @nodes.first.value.to_s.should == "hello" }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::Checker do
|
4
|
+
context "checkers" do
|
5
|
+
subject { Class.new { include JRuby::Lint::Checker } }
|
6
|
+
|
7
|
+
it "finds all loaded checkers" do
|
8
|
+
JRuby::Lint::Checker.loaded_checkers.should include(subject)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe JRuby::Lint::Checkers do
|
14
|
+
Given(:gems) { { "rdiscount" => "may not work", "bson_ext" => "not needed" } }
|
15
|
+
Given(:project) { double("project").tap {|p| p.stub_chain("libraries.gems") { gems } } }
|
16
|
+
Given(:collector) do
|
17
|
+
JRuby::Lint::Collector.new(project).tap do |c|
|
18
|
+
c.contents = script
|
19
|
+
c.checkers = [checker]
|
20
|
+
checker.collector = c
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "Fork/exec checker" do
|
25
|
+
Given(:checker) { JRuby::Lint::Checkers::ForkExec.new }
|
26
|
+
|
27
|
+
context "detects fcall-style" do
|
28
|
+
# FCallNoArgBlockNode |fork|
|
29
|
+
Given(:script) { "fork { }; exec('cmd')" }
|
30
|
+
When { collector.run }
|
31
|
+
Then { collector.findings.size.should == 2 }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "detects vcall-style" do
|
35
|
+
# VCallNode |fork|
|
36
|
+
Given(:script) { "fork" }
|
37
|
+
When { collector.run }
|
38
|
+
Then { collector.findings.size.should == 1 }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "does not detect call-style" do
|
42
|
+
# CallNoArgNode |fork|
|
43
|
+
# VCallNode |fork|
|
44
|
+
Given(:script) { "fork.fork" }
|
45
|
+
When { collector.run }
|
46
|
+
Then { collector.findings.size.should == 0 }
|
47
|
+
end
|
48
|
+
|
49
|
+
context "detects Kernel::fork style" do
|
50
|
+
# CallNoArgNode |fork|
|
51
|
+
# ConstNode |Kernel|
|
52
|
+
Given(:script) { "Kernel::fork; Kernel::exec('cmd')" }
|
53
|
+
When { collector.run }
|
54
|
+
Then { collector.findings.size.should == 2 }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "Gem checker" do
|
59
|
+
Given(:checker) { JRuby::Lint::Checkers::Gem.new }
|
60
|
+
|
61
|
+
context "creates a finding for a gem mentioned in the libraries" do
|
62
|
+
Given(:script) { "gem 'rdiscount'" }
|
63
|
+
When { collector.run }
|
64
|
+
Then { collector.findings.size.should == 2 }
|
65
|
+
end
|
66
|
+
|
67
|
+
context "creates one finding to mention the wiki for gem compatibility" do
|
68
|
+
Given(:script) { "gem 'rdiscount'; gem 'bson_ext'" }
|
69
|
+
When { collector.run }
|
70
|
+
Then { collector.findings.size.should == 3 }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "does not create a finding for a gem not mentioned in the gems info" do
|
74
|
+
Given(:script) { "gem 'json_pure'" }
|
75
|
+
When { collector.run }
|
76
|
+
Then { collector.findings.size.should == 0 }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "only checks calls to #gem" do
|
80
|
+
Given(:script) { "require 'rdiscount'" }
|
81
|
+
When { collector.run }
|
82
|
+
Then { collector.findings.size.should == 0 }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "Gemspec checker" do
|
87
|
+
Given(:checker) { JRuby::Lint::Checkers::Gemspec.new }
|
88
|
+
|
89
|
+
Given(:script) { "Gem::Specification.new do |s|" +
|
90
|
+
"\ns.name = 'hello'\ns.add_dependency 'rdiscount'\n" +
|
91
|
+
"s.add_development_dependency 'ruby-debug19'\nend\n" }
|
92
|
+
|
93
|
+
When { collector.run }
|
94
|
+
Then { collector.findings.size.should == 2 }
|
95
|
+
Then { collector.findings.detect{|f| f.message =~ /rdiscount/ }.should be_true }
|
96
|
+
end
|
97
|
+
|
98
|
+
context "Thread.critical checker" do
|
99
|
+
Given(:checker) { JRuby::Lint::Checkers::ThreadCritical.new }
|
100
|
+
|
101
|
+
context "read" do
|
102
|
+
Given(:script) { "begin \n Thread.critical \n end"}
|
103
|
+
When { collector.run }
|
104
|
+
Then { collector.findings.size.should == 1 }
|
105
|
+
end
|
106
|
+
|
107
|
+
context "assign" do
|
108
|
+
Given(:script) { "begin \n Thread.critical = true \n ensure Thread.critical = false \n end"}
|
109
|
+
When { collector.run }
|
110
|
+
Then { collector.findings.size.should == 2 }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "ObjectSpace" do
|
115
|
+
Given(:checker) { JRuby::Lint::Checkers::ObjectSpace.new }
|
116
|
+
|
117
|
+
context "_id2ref usage" do
|
118
|
+
Given(:script) { "ObjectSpace._id2ref(obj)"}
|
119
|
+
When { collector.run }
|
120
|
+
Then { collector.findings.size.should == 1 }
|
121
|
+
end
|
122
|
+
|
123
|
+
context "each_object usage" do
|
124
|
+
Given(:script) { "ObjectSpace.each_object { }"}
|
125
|
+
When { collector.run }
|
126
|
+
Then { collector.findings.size.should == 1 }
|
127
|
+
end
|
128
|
+
|
129
|
+
context "each_object(Class) usage is ok" do
|
130
|
+
Given(:script) { "ObjectSpace.each_object(Class) { }"}
|
131
|
+
When { collector.run }
|
132
|
+
Then { collector.findings.size.should == 0 }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
require 'jruby/lint/cli'
|
3
|
+
|
4
|
+
describe JRuby::Lint::CLI do
|
5
|
+
context "when launched" do
|
6
|
+
Given(:command) { "ruby -I#{project_dir}/lib -S #{project_dir}/bin/jrlint #{args}" }
|
7
|
+
|
8
|
+
context "with the help option" do
|
9
|
+
Given(:args) { "--help" }
|
10
|
+
When { run_simple(command) }
|
11
|
+
Then do
|
12
|
+
output_from(command).should =~ /help.*This message/
|
13
|
+
@last_exit_status.should == 0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with the version option" do
|
18
|
+
Given(:args) { "--version" }
|
19
|
+
When { run_simple(command) }
|
20
|
+
Then do
|
21
|
+
output_from(command).should =~ /version #{JRuby::Lint::VERSION}/
|
22
|
+
@last_exit_status.should == 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a dash-e option" do
|
27
|
+
Given(:args) { "-e true"}
|
28
|
+
When { run_simple(command) }
|
29
|
+
Then do
|
30
|
+
output_from(command).should =~ /Processed 1 expression/
|
31
|
+
@last_exit_status.should == 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with a file argument" do
|
36
|
+
Given(:args) { "sample.rb" }
|
37
|
+
Given { write_file("sample.rb", "puts 'hello'"); write_file("example.rb", "puts 'hello'") }
|
38
|
+
When { run_simple(command) }
|
39
|
+
Then do
|
40
|
+
output_from(command).should =~ /Processed 1 file/
|
41
|
+
@last_exit_status.should == 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with no arguments" do
|
46
|
+
Given(:args) { "" }
|
47
|
+
|
48
|
+
context "and some files to process" do
|
49
|
+
Given { write_file("Rakefile", "") }
|
50
|
+
When { run_simple(command) }
|
51
|
+
Then do
|
52
|
+
output = output_from(command)
|
53
|
+
output.should =~ /JRuby-Lint version #{JRuby::Lint::VERSION}/
|
54
|
+
output.should =~ /Processed 1 file/
|
55
|
+
output.should =~ /OK/
|
56
|
+
@last_exit_status.should == 0
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "and no files to process" do
|
61
|
+
When { run_simple(command) }
|
62
|
+
Then { output_from(command).should =~ /Processed 0 files/ }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::Collector do
|
4
|
+
Given(:collector) { JRuby::Lint::Collector.new }
|
5
|
+
Given(:checker_class) { Class.new { include JRuby::Lint::Checker } }
|
6
|
+
|
7
|
+
context "loads detected checkers" do
|
8
|
+
When { checker_class }
|
9
|
+
Then { collector.checkers.detect {|c| checker_class === c }.should be_true }
|
10
|
+
end
|
11
|
+
|
12
|
+
context "invokes all checkers" do
|
13
|
+
Given(:checker) do
|
14
|
+
double("checker").tap do |checker|
|
15
|
+
checker.should_receive(:visitTrueNode)
|
16
|
+
collector.checkers = [checker]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
When { collector.contents = 'true' }
|
20
|
+
Then { collector.run }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "loads an AST" do
|
24
|
+
When { collector.contents = 'puts "hello"' }
|
25
|
+
When { @ast = collector.ast }
|
26
|
+
Then { @ast.inspect.should =~ /"hello"/m }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "reports syntax errors as findings" do
|
30
|
+
When { collector.contents = '<% true %>' }
|
31
|
+
When { collector.run }
|
32
|
+
Then { collector.findings.size.should == 1 }
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::Finding do
|
4
|
+
Given(:message) { "bad code" }
|
5
|
+
Given(:file) { "file" }
|
6
|
+
Given(:line) { 19 }
|
7
|
+
Given(:tags) { ["threads", "info"] }
|
8
|
+
|
9
|
+
context "has a message, location and tags" do
|
10
|
+
When { @finding = JRuby::Lint::Finding.new(message, tags, file, line) }
|
11
|
+
Then { @finding.message.should == message }
|
12
|
+
Then { @finding.tags.should == tags }
|
13
|
+
Then { @finding.file.should == file }
|
14
|
+
Then { @finding.line.should == line }
|
15
|
+
Then { @finding.to_s.should == "#{file}:#{line}: [#{tags.join(', ')}] #{message}" }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "can receive location from SourcePosition" do
|
19
|
+
Given(:source_position) { org.jruby.lexer.yacc.SimpleSourcePosition.new(file, line) }
|
20
|
+
|
21
|
+
When { @finding = JRuby::Lint::Finding.new(message, tags, source_position) }
|
22
|
+
Then { @finding.file.should == file }
|
23
|
+
Then { @finding.line.should == line + 1 }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "converts all tags to strings" do
|
27
|
+
Given(:tags) { [1, :two, 3.0] }
|
28
|
+
When { @finding = JRuby::Lint::Finding.new(message, tags, file, line) }
|
29
|
+
Then { @finding.tags.should == ["1", "two", "3.0"] }
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::Libraries do
|
4
|
+
Given(:cache_dir) { ENV['JRUBY_LINT_CACHE'] }
|
5
|
+
Given(:cache) { JRuby::Lint::Libraries::Cache.new(cache_dir) }
|
6
|
+
|
7
|
+
context "cache" do
|
8
|
+
Given(:cache_dir) { current_dir }
|
9
|
+
|
10
|
+
context "with net access", :requires_net => true do
|
11
|
+
context "fetch" do
|
12
|
+
When { cache.fetch('C-Extension-Alternatives') }
|
13
|
+
Then { check_file_presence('C-Extension-Alternatives.html', true) }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "refreshes a file that's too old" do
|
17
|
+
Given { write_file('C-Extension-Alternatives.html', 'alternatives') }
|
18
|
+
Given(:yesterday) { Time.now - 25 * 60 * 60 }
|
19
|
+
Given { File.utime yesterday, yesterday, File.join(current_dir, 'C-Extension-Alternatives.html')}
|
20
|
+
When { cache.fetch('C-Extension-Alternatives') }
|
21
|
+
Then { File.mtime(File.join(current_dir, 'C-Extension-Alternatives.html')).should > yesterday }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with no net access" do
|
26
|
+
Given { Net::HTTP.should_not_receive(:start) }
|
27
|
+
|
28
|
+
context "store assumes .html extension by default" do
|
29
|
+
When { cache.store('hello.yml', 'hi')}
|
30
|
+
Then { check_file_presence('hello.yml', true) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "store assumes .html extension by default" do
|
34
|
+
When { cache.store('hello', 'hi')}
|
35
|
+
Then { check_file_presence('hello.html', true) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context "fetch should not access net when file is cached" do
|
39
|
+
Given { write_file('hello.html', 'hi') }
|
40
|
+
When { cache.fetch('hello') }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "c extensions list" do
|
46
|
+
Given(:list) { JRuby::Lint::Libraries::CExtensions.new(cache) }
|
47
|
+
When { list.load }
|
48
|
+
Then { list.gems.keys.should include("rdiscount", "rmagick")}
|
49
|
+
end
|
50
|
+
|
51
|
+
context "aggregate information" do
|
52
|
+
Given(:info) { JRuby::Lint::Libraries.new(cache) }
|
53
|
+
When { info.load }
|
54
|
+
Then { info.gems.keys.should include("rdiscount", "rmagick")}
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::Project do
|
4
|
+
Given(:project) { in_current_dir { JRuby::Lint::Project.new.tap {|p| p.reporters.clear } } }
|
5
|
+
|
6
|
+
context "collects Ruby scripts" do
|
7
|
+
Given { write_file('script.rb', '') }
|
8
|
+
When { @collectors = project.collectors }
|
9
|
+
Then { @collectors.size.should == 1 }
|
10
|
+
Then { @collectors.first.should be_instance_of(JRuby::Lint::Collectors::Ruby) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "collects Bundler Gemfiles" do
|
14
|
+
Given { write_file('Gemfile', '') }
|
15
|
+
When { @collectors = project.collectors }
|
16
|
+
Then { @collectors.size.should == 1 }
|
17
|
+
Then { @collectors.first.should be_instance_of(JRuby::Lint::Collectors::Bundler) }
|
18
|
+
end
|
19
|
+
|
20
|
+
context "collects Rakefiles" do
|
21
|
+
Given { write_file('Rakefile', '') }
|
22
|
+
When { @collectors = project.collectors }
|
23
|
+
Then { @collectors.size.should == 1 }
|
24
|
+
Then { @collectors.first.should be_instance_of(JRuby::Lint::Collectors::Rake) }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "collects gemspecs" do
|
28
|
+
Given { write_file('temp.gemspec', '') }
|
29
|
+
When { @collectors = project.collectors }
|
30
|
+
Then { @collectors.size.should == 1 }
|
31
|
+
Then { @collectors.first.should be_instance_of(JRuby::Lint::Collectors::Gemspec) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "aggregates findings from all collectors" do
|
35
|
+
Given(:collector1) do
|
36
|
+
double("collector 1").tap do |c1|
|
37
|
+
c1.should_receive(:run)
|
38
|
+
c1.stub!(:findings).and_return [double("finding 1")]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
Given(:collector2) do
|
42
|
+
double("collector 2").tap do |c2|
|
43
|
+
c2.should_receive(:run)
|
44
|
+
c2.stub!(:findings).and_return [double("finding 2")]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
When { project.collectors.replace([collector1, collector2]) }
|
49
|
+
When { findings = project.run }
|
50
|
+
|
51
|
+
Then { project.findings.size.should == 2 }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "reports findings" do
|
55
|
+
Given(:finding) { double "finding" }
|
56
|
+
|
57
|
+
Given(:collector) do
|
58
|
+
double("collector").tap do |c|
|
59
|
+
c.should_receive(:run)
|
60
|
+
c.stub!(:findings).and_return [finding]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Given(:reporter) do
|
65
|
+
double("reporter").tap do |r|
|
66
|
+
r.should_receive(:report).with([finding])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
When do
|
71
|
+
project.collectors.replace [collector]
|
72
|
+
project.reporters.replace [reporter]
|
73
|
+
end
|
74
|
+
|
75
|
+
Then { project.run }
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe JRuby::Lint::Reporters do
|
4
|
+
Given(:project) { double "project", :tags => %w(warning error info) }
|
5
|
+
|
6
|
+
context "Text reporter" do
|
7
|
+
Given(:reporter) { JRuby::Lint::Reporters::Text.new(project, output) }
|
8
|
+
|
9
|
+
context "with a finding sharing a tag with the project" do
|
10
|
+
Given(:finding) { double "finding", :to_s => "hello", :tags => %w(info) }
|
11
|
+
Given(:output) { double("output").tap {|o| o.should_receive(:puts).with("hello") } }
|
12
|
+
|
13
|
+
Then { reporter.report [finding] }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with a finding sharing no tags with the project" do
|
17
|
+
Given(:finding) { double "finding", :to_s => "hello", :tags => %w(debug) }
|
18
|
+
Given(:output) { double("output").tap {|o| o.should_not_receive(:puts) } }
|
19
|
+
|
20
|
+
Then { reporter.report [finding] }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "Color text reporter" do
|
25
|
+
include Term::ANSIColor
|
26
|
+
Given(:reporter) { JRuby::Lint::Reporters::ANSIColor.new(project, output) }
|
27
|
+
|
28
|
+
context "shows a finding tagged 'error' in red" do
|
29
|
+
Given(:finding) { double "finding", :to_s => "hello", :tags => %w(error) }
|
30
|
+
Given(:output) { double("output").tap {|o| o.should_receive(:puts).with(red("hello")) } }
|
31
|
+
|
32
|
+
Then { reporter.report [finding] }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "shows a finding tagged 'warning' in yellow" do
|
36
|
+
Given(:finding) { double "finding", :to_s => "hello", :tags => %w(warning) }
|
37
|
+
Given(:output) { double("output").tap {|o| o.should_receive(:puts).with(yellow("hello")) } }
|
38
|
+
|
39
|
+
Then { reporter.report [finding] }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|