what_weve_got_here_is_an_error_to_communicate 0.0.1 → 0.0.2

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.
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