transpec 1.9.3 → 1.10.0

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