jruby-lint 0.1
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.
- 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
|