transpec 1.9.3 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +5 -0
  5. data/CONTRIBUTING.md +19 -0
  6. data/README.md +78 -9
  7. data/README.md.erb +68 -10
  8. data/lib/transpec/annotatable.rb +16 -0
  9. data/lib/transpec/cli.rb +35 -27
  10. data/lib/transpec/configuration.rb +1 -0
  11. data/lib/transpec/conversion_error.rb +23 -0
  12. data/lib/transpec/converter.rb +59 -50
  13. data/lib/transpec/dynamic_analyzer.rb +13 -29
  14. data/lib/transpec/dynamic_analyzer/rewriter.rb +3 -10
  15. data/lib/transpec/dynamic_analyzer/runtime_data.rb +16 -8
  16. data/lib/transpec/file_finder.rb +1 -1
  17. data/lib/transpec/git.rb +1 -1
  18. data/lib/transpec/option_parser.rb +12 -10
  19. data/lib/transpec/project.rb +3 -3
  20. data/lib/transpec/record.rb +19 -2
  21. data/lib/transpec/report.rb +29 -13
  22. data/lib/transpec/rspec_version.rb +7 -7
  23. data/lib/transpec/static_context_inspector.rb +1 -5
  24. data/lib/transpec/syntax.rb +11 -16
  25. data/lib/transpec/syntax/be_boolean.rb +1 -1
  26. data/lib/transpec/syntax/be_close.rb +1 -1
  27. data/lib/transpec/syntax/current_example.rb +88 -0
  28. data/lib/transpec/syntax/double.rb +1 -1
  29. data/lib/transpec/syntax/example.rb +60 -54
  30. data/lib/transpec/syntax/have.rb +27 -15
  31. data/lib/transpec/syntax/have/have_record.rb +12 -0
  32. data/lib/transpec/syntax/have/source_builder.rb +18 -16
  33. data/lib/transpec/syntax/its.rb +12 -11
  34. data/lib/transpec/syntax/matcher_definition.rb +1 -1
  35. data/lib/transpec/syntax/method_stub.rb +3 -7
  36. data/lib/transpec/syntax/mixin/matcher_owner.rb +2 -2
  37. data/lib/transpec/syntax/mixin/monkey_patch.rb +3 -5
  38. data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +2 -4
  39. data/lib/transpec/syntax/mixin/owned_matcher.rb +1 -4
  40. data/lib/transpec/syntax/mixin/send.rb +7 -9
  41. data/lib/transpec/syntax/oneliner_should.rb +4 -4
  42. data/lib/transpec/syntax/operator.rb +27 -11
  43. data/lib/transpec/syntax/pending.rb +110 -0
  44. data/lib/transpec/syntax/raise_error.rb +1 -1
  45. data/lib/transpec/syntax/receive.rb +4 -4
  46. data/lib/transpec/syntax/rspec_configure/framework.rb +3 -3
  47. data/lib/transpec/syntax/should.rb +2 -2
  48. data/lib/transpec/syntax/should_receive.rb +3 -3
  49. data/lib/transpec/util.rb +38 -6
  50. data/lib/transpec/version.rb +2 -2
  51. data/spec/support/file_helper.rb +1 -1
  52. data/spec/support/shared_context.rb +3 -8
  53. data/spec/transpec/cli_spec.rb +63 -1
  54. data/spec/transpec/configuration_spec.rb +1 -0
  55. data/spec/transpec/converter_spec.rb +106 -15
  56. data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +12 -52
  57. data/spec/transpec/dynamic_analyzer_spec.rb +2 -2
  58. data/spec/transpec/option_parser_spec.rb +3 -2
  59. data/spec/transpec/report_spec.rb +33 -4
  60. data/spec/transpec/rspec_version_spec.rb +5 -2
  61. data/spec/transpec/syntax/current_example_spec.rb +267 -0
  62. data/spec/transpec/syntax/example_spec.rb +156 -122
  63. data/spec/transpec/syntax/have_spec.rb +43 -32
  64. data/spec/transpec/syntax/method_stub_spec.rb +8 -0
  65. data/spec/transpec/syntax/operator_spec.rb +67 -2
  66. data/spec/transpec/syntax/pending_spec.rb +375 -0
  67. metadata +12 -4
  68. data/lib/transpec/context_error.rb +0 -23
@@ -10,7 +10,7 @@ module Transpec
10
10
  def command_available?
11
11
  ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
12
12
  git_path = File.join(path, GIT)
13
- File.exists?(git_path)
13
+ File.exist?(git_path)
14
14
  end
15
15
  end
16
16
 
@@ -18,6 +18,7 @@ module Transpec
18
18
  stub: :convert_stub=,
19
19
  have_items: :convert_have_items=,
20
20
  its: :convert_its=,
21
+ pending: :convert_pending=,
21
22
  deprecated: :convert_deprecated_method=
22
23
  }
23
24
 
@@ -51,27 +52,27 @@ module Transpec
51
52
  @parser = create_parser
52
53
 
53
54
  define_option('-f', '--force') do
54
- @configuration.forced = true
55
+ configuration.forced = true
55
56
  end
56
57
 
57
58
  define_option('-s', '--skip-dynamic-analysis') do
58
- @configuration.skip_dynamic_analysis = true
59
+ configuration.skip_dynamic_analysis = true
59
60
  end
60
61
 
61
62
  define_option('-c', '--rspec-command COMMAND') do |command|
62
- @configuration.rspec_command = command
63
+ configuration.rspec_command = command
63
64
  end
64
65
 
65
66
  define_option('-k', '--keep TYPE[,TYPE...]') do |types|
66
67
  types.split(',').each do |type|
67
68
  config_attr = CONFIG_ATTRS_FOR_KEEP_TYPES[type.to_sym]
68
69
  fail ArgumentError, "Unknown syntax type #{type.inspect}" unless config_attr
69
- @configuration.send(config_attr, false)
70
+ configuration.send(config_attr, false)
70
71
  end
71
72
  end
72
73
 
73
74
  define_option('-n', '--negative-form FORM') do |form|
74
- @configuration.negative_form_of_to = form
75
+ configuration.negative_form_of_to = form
75
76
  end
76
77
 
77
78
  define_option('-b', '--boolean-matcher TYPE') do |type|
@@ -79,20 +80,20 @@ module Transpec
79
80
  types = VALID_BOOLEAN_MATCHER_TYPES.map(&:inspect).join(', ')
80
81
  fail ArgumentError, "Boolean matcher type must be any of #{types}"
81
82
  end
82
- @configuration.boolean_matcher_type = type.include?('truthy') ? :conditional : :exact
83
- @configuration.form_of_be_falsey = type.include?('falsy') ? 'be_falsy' : 'be_falsey'
83
+ configuration.boolean_matcher_type = type.include?('truthy') ? :conditional : :exact
84
+ configuration.form_of_be_falsey = type.include?('falsy') ? 'be_falsy' : 'be_falsey'
84
85
  end
85
86
 
86
87
  define_option('-a', '--no-yield-any-instance') do
87
- @configuration.add_receiver_arg_to_any_instance_implementation_block = false
88
+ configuration.add_receiver_arg_to_any_instance_implementation_block = false
88
89
  end
89
90
 
90
91
  define_option('-t', '--convert-stub-with-hash') do
91
- @configuration.convert_stub_with_hash_to_stub_and_return = true
92
+ configuration.convert_stub_with_hash_to_stub_and_return = true
92
93
  end
93
94
 
94
95
  define_option('-p', '--no-parentheses-matcher-arg') do
95
- @configuration.parenthesize_matcher_arg = false
96
+ configuration.parenthesize_matcher_arg = false
96
97
  end
97
98
 
98
99
  define_option('--no-color') do
@@ -145,6 +146,7 @@ module Transpec
145
146
  " #{'stub'.bright} (to #{'allow(obj).to receive'.underline})",
146
147
  " #{'have_items'.bright} (to #{'expect(obj.size).to eq(n)'.underline})",
147
148
  " #{'its'.bright} (to #{'describe { subject { }; it { } }'.underline})",
149
+ " #{'pending'.bright} (to #{'skip'.underline})",
148
150
  " #{'deprecated'.bright} (e.g. from #{'mock'.underline} to #{'double'.underline})",
149
151
  'These are all converted by default.'
150
152
  ],
@@ -12,11 +12,11 @@ module Transpec
12
12
  end
13
13
 
14
14
  def basename
15
- File.basename(@path)
15
+ File.basename(path)
16
16
  end
17
17
 
18
18
  def require_bundler?
19
- gemfile_path = File.join(@path, 'Gemfile')
19
+ gemfile_path = File.join(path, 'Gemfile')
20
20
  File.exist?(gemfile_path)
21
21
  end
22
22
 
@@ -44,7 +44,7 @@ module Transpec
44
44
 
45
45
  output = nil
46
46
 
47
- Dir.chdir(@path) do
47
+ Dir.chdir(path) do
48
48
  with_bundler_clean_env do
49
49
  IO.popen(command) do |io|
50
50
  output = io.read
@@ -1,12 +1,15 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'transpec/annotatable'
4
+
3
5
  module Transpec
4
6
  class Record
5
- attr_reader :original_syntax, :converted_syntax
7
+ attr_reader :original_syntax, :converted_syntax, :annotation
6
8
 
7
- def initialize(original_syntax, converted_syntax)
9
+ def initialize(original_syntax, converted_syntax, annotation = nil)
8
10
  @original_syntax = original_syntax
9
11
  @converted_syntax = converted_syntax
12
+ @annotation = annotation
10
13
  end
11
14
 
12
15
  def ==(other)
@@ -25,4 +28,18 @@ module Transpec
25
28
  "`#{original_syntax}` -> `#{converted_syntax}`"
26
29
  end
27
30
  end
31
+
32
+ class Annotation
33
+ include Annotatable
34
+ end
35
+
36
+ class AccuracyAnnotation < Annotation
37
+ def initialize(source_range)
38
+ message = "The `#{source_range.source}` has been converted " \
39
+ 'but it might possibly be incorrect ' \
40
+ 'due to a lack of runtime information. ' \
41
+ "It's recommended to review the change carefully."
42
+ super(message, source_range)
43
+ end
44
+ end
28
45
  end
@@ -4,14 +4,21 @@ require 'rainbow'
4
4
 
5
5
  module Transpec
6
6
  class Report
7
- attr_reader :records, :context_errors, :syntax_errors
7
+ attr_reader :records, :conversion_errors, :syntax_errors
8
8
 
9
9
  def initialize
10
10
  @records = []
11
- @context_errors = []
11
+ @conversion_errors = []
12
12
  @syntax_errors = []
13
13
  end
14
14
 
15
+ def <<(other)
16
+ records.concat(other.records)
17
+ conversion_errors.concat(other.conversion_errors)
18
+ syntax_errors.concat(other.syntax_errors)
19
+ self
20
+ end
21
+
15
22
  def unique_record_counts
16
23
  record_counts = Hash.new(0)
17
24
 
@@ -40,7 +47,15 @@ module Transpec
40
47
  end
41
48
 
42
49
  def colored_stats
43
- convertion_and_incomplete_stats + error_stats
50
+ base_color = if !conversion_errors.empty?
51
+ :magenta
52
+ elsif annotation_count.nonzero?
53
+ :yellow
54
+ else
55
+ :green
56
+ end
57
+
58
+ convertion_incomplete_caution_stats(base_color) + error_stats(base_color)
44
59
  end
45
60
 
46
61
  def stats
@@ -78,21 +93,18 @@ module Transpec
78
93
  text << indentation + ' ' + colorize('to: ', :cyan) + record.converted_syntax + "\n"
79
94
  end
80
95
 
81
- def convertion_and_incomplete_stats
82
- color = context_errors.empty? ? :green : :yellow
83
-
96
+ def convertion_incomplete_caution_stats(color)
84
97
  text = pluralize(records.count, 'conversion') + ', '
85
- text << pluralize(context_errors.count, 'incomplete') + ', '
98
+ text << pluralize(conversion_errors.count, 'incomplete') + ', '
99
+ text << pluralize(annotation_count, 'caution') + ', '
86
100
  colorize(text, color)
87
101
  end
88
102
 
89
- def error_stats
90
- color = if !syntax_errors.empty?
91
- :red
92
- elsif context_errors.empty?
93
- :green
103
+ def error_stats(base_color)
104
+ color = if syntax_errors.empty?
105
+ base_color
94
106
  else
95
- :yellow
107
+ :red
96
108
  end
97
109
 
98
110
  colorize(pluralize(syntax_errors.count, 'error'), color)
@@ -112,5 +124,9 @@ module Transpec
112
124
 
113
125
  text
114
126
  end
127
+
128
+ def annotation_count
129
+ records.count(&:annotation)
130
+ end
115
131
  end
116
132
  end
@@ -34,27 +34,27 @@ module Transpec
34
34
  end
35
35
 
36
36
  def <=>(other)
37
- @gem_version <=> other.gem_version
37
+ gem_version <=> other.gem_version
38
38
  end
39
39
 
40
40
  def to_s
41
- @gem_version.to_s
41
+ gem_version.to_s
42
42
  end
43
43
 
44
44
  define_feature :be_truthy, '2.99.0.beta1'
45
45
  define_feature :yielded_example, '2.99.0.beta1'
46
46
  define_feature :yielding_receiver_to_any_instance_implementation_block, '2.99.0.beta1'
47
47
  define_feature :oneliner_is_expected, '2.99.0.beta2', except: '3.0.0.beta1'
48
+ define_feature :skip, '2.99.0.beta2', except: '3.0.0.beta1'
48
49
  define_feature :receive_messages, '3.0.0.beta1'
49
50
  define_feature :receive_message_chain, '3.0.0.beta2'
50
51
  define_feature :non_should_matcher_protocol, '3.0.0.beta2'
51
52
 
52
- ANY_INSTANCE_IMPLEMENTATION_BLOCK_MIGRATION_BEGIN = new('2.99.0.beta1')
53
- ANY_INSTANCE_IMPLEMENTATION_BLOCK_MIGRATION_EXCLUSIVE_END = new('3.0.0.beta1')
53
+ RSPEC_2_99 = new('2.99.0.beta1')
54
+ RSPEC_3_0 = new('3.0.0.beta1')
54
55
 
55
- def migration_term_of_any_instance_implementation_block?
56
- self >= ANY_INSTANCE_IMPLEMENTATION_BLOCK_MIGRATION_BEGIN &&
57
- self < ANY_INSTANCE_IMPLEMENTATION_BLOCK_MIGRATION_EXCLUSIVE_END
56
+ def rspec_2_99?
57
+ RSPEC_2_99 <= self && self < RSPEC_3_0
58
58
  end
59
59
  end
60
60
  end
@@ -48,10 +48,6 @@ module Transpec
48
48
  end
49
49
  end
50
50
 
51
- def inside_of_example_group?
52
- scopes.include?(:example_group)
53
- end
54
-
55
51
  def non_monkey_patch_expectation_available?
56
52
  return @expectation_available if instance_variable_defined?(:@expectation_available)
57
53
  @expectation_available = match_scopes(NON_MONKEY_PATCH_EXPECTATION_AVAILABLE_CONTEXT)
@@ -72,7 +68,7 @@ module Transpec
72
68
  def valid_ancestor_nodes
73
69
  valid_nodes = []
74
70
 
75
- self_and_ancestor_nodes = [@node] + @node.ancestor_nodes
71
+ self_and_ancestor_nodes = [node] + node.ancestor_nodes
76
72
 
77
73
  self_and_ancestor_nodes.each_cons(2) do |child, parent|
78
74
  valid_nodes << parent unless belong_to_direct_outer_scope?(child)
@@ -1,9 +1,10 @@
1
1
  # coding: utf-8
2
2
 
3
- require 'transpec/context_error'
4
- require 'transpec/static_context_inspector'
3
+ require 'transpec/conversion_error'
4
+ require 'transpec/dynamic_analyzer/runtime_data'
5
5
  require 'transpec/record'
6
6
  require 'transpec/report'
7
+ require 'transpec/static_context_inspector'
7
8
  require 'active_support/concern'
8
9
 
9
10
  module Transpec
@@ -37,19 +38,19 @@ module Transpec
37
38
  private
38
39
 
39
40
  def remove(range)
40
- @source_rewriter.remove(range)
41
+ source_rewriter.remove(range)
41
42
  end
42
43
 
43
44
  def insert_before(range, content)
44
- @source_rewriter.insert_before(range, content)
45
+ source_rewriter.insert_before(range, content)
45
46
  end
46
47
 
47
48
  def insert_after(range, content)
48
- @source_rewriter.insert_after(range, content)
49
+ source_rewriter.insert_after(range, content)
49
50
  end
50
51
 
51
52
  def replace(range, content)
52
- @source_rewriter.replace(range, content)
53
+ source_rewriter.replace(range, content)
53
54
  end
54
55
  end
55
56
  end
@@ -114,26 +115,20 @@ module Transpec
114
115
  def initialize(node, source_rewriter = nil, runtime_data = nil, report = nil)
115
116
  @node = node
116
117
  @source_rewriter = source_rewriter
117
- @runtime_data = runtime_data
118
+ @runtime_data = runtime_data || DynamicAnalyzer::RuntimeData.new
118
119
  @report = report || Report.new
119
120
  end
120
121
 
121
122
  def static_context_inspector
122
- @static_context_inspector ||= StaticContextInspector.new(@node)
123
+ @static_context_inspector ||= StaticContextInspector.new(node)
123
124
  end
124
125
 
125
126
  def parent_node
126
- @node.parent_node
127
+ node.parent_node
127
128
  end
128
129
 
129
130
  def expression_range
130
- @node.loc.expression
131
- end
132
-
133
- private
134
-
135
- def runtime_node_data(node)
136
- @runtime_data && @runtime_data[node]
131
+ node.loc.expression
137
132
  end
138
133
  end
139
134
  end
@@ -31,7 +31,7 @@ module Transpec
31
31
  private
32
32
 
33
33
  def register_record(converted_syntax)
34
- @report.records << Record.new(method_name.to_s, converted_syntax)
34
+ report.records << Record.new(method_name.to_s, converted_syntax)
35
35
  end
36
36
  end
37
37
  end
@@ -29,7 +29,7 @@ module Transpec
29
29
  private
30
30
 
31
31
  def register_record
32
- @report.records << Record.new('be_close(expected, delta)', 'be_within(delta).of(expected)')
32
+ report.records << Record.new('be_close(expected, delta)', 'be_within(delta).of(expected)')
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,88 @@
1
+ # coding: utf-8
2
+
3
+ require 'transpec/syntax'
4
+ require 'transpec/syntax/mixin/send'
5
+ require 'transpec/rspec_dsl'
6
+ require 'transpec/util'
7
+
8
+ module Transpec
9
+ class Syntax
10
+ class CurrentExample < Syntax
11
+ include Mixin::Send, RSpecDSL, Util
12
+
13
+ METHODS_YIELD_EXAMPLE = (EXAMPLE_METHODS + HOOK_METHODS + HELPER_METHODS).freeze
14
+
15
+ def self.check_target_node_statically(node)
16
+ super(node) && Util.block_node_taken_by_method(node).nil?
17
+ end
18
+
19
+ def self.target_method?(receiver_node, method_name)
20
+ receiver_node.nil? && [:example, :running_example].include?(method_name)
21
+ end
22
+
23
+ def convert!
24
+ if block_node
25
+ insert_after(block_node.loc.begin, " |#{block_arg_name}|") unless block_has_argument?
26
+ replace(selector_range, block_arg_name.to_s) unless method_name == block_arg_name
27
+ block_node.metadata[:added_example_block_arg] = true
28
+ else
29
+ replace(selector_range, 'RSpec.current_example')
30
+ end
31
+
32
+ register_record
33
+ end
34
+
35
+ private
36
+
37
+ def block_has_argument?
38
+ block_arg_node || block_node.metadata[:added_example_block_arg]
39
+ end
40
+
41
+ def block_node
42
+ return @block_node if instance_variable_defined?(:@block_node)
43
+
44
+ @block_node ||= node.each_ancestor_node.find do |ancestor_node|
45
+ next false unless ancestor_node.block_type?
46
+ method_name = method_name_of_block_node(ancestor_node)
47
+ METHODS_YIELD_EXAMPLE.include?(method_name)
48
+ end
49
+ end
50
+
51
+ def block_method_name
52
+ method_name_of_block_node(block_node)
53
+ end
54
+
55
+ def block_arg_node
56
+ args_node = block_node.children[1]
57
+ args_node.children.first
58
+ end
59
+
60
+ def block_arg_name
61
+ if block_arg_node
62
+ block_arg_node.children.first
63
+ else
64
+ :example
65
+ end
66
+ end
67
+
68
+ def method_name_of_block_node(block_node)
69
+ send_node = block_node.children.first
70
+ send_node.children[1]
71
+ end
72
+
73
+ def register_record
74
+ if block_node
75
+ prefix = "#{block_method_name}"
76
+ prefix << '(:name)' if HELPER_METHODS.include?(block_method_name)
77
+ original_syntax = "#{prefix} { example }"
78
+ converted_syntax = "#{prefix} { |example| example }"
79
+ else
80
+ original_syntax = 'def helper_method example; end'
81
+ converted_syntax = 'def helper_method RSpec.current_example; end'
82
+ end
83
+
84
+ report.records << Record.new(original_syntax, converted_syntax)
85
+ end
86
+ end
87
+ end
88
+ end