what_weve_got_here_is_an_error_to_communicate 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/Readme.md +9 -7
  4. data/experiments/formatting/other_resources +7 -0
  5. data/lib/error_to_communicate/at_exit.rb +15 -9
  6. data/lib/error_to_communicate/config.rb +55 -22
  7. data/lib/error_to_communicate/exception_info.rb +86 -31
  8. data/lib/error_to_communicate/format_terminal.rb +146 -0
  9. data/lib/error_to_communicate/heuristic/exception.rb +22 -0
  10. data/lib/error_to_communicate/heuristic/load_error.rb +57 -0
  11. data/lib/error_to_communicate/heuristic/no_method_error.rb +35 -0
  12. data/lib/error_to_communicate/heuristic/syntax_error.rb +55 -0
  13. data/lib/error_to_communicate/heuristic/wrong_number_of_arguments.rb +73 -0
  14. data/lib/error_to_communicate/heuristic.rb +54 -0
  15. data/lib/error_to_communicate/project.rb +50 -0
  16. data/lib/error_to_communicate/rspec_formatter.rb +8 -9
  17. data/lib/error_to_communicate/theme.rb +137 -0
  18. data/lib/error_to_communicate/version.rb +2 -2
  19. data/lib/error_to_communicate.rb +4 -2
  20. data/spec/acceptance/exception_spec.rb +2 -4
  21. data/spec/acceptance/load_error_spec.rb +23 -0
  22. data/spec/acceptance/name_error_spec.rb +46 -0
  23. data/spec/acceptance/no_methood_error_spec.rb +6 -8
  24. data/spec/acceptance/runtime_error_spec.rb +27 -0
  25. data/spec/acceptance/short_and_long_require_spec.rb +29 -0
  26. data/spec/acceptance/spec_helper.rb +4 -3
  27. data/spec/acceptance/syntax_error_spec.rb +32 -0
  28. data/spec/acceptance/{argument_error_spec.rb → wrong_number_of_arguments_spec.rb} +1 -1
  29. data/spec/config_spec.rb +120 -0
  30. data/spec/heuristic/exception_spec.rb +17 -0
  31. data/spec/heuristic/load_error_spec.rb +195 -0
  32. data/spec/heuristic/no_method_error_spec.rb +25 -0
  33. data/spec/heuristic/spec_helper.rb +33 -0
  34. data/spec/heuristic/wrong_number_of_arguments_spec.rb +115 -0
  35. data/spec/heuristic_spec.rb +76 -0
  36. data/spec/parsing_exception_info_spec.rb +212 -0
  37. data/spec/rspec_formatter_spec.rb +3 -1
  38. data/spec/spec_helper.rb +28 -1
  39. data/what_weve_got_here_is_an_error_to_communicate.gemspec +2 -2
  40. metadata +29 -19
  41. data/lib/error_to_communicate/format/terminal_helpers.rb +0 -97
  42. data/lib/error_to_communicate/format.rb +0 -132
  43. data/lib/error_to_communicate/parse/backtrace.rb +0 -34
  44. data/lib/error_to_communicate/parse/exception.rb +0 -21
  45. data/lib/error_to_communicate/parse/no_method_error.rb +0 -27
  46. data/lib/error_to_communicate/parse/registry.rb +0 -30
  47. data/lib/error_to_communicate/parse/wrong_number_of_arguments.rb +0 -35
  48. data/spec/parse/backtrace_spec.rb +0 -101
  49. data/spec/parse/exception_spec.rb +0 -14
  50. data/spec/parse/no_method_error_spec.rb +0 -23
  51. data/spec/parse/registered_parsers_spec.rb +0 -68
  52. data/spec/parse/spec_helper.rb +0 -23
  53. data/spec/parse/wrong_number_of_arguments_spec.rb +0 -77
@@ -0,0 +1,73 @@
1
+ require 'error_to_communicate/heuristic'
2
+
3
+ module ErrorToCommunicate
4
+ class Heuristic
5
+ class WrongNumberOfArguments < Heuristic
6
+ def self.for?(einfo)
7
+ extract_from einfo
8
+ end
9
+
10
+ attr_accessor :explanation, :num_expected, :num_received
11
+
12
+ def initialize(*)
13
+ super
14
+ self.num_received, self.num_expected = self.class.extract_from(einfo)
15
+ self.explanation = 'Wrong number of arguments'
16
+ end
17
+
18
+ def semantic_explanation
19
+ [ :message,
20
+ [ [:explanation, explanation],
21
+ [:context, ' (expected '],
22
+ [:details, num_expected],
23
+ [:context, ', sent '],
24
+ [:details, num_received],
25
+ [:context, ')'],
26
+ ]
27
+ ]
28
+ end
29
+
30
+ def semantic_info
31
+ if backtrace.length == 0
32
+ [:context, "Couldn\'t find anything interesting ¯\_(ツ)_/¯\n"]
33
+ elsif backtrace.length == 1
34
+ [:heuristic, [:code, {
35
+ location: backtrace[0],
36
+ highlight: backtrace[0].label,
37
+ context: (-5..5),
38
+ emphasis: :code,
39
+ }]]
40
+ else
41
+ [:heuristic, [
42
+ [:code, {
43
+ location: backtrace[0],
44
+ highlight: backtrace[0].label,
45
+ context: 0..5,
46
+ message: "EXPECTED #{num_expected}",
47
+ emphasis: :code,
48
+ }],
49
+
50
+ [:code, {
51
+ location: backtrace[1],
52
+ highlight: backtrace[0].label,
53
+ context: -5..5,
54
+ message: "SENT #{num_received}",
55
+ emphasis: :code,
56
+ }],
57
+ ]]
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def self.extract_from(einfo)
64
+ case einfo.message
65
+ when /^wrong number of arguments.*?\((\d+) for (\d+)\)$/ # MRI / JRuby
66
+ num_received, num_expected = $1.to_i, $2.to_i
67
+ when /^method '.*?': given (\d+).*? expected (\d+)$/ # RBX
68
+ num_received, num_expected = $1.to_i, $2.to_i
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,54 @@
1
+ module ErrorToCommunicate
2
+ class Heuristic
3
+ def self.for?(einfo)
4
+ raise NotImplementedError, "#{self} needs to implement .for? (subclass responsibility)"
5
+ end
6
+
7
+ attr_accessor :einfo, :project
8
+
9
+ def initialize(attributes)
10
+ self.einfo = attributes.fetch :einfo
11
+ self.project = attributes.fetch :project
12
+ end
13
+
14
+ def classname
15
+ einfo.classname
16
+ end
17
+
18
+ def backtrace
19
+ einfo.backtrace
20
+ end
21
+
22
+ def explanation
23
+ einfo.message
24
+ end
25
+
26
+ def semantic_explanation
27
+ explanation
28
+ end
29
+
30
+ def semantic_summary
31
+ [:summary, [
32
+ [:columns,
33
+ [:classname, classname],
34
+ [:explanation, semantic_explanation]]]]
35
+ end
36
+
37
+ def semantic_info
38
+ [:null]
39
+ end
40
+
41
+ def semantic_backtrace
42
+ [:backtrace,
43
+ backtrace.map do |location|
44
+ [:code, {
45
+ location: location,
46
+ highlight: (location.pred && location.pred.label),
47
+ context: 0..0,
48
+ emphasis: :path,
49
+ }]
50
+ end
51
+ ]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ module ErrorToCommunicate
2
+ class Project
3
+ def self.find_rubygems_dir(loaded_features)
4
+ dir = loaded_features.grep(/\/rubygems\/specification.rb/).first
5
+ dir && File.dirname(dir)
6
+ end
7
+
8
+ # Things this should possibly deal with?
9
+ # Gemfile?
10
+ # what gems are in it?
11
+ # what load paths do these make available?
12
+ # directory structure
13
+ # what paths are in the lib?
14
+ # what dirs are in the lib?
15
+ # home_dir
16
+ # available_gems
17
+ # common_gems (maybe they meant to require something from a common gem)
18
+ # $LOAD_PATH
19
+ # What all is in it? Is a dir misspelled?
20
+
21
+ attr_accessor :root
22
+
23
+ def initialize(attributes={})
24
+ attributes.each { |name, value| __send__ :"#{name}=", value }
25
+ end
26
+
27
+ def rubygems?
28
+ !rubygems_dir
29
+ end
30
+
31
+ def loaded_features
32
+ @loaded_features ||= []
33
+ end
34
+
35
+ def loaded_features=(loaded_features)
36
+ @loaded_features = loaded_features
37
+ end
38
+
39
+ attr_writer :rubygems_dir
40
+ def rubygems_dir
41
+ @rubygems_dir ||= self.class.find_rubygems_dir(loaded_features)
42
+ end
43
+
44
+ private
45
+
46
+ def extract(attributes, key, set_default=nil)
47
+ attributes.delete(key) || __send__(set_default)
48
+ end
49
+ end
50
+ end
@@ -1,8 +1,7 @@
1
1
  require 'error_to_communicate'
2
- require 'error_to_communicate/format'
3
2
  require 'rspec/core/formatters/documentation_formatter'
4
3
 
5
- module WhatWeveGotHereIsAnErrorToCommunicate
4
+ module ErrorToCommunicate
6
5
  class RSpecFormatter < RSpec::Core::Formatters::DocumentationFormatter
7
6
  # Register for notifications from our parent classes
8
7
  # http://rspec.info/documentation/3.2/rspec-core/RSpec/Core/Formatters.html
@@ -13,7 +12,7 @@ module WhatWeveGotHereIsAnErrorToCommunicate
13
12
  # }
14
13
  RSpec::Core::Formatters.register self
15
14
 
16
- # Use WhatWeveGotHereIsAnErrorToCommunicate to print error info
15
+ # Use ErrorToCommunicate to print error info
17
16
  # rather than default DocumentationFormatter.
18
17
  #
19
18
  # How did we figure out how to implement it?
@@ -23,7 +22,7 @@ module WhatWeveGotHereIsAnErrorToCommunicate
23
22
  # FIXME: Needs to respect RSpec.configuration.color_enabled?
24
23
  # but we can't currently turn colour off in our output
25
24
  def dump_failures(notification)
26
- formatted = "\nFailures:\n"
25
+ result = "\nFailures:\n"
27
26
  notification.failure_notifications.each_with_index do |failure, failure_number|
28
27
  # get the exception with the modified backtrace
29
28
  exception = failure.exception.dup
@@ -33,14 +32,14 @@ module WhatWeveGotHereIsAnErrorToCommunicate
33
32
  .format_backtrace(exception.backtrace, metadata)
34
33
 
35
34
  # format it with our lib
36
- exception_info = ErrorToCommunicate::CONFIG.parse(exception)
37
- formatted_exception = ErrorToCommunicate.format(exception_info)
35
+ heuristic = ErrorToCommunicate::Config.default.heuristic_for exception
36
+ formatted = ErrorToCommunicate::Config.default.format heuristic, Dir.pwd
38
37
 
39
38
  # fit it into the larger failure summary
40
- formatted << "\n #{failure_number+1}) #{failure.description}\n"
41
- formatted << formatted_exception.chomp.gsub(/^/, ' ')
39
+ result << "\n #{failure_number+1}) #{failure.description}\n"
40
+ result << formatted.chomp.gsub(/^/, ' ')
42
41
  end
43
- output.puts formatted
42
+ output.puts result
44
43
  end
45
44
  end
46
45
  end
@@ -0,0 +1,137 @@
1
+ module ErrorToCommunicate
2
+ class Theme
3
+ # ----- Semantic -----
4
+ # TODO: Not good enough, see note on FormatTerminal#format
5
+
6
+ def columns(*content)
7
+ content.join(' | ') + "\n"
8
+ end
9
+
10
+ def classname(classname)
11
+ "#{white}#{classname}#{none}"
12
+ end
13
+
14
+ def message(message)
15
+ "#{bri_red}#{message}#{none}"
16
+ end
17
+
18
+ def explanation(explanation)
19
+ "#{bri_red}#{explanation}#{none}"
20
+ end
21
+
22
+ def context(context)
23
+ "#{dim_red}#{context}#{none}"
24
+ end
25
+
26
+ def details(details)
27
+ "#{white}#{details}#{none}"
28
+ end
29
+
30
+ def mark_linenum(linenum)
31
+ "#{bri_red}#{linenum}#{none}"
32
+ end
33
+
34
+ # --------------------------------
35
+
36
+ def separator_line
37
+ ("="*70) << "\n"
38
+ end
39
+
40
+ def color_path(str)
41
+ "\e[38;5;36m#{str}\e[39m" # fg r:0, g:3, b:2 (out of 0..5)
42
+ end
43
+
44
+ def color_linenum(linenum)
45
+ "\e[34m#{linenum}\e[39m"
46
+ end
47
+
48
+ def invert(text)
49
+ "\e[7m#{text}\e[27m"
50
+ end
51
+
52
+ def screaming_red(text)
53
+ return "" if text.empty?
54
+ "\e[38;5;255;48;5;88m #{text} \e[39;49m" # bright white on medium red
55
+ end
56
+
57
+ def underline(str)
58
+ "\e[4m#{str}\e[24m"
59
+ end
60
+
61
+ def color_filename(str)
62
+ "\e[38;5;49;1m#{str}\e[39m" # fg r:0, g:5, b:3 (out of 0..5)
63
+ end
64
+
65
+ def desaturate(str)
66
+ nocolor = str.gsub(/\e\[[\d;]+?m/, "")
67
+ allgray = nocolor.gsub(/^(.*?)\n?$/, "\e[38;5;240m\\1\e[39m\n")
68
+ allgray
69
+ end
70
+
71
+ # For a list of themes:
72
+ # https://github.com/JoshCheek/what-we-ve-got-here-is-an-error-to-communicate/issues/36#issuecomment-91200262
73
+ def syntax_highlight(raw_code)
74
+ formatter = Rouge::Formatters::Terminal256.new theme: 'github'
75
+ lexer = Rouge::Lexers::Ruby.new
76
+ tokens = lexer.lex raw_code
77
+ formatted = formatter.format(tokens)
78
+ formatted << "\n" unless formatted.end_with? "\n"
79
+ remove_ansi_codes_after_last_newline(formatted)
80
+ end
81
+
82
+ def remove_ansi_codes_after_last_newline(text)
83
+ *neck, ass = text.lines # ... kinda like head/tail :P
84
+ return text unless neck.any? && ass[/^(\e\[(\d+;?)*m)*$/]
85
+ neck.join("").chomp << ass
86
+ end
87
+
88
+ def highlight_text(code, index, text)
89
+ lines = code.lines
90
+ return code unless text && lines[index]
91
+ lines[index].gsub! text, invert(text)
92
+ lines.join("")
93
+ end
94
+
95
+ def indent(str, indentation_str)
96
+ str.gsub /^/, indentation_str
97
+ end
98
+
99
+ # TODO: rename these to all imply foreground
100
+ def white
101
+ fg_rgb 5, 5, 5
102
+ end
103
+
104
+ def bri_red
105
+ fg_rgb 5, 0, 0
106
+ end
107
+
108
+ def dim_red
109
+ fg_rgb 3, 0, 0
110
+ end
111
+
112
+ def none
113
+ "\e[39m"
114
+ end
115
+
116
+ # Each of r, g, b, can have a value 0-5.
117
+ # If you want to do 0-255, you need to pass 255 as max.
118
+ # The terminal cans till only display 0-5,
119
+ # but you can pass your rgb values in, and we'll translate them here
120
+ def rgb(red, green, blue, max=6)
121
+ max = max.to_f
122
+ n = 6
123
+
124
+ # translate each colour to 0-5
125
+ r = (n * red / max).to_i
126
+ g = (n * green / max).to_i
127
+ b = (n * blue / max).to_i
128
+
129
+ # move them to their offsets, I think the first 16 are for the system colors
130
+ 16 + r*(n**2) + g*(n**1) + b*(n**0)
131
+ end
132
+
133
+ def fg_rgb(red, green, blue, max=6)
134
+ "\e[38;5;#{rgb red, green, blue, max}m"
135
+ end
136
+ end
137
+ end
@@ -1,3 +1,3 @@
1
- module WhatWeveGotHereIsAnErrorToCommunicate
2
- VERSION = '0.0.1'.freeze
1
+ module ErrorToCommunicate
2
+ VERSION = '0.0.2'.freeze
3
3
  end
@@ -1,3 +1,5 @@
1
1
  require 'error_to_communicate/config'
2
- ErrorToCommunicate = WhatWeveGotHereIsAnErrorToCommunicate
3
- ErrorToCommunicate::CONFIG = ErrorToCommunicate::Config.new
2
+
3
+ # We will use the shorthand for convenience,
4
+ # but include the pattern-based name, for politeness :)
5
+ WhatWeveGotHereIsAnErrorToCommunicate = ErrorToCommunicate
@@ -2,22 +2,20 @@ require 'acceptance/spec_helper'
2
2
 
3
3
  RSpec.context 'Exception', acceptance: true do
4
4
  it 'Prints heuristics' do
5
- # Given a file with three lines in the backtrace that explodes on the third
6
5
  write_file 'exception.rb', <<-BODY
7
6
  require "what_weve_got_here_is_an_error_to_communicate"
8
7
  raise Exception, "mah message"
9
8
  BODY
10
9
 
11
10
  invocation = ruby 'exception.rb'
12
- stderr = strip_color invocation.stderr
11
+ stderr = strip_color invocation.stderr
13
12
 
14
13
  # sanity
15
14
  expect(invocation.stdout).to eq ''
16
15
  expect(invocation.exitstatus).to eq 1
17
16
 
18
17
  # error: It prints the exception class and prints the message
19
- expect(stderr).to include 'Exception'
20
- expect(stderr).to include 'mah message'
18
+ expect(stderr).to match /Exception.*?mah message/
21
19
 
22
20
  # heuristic displays the line the exception was raised at, and some context
23
21
  expect(stderr).to include 'raise Exception, "mah message"'
@@ -0,0 +1,23 @@
1
+ require 'acceptance/spec_helper'
2
+
3
+ RSpec.context 'LoadError', acceptance: true do
4
+ it 'Prints heuristics' do
5
+ write_file 'load_error.rb', <<-BODY
6
+ require "what_weve_got_here_is_an_error_to_communicate"
7
+ require "not/a/real/dir"
8
+ BODY
9
+
10
+ invocation = ruby 'load_error.rb'
11
+ stderr = strip_color invocation.stderr
12
+
13
+ # sanity
14
+ expect(invocation.stdout).to eq ''
15
+ expect(invocation.exitstatus).to eq 1
16
+
17
+ # error: It prints the exception class and prints the message
18
+ expect(stderr).to match %r(LoadError.*?load.*?not/a/real/dir)
19
+
20
+ # heuristic displays the line the exception was raised at, and some context
21
+ expect(stderr).to match %r(2:.*?require "not/a/real/dir".*?Couldn't find "not/a/real/dir")
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ require 'acceptance/spec_helper'
2
+
3
+ RSpec.describe 'NameError', acceptance: true do
4
+ it 'Prints heuristics' do
5
+ write_file 'name_error.rb', <<-BODY
6
+ require 'error_to_communicate/at_exit'
7
+ module Test
8
+ def self.backtrace1
9
+ end
10
+
11
+ def self.backtrace2
12
+ backtrace4
13
+ end
14
+
15
+ def self.backtrace3
16
+ end
17
+ end
18
+
19
+ Test.backtrace2
20
+ BODY
21
+
22
+ invocation = ruby 'name_error.rb'
23
+ stderr = strip_color invocation.stderr
24
+
25
+ # sanity
26
+ expect(invocation.stdout).to eq ''
27
+ expect(invocation.exitstatus).to eq 1
28
+
29
+ # error: It prints the exception class and message
30
+ expect(stderr).to match /NameError.*?undefined local variable or method `backtrace4'/
31
+
32
+ # heuristic:
33
+ # It displays a helpful message next to the callsite
34
+ expect(stderr).to match /backtrace4.*?backtrace4 is undefined/
35
+
36
+ # It displays the line of the backtrace with the NoMethodError, and some context around it
37
+ expect(stderr).to include 'def self.backtrace2'
38
+ expect(stderr).to include ' backtrace4'
39
+ expect(stderr).to include 'end'
40
+ expect(stderr).to_not include 'error_to_communicate/at_exit'
41
+
42
+ # backtrace: It displays each line of the backtrace and includes the code from that line
43
+ expect(stderr).to match /name_error\.rb:7\n.*?backtrace4/
44
+ expect(stderr).to match /name_error\.rb:14\n.*?Test.backtrace2/
45
+ end
46
+ end
@@ -2,7 +2,7 @@ require 'acceptance/spec_helper'
2
2
 
3
3
  RSpec.describe 'NoMethodError', acceptance: true do
4
4
  it 'Prints heuristics' do
5
- # Given a file that throws an error with two lines in the backtrace
5
+ # a file that throws an error with two lines in the backtrace
6
6
  write_file 'no_method_error.rb', <<-BODY
7
7
  require 'error_to_communicate/at_exit'
8
8
  module Test
@@ -32,19 +32,17 @@ RSpec.describe 'NoMethodError', acceptance: true do
32
32
  expect(stderr).to include "undefined method `backtrace4'"
33
33
 
34
34
  # heuristic:
35
- # It displays a hlepful message next to the callsite
36
- expect(stderr).to include 'backtrace4 is undefined'
35
+ # It displays a helpful message next to the callsite
36
+ expect(stderr).to match /Test\.backtrace4.*?backtrace4 is undefined/
37
37
 
38
38
  # It displays the line of the backtrace with the NoMethodError, and some context around it
39
39
  expect(stderr).to include 'def self.backtrace2'
40
40
  expect(stderr).to include ' Test.backtrace4'
41
41
  expect(stderr).to include 'end'
42
+ expect(stderr).to_not include 'error_to_communicate/at_exit'
42
43
 
43
44
  # backtrace: It displays each line of the backtrace and includes the code from that line
44
- expect(stderr).to include 'no_method_error.rb:7'
45
- expect(stderr).to include 'backtrace4'
46
-
47
- expect(stderr).to include '14'
48
- expect(stderr).to include 'backtrace2'
45
+ expect(stderr).to match /no_method_error\.rb:7\n.*?backtrace4/
46
+ expect(stderr).to match /no_method_error\.rb:14\n.*?Test.backtrace2/
49
47
  end
50
48
  end
@@ -0,0 +1,27 @@
1
+ require 'acceptance/spec_helper'
2
+
3
+ RSpec.describe 'RuntimeError', acceptance: true do
4
+ it 'Prints heuristics' do
5
+ write_file 'runtime_error.rb', <<-BODY
6
+ require 'error_to_communicate/at_exit'
7
+ raise 'omg'
8
+ 1
9
+ BODY
10
+
11
+ invocation = ruby 'runtime_error.rb'
12
+ stderr = strip_color invocation.stderr
13
+
14
+ # sanity
15
+ expect(invocation.stdout).to eq ''
16
+ expect(invocation.exitstatus).to eq 1
17
+
18
+ # error: It prints the exception class and message
19
+ expect(stderr).to match /RuntimeError.*?omg/
20
+
21
+ # heuristic:
22
+ # it displays the error and some context
23
+ expect(stderr).to match /1:.*?require 'error_to_communicate\/at_exit'/
24
+ expect(stderr).to match /2:.*?raise 'omg'/
25
+ expect(stderr).to match /3:.*?1/
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ require 'acceptance/spec_helper'
2
+
3
+ RSpec.context 'Short and long require statements', acceptance: true do
4
+ it 'can require error_to_communicate' do
5
+ write_file 'require_shorthand.rb', <<-BODY
6
+ require 'error_to_communicate/at_exit'
7
+ ErrorToCommunicate
8
+ ErrorToCommunicate
9
+ BODY
10
+
11
+ invocation = ruby 'load_shorthand.rb'
12
+ expect(invocation.stdout).to eq ''
13
+ expect(invocation.stdout).to eq ''
14
+ expect(invocation.exitstatus).to eq 1
15
+ end
16
+
17
+ it 'can require what_weve_got_here_is_an_error_to_communicate' do
18
+ write_file 'require_longhand.rb', <<-BODY
19
+ require 'what_weve_got_here_is_an_error_to_communicate'
20
+ ErrorToCommunicate
21
+ ErrorToCommunicate
22
+ BODY
23
+
24
+ invocation = ruby 'load_longhand.rb'
25
+ expect(invocation.stdout).to eq ''
26
+ expect(invocation.stdout).to eq ''
27
+ expect(invocation.exitstatus).to eq 1
28
+ end
29
+ end
@@ -17,10 +17,11 @@ module AcceptanceSpecHelpers
17
17
  File.join root_dir, 'proving_grounds'
18
18
  end
19
19
 
20
- def ruby(filename)
20
+ def ruby(*args)
21
21
  # workaround for JRuby bug (capture3 calls open3 with invalid args, needs to splat an array, but doesn't)
22
22
  in_proving_grounds do
23
- Haiti::CommandLineHelpers::Invocation.new *Open3.capture3(ENV, 'ruby', '-I', lib_dir, filename)
23
+ out, err, status = Open3.capture3 ENV, 'ruby', '-I', lib_dir, *args
24
+ Haiti::CommandLineHelpers::Invocation.new out, err, status
24
25
  end
25
26
  end
26
27
 
@@ -37,5 +38,5 @@ end
37
38
  RSpec.configure do |config|
38
39
  # Helpers for acceptance tests
39
40
  config.before(acceptance: true) { make_proving_grounds }
40
- config.include AcceptanceSpecHelpers, acceptance: true
41
+ config.include AcceptanceSpecHelpers, acceptance: true
41
42
  end
@@ -0,0 +1,32 @@
1
+ require 'acceptance/spec_helper'
2
+
3
+ RSpec.describe 'SyntaxError', acceptance: true do
4
+ it 'Prints heuristics' do
5
+ write_file 'simple_syntax_error.rb', <<-BODY
6
+ 100
7
+ "abc" 200
8
+ 300
9
+ BODY
10
+
11
+ write_file 'requires_simple_sintax_error.rb', <<-BODY
12
+ require "what_weve_got_here_is_an_error_to_communicate"
13
+ require_relative 'simple_syntax_error'
14
+ BODY
15
+
16
+ invocation = ruby 'requires_simple_sintax_error.rb'
17
+ stderr = strip_color invocation.stderr
18
+
19
+ # sanity
20
+ expect(invocation.stdout).to eq ''
21
+ expect(invocation.exitstatus).to eq 1
22
+
23
+ # error: It prints the exception class and message
24
+ expect(stderr).to match /SyntaxError.*?unexpected.*?expected/i
25
+
26
+ # heuristic:
27
+ # it displays the error and some context
28
+ expect(stderr).to match /1:.*?100/
29
+ expect(stderr).to match /2:.*"abc" 200.*?unexpected/
30
+ expect(stderr).to match /3:.*?300/
31
+ end
32
+ end
@@ -1,6 +1,6 @@
1
1
  require 'acceptance/spec_helper'
2
2
 
3
- RSpec.context 'ArgumentError', acceptance: true do
3
+ RSpec.context 'WrongNumberOfArguments', acceptance: true do
4
4
  it 'Prints heuristics' do
5
5
  # Given a file with three lines in the backtrace that explodes on the third
6
6
  write_file 'argument_error.rb', <<-BODY