transpec 1.10.4 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -3
- data/CHANGELOG.md +9 -0
- data/README.md +96 -18
- data/README.md.erb +117 -51
- data/lib/transpec/base_rewriter.rb +25 -28
- data/lib/transpec/cli.rb +27 -14
- data/lib/transpec/configuration.rb +2 -1
- data/lib/transpec/converter.rb +54 -32
- data/lib/transpec/dynamic_analyzer/helper.rb.erb +44 -0
- data/lib/transpec/dynamic_analyzer/node_util.rb +17 -0
- data/lib/transpec/dynamic_analyzer/rewriter.rb +12 -4
- data/lib/transpec/dynamic_analyzer/runtime_data.rb +3 -4
- data/lib/transpec/dynamic_analyzer.rb +14 -52
- data/lib/transpec/option_parser.rb +89 -81
- data/lib/transpec/processed_source.rb +48 -0
- data/lib/transpec/record.rb +2 -2
- data/lib/transpec/report.rb +5 -1
- data/lib/transpec/rspec_dsl.rb +12 -2
- data/lib/transpec/rspec_version.rb +8 -7
- data/lib/transpec/spec_suite.rb +114 -0
- data/lib/transpec/syntax/example.rb +4 -20
- data/lib/transpec/syntax/example_group.rb +38 -0
- data/lib/transpec/syntax/have.rb +6 -9
- data/lib/transpec/syntax/its.rb +5 -7
- data/lib/transpec/syntax/method_stub.rb +12 -13
- data/lib/transpec/syntax/mixin/any_instance_block.rb +14 -6
- data/lib/transpec/syntax/mixin/context_sensitive.rb +41 -0
- data/lib/transpec/syntax/mixin/matcher_owner.rb +16 -26
- data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +1 -1
- data/lib/transpec/syntax/mixin/no_message_allowance.rb +2 -2
- data/lib/transpec/syntax/mixin/useless_and_return.rb +2 -2
- data/lib/transpec/syntax/oneliner_should.rb +9 -13
- data/lib/transpec/syntax/operator.rb +3 -7
- data/lib/transpec/syntax/pending.rb +4 -20
- data/lib/transpec/syntax/rspec_configure/configuration_modification.rb +86 -0
- data/lib/transpec/syntax/rspec_configure/framework.rb +45 -86
- data/lib/transpec/syntax/rspec_configure.rb +18 -6
- data/lib/transpec/syntax/should.rb +3 -5
- data/lib/transpec/syntax/should_receive.rb +1 -1
- data/lib/transpec/syntax.rb +8 -0
- data/lib/transpec/util.rb +0 -8
- data/lib/transpec/version.rb +2 -2
- data/spec/acceptance/configuration_modification_spec.rb +132 -0
- data/spec/acceptance/conversion_spec.rb +114 -0
- data/spec/support/shared_context.rb +6 -12
- data/spec/transpec/cli_spec.rb +21 -23
- data/spec/transpec/configuration_spec.rb +2 -1
- data/spec/transpec/converter_spec.rb +292 -213
- data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +3 -3
- data/spec/transpec/dynamic_analyzer_spec.rb +8 -2
- data/spec/transpec/option_parser_spec.rb +42 -4
- data/spec/transpec/processed_source_spec.rb +67 -0
- data/spec/transpec/rspec_version_spec.rb +8 -2
- data/spec/transpec/spec_suite_spec.rb +107 -0
- data/spec/transpec/syntax/allow_spec.rb +9 -27
- data/spec/transpec/syntax/example_group_spec.rb +172 -0
- data/spec/transpec/syntax/expect_spec.rb +18 -54
- data/spec/transpec/syntax/have_spec.rb +35 -14
- data/spec/transpec/syntax/its_spec.rb +27 -7
- data/spec/transpec/syntax/method_stub_spec.rb +31 -8
- data/spec/transpec/syntax/oneliner_should_spec.rb +22 -131
- data/spec/transpec/syntax/rspec_configure_spec.rb +118 -15
- data/spec/transpec/syntax/should_spec.rb +51 -82
- data/tasks/fixtures/guard/COMMIT_EDITMSG +80 -0
- data/tasks/fixtures/mail/COMMIT_EDITMSG +84 -0
- data/tasks/fixtures/twitter/COMMIT_EDITMSG +36 -0
- data/tasks/lib/transpec_test.rb +23 -2
- data/tasks/readme.rake +35 -0
- metadata +22 -4
- data/lib/transpec/parser.rb +0 -9
@@ -7,10 +7,8 @@ require 'optparse'
|
|
7
7
|
require 'rainbow'
|
8
8
|
require 'rainbow/ext/string' unless String.respond_to?(:color)
|
9
9
|
|
10
|
-
# rubocop:disable ClassLength
|
11
|
-
|
12
10
|
module Transpec
|
13
|
-
class OptionParser
|
11
|
+
class OptionParser # rubocop:disable ClassLength
|
14
12
|
CONFIG_ATTRS_FOR_KEEP_TYPES = {
|
15
13
|
should: :convert_should=,
|
16
14
|
oneliner: :convert_oneliner=,
|
@@ -22,6 +20,11 @@ module Transpec
|
|
22
20
|
deprecated: :convert_deprecated_method=
|
23
21
|
}
|
24
22
|
|
23
|
+
CONFIG_ATTRS_FOR_CONVERT_TYPES = {
|
24
|
+
stub_with_hash: :convert_stub_with_hash_to_allow_to_receive_and_return=,
|
25
|
+
example_group: :convert_example_group=
|
26
|
+
}
|
27
|
+
|
25
28
|
VALID_BOOLEAN_MATCHER_TYPES = %w(truthy,falsey truthy,falsy true,false)
|
26
29
|
|
27
30
|
attr_reader :configuration
|
@@ -36,7 +39,7 @@ module Transpec
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def parse(args)
|
39
|
-
args =
|
42
|
+
args = convert_deprecated_options(args)
|
40
43
|
@parser.parse!(args)
|
41
44
|
args
|
42
45
|
end
|
@@ -47,28 +50,27 @@ module Transpec
|
|
47
50
|
|
48
51
|
private
|
49
52
|
|
50
|
-
# rubocop:disable MethodLength
|
51
|
-
def setup_parser
|
53
|
+
def setup_parser # rubocop:disable MethodLength
|
52
54
|
@parser = create_parser
|
53
55
|
|
54
56
|
define_option('-f', '--force') do
|
55
57
|
configuration.forced = true
|
56
58
|
end
|
57
59
|
|
58
|
-
define_option('-s', '--skip-dynamic-analysis') do
|
59
|
-
configuration.skip_dynamic_analysis = true
|
60
|
-
end
|
61
|
-
|
62
60
|
define_option('-c', '--rspec-command COMMAND') do |command|
|
63
61
|
configuration.rspec_command = command
|
64
62
|
end
|
65
63
|
|
66
64
|
define_option('-k', '--keep TYPE[,TYPE...]') do |types|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
configure_conversion(CONFIG_ATTRS_FOR_KEEP_TYPES, types, false)
|
66
|
+
end
|
67
|
+
|
68
|
+
define_option('-v', '--convert TYPE[,TYPE...]') do |types|
|
69
|
+
configure_conversion(CONFIG_ATTRS_FOR_CONVERT_TYPES, types, true)
|
70
|
+
end
|
71
|
+
|
72
|
+
define_option('-s', '--skip-dynamic-analysis') do
|
73
|
+
configuration.skip_dynamic_analysis = true
|
72
74
|
end
|
73
75
|
|
74
76
|
define_option('-n', '--negative-form FORM') do |form|
|
@@ -88,10 +90,6 @@ module Transpec
|
|
88
90
|
configuration.add_receiver_arg_to_any_instance_implementation_block = false
|
89
91
|
end
|
90
92
|
|
91
|
-
define_option('-t', '--convert-stub-with-hash') do
|
92
|
-
configuration.convert_stub_with_hash_to_stub_and_return = true
|
93
|
-
end
|
94
|
-
|
95
93
|
define_option('-p', '--no-parentheses-matcher-arg') do
|
96
94
|
configuration.parenthesize_matcher_arg = false
|
97
95
|
end
|
@@ -105,7 +103,6 @@ module Transpec
|
|
105
103
|
exit
|
106
104
|
end
|
107
105
|
end
|
108
|
-
# rubocop:enable MethodLength
|
109
106
|
|
110
107
|
def create_parser
|
111
108
|
banner = "Usage: transpec [options] [files or directories]\n\n"
|
@@ -116,81 +113,72 @@ module Transpec
|
|
116
113
|
|
117
114
|
def define_option(*options, &block)
|
118
115
|
description_lines = descriptions[options.first]
|
116
|
+
description_lines = description_lines.map { |line| highlight_text(line) }
|
119
117
|
@parser.on(*options, *description_lines, &block)
|
120
118
|
end
|
121
119
|
|
122
|
-
# rubocop:disable
|
123
|
-
def descriptions
|
120
|
+
# rubocop:disable AlignHash
|
121
|
+
def descriptions # rubocop:disable MethodLength
|
124
122
|
@descriptions ||= {
|
125
123
|
'-f' => [
|
126
|
-
'Force processing even if the current Git'
|
127
|
-
'repository is not clean.'
|
124
|
+
'Force processing even if the current Git repository is not clean.'
|
128
125
|
],
|
129
126
|
'-s' => [
|
130
|
-
'Skip dynamic analysis and convert with only',
|
131
|
-
'
|
132
|
-
'option decreases the conversion accuracy.'
|
127
|
+
'Skip dynamic analysis and convert with only static analysis. Note',
|
128
|
+
'that specifying this option decreases the conversion accuracy.'
|
133
129
|
],
|
134
130
|
'-c' => [
|
135
|
-
'Specify a command to run your specs that is',
|
136
|
-
'
|
131
|
+
'Specify a command to run your specs that is used for dynamic',
|
132
|
+
'analysis.',
|
137
133
|
'Default: "bundle exec rspec"'
|
138
134
|
],
|
139
135
|
'-k' => [
|
140
|
-
'Keep specific syntaxes by disabling',
|
141
|
-
'
|
142
|
-
'
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
"
|
148
|
-
|
149
|
-
|
150
|
-
" #{'deprecated'.bright} (e.g. from #{'mock'.underline} to #{'double'.underline})",
|
136
|
+
'Keep specific syntaxes by disabling conversions.',
|
137
|
+
'Conversion Types:',
|
138
|
+
' *should* (to `expect(obj).to`)',
|
139
|
+
' *oneliner* (from `it { should ... }` to `it { is_expected.to ... }`)',
|
140
|
+
' *should_receive* (to `expect(obj).to receive`)',
|
141
|
+
' *stub* (to `allow(obj).to receive`)',
|
142
|
+
' *have_items* (to `expect(collection.size).to eq(n)`)',
|
143
|
+
" *its* (to `describe '#attr' { subject { }; it { } }`)",
|
144
|
+
' *pending* (to `skip`)',
|
145
|
+
' *deprecated* (all other deprecated syntaxes to latest syntaxes)',
|
151
146
|
'These are all converted by default.'
|
152
147
|
],
|
148
|
+
'-v' => [
|
149
|
+
'Enable specific conversions that are disabled by default.',
|
150
|
+
'Conversion Types:',
|
151
|
+
' *stub_with_hash* (`obj.stub(:msg => val)` to',
|
152
|
+
' `allow(obj).to receive(:msg).and_return(val)`)',
|
153
|
+
'These conversions are disabled by default.'
|
154
|
+
],
|
153
155
|
'-n' => [
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
"Default: #{'not_to'.bright}"
|
156
|
+
'Specify a negative form of `to` that is used in the `expect(...).to`',
|
157
|
+
'syntax. Either *not_to* or *to_not*.',
|
158
|
+
'Default: *not_to*'
|
158
159
|
],
|
159
160
|
'-b' => [
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
161
|
+
'Specify a matcher type that `be_true` and `be_false` will be',
|
162
|
+
'converted to.',
|
163
|
+
' *truthy,falsey* (conditional semantics)',
|
164
|
+
' *truthy,falsy* (alias of `falsey`)',
|
165
|
+
' *true,false* (exact equality)',
|
166
|
+
'Default: *truthy,falsey*'
|
166
167
|
],
|
167
168
|
'-a' => [
|
168
|
-
'Suppress yielding receiver instances to',
|
169
|
-
|
170
|
-
'first block argument.'
|
171
|
-
],
|
172
|
-
'-t' => [
|
173
|
-
"Enable conversion of #{'obj.stub(:msg => val)'.underline} to",
|
174
|
-
"#{'allow(obj).to receive(:msg).and_return(val)'.underline}",
|
175
|
-
"when #{'receive_messages(:msg => val)'.underline} is",
|
176
|
-
'unavailable (prior to RSpec 3.0). It will be',
|
177
|
-
'converted to multiple statements if the hash',
|
178
|
-
'includes multiple pairs. This conversion is',
|
179
|
-
'disabled by default.'
|
169
|
+
'Suppress yielding receiver instances to `any_instance`',
|
170
|
+
'implementation blocks as the first block argument.'
|
180
171
|
],
|
181
172
|
'-p' => [
|
182
|
-
'Suppress parenthesizing arguments of matchers',
|
183
|
-
|
184
|
-
|
185
|
-
'
|
186
|
-
'
|
187
|
-
'
|
188
|
-
'
|
189
|
-
'
|
190
|
-
'
|
191
|
-
" #{'== 10'.underline} to #{'eq(10)'.underline}",
|
192
|
-
" #{'=~ /pattern/'.underline} to #{'match(/pattern/)'.underline}",
|
193
|
-
" #{'=~ [1, 2]'.underline} to #{'match_array([1, 2])'.underline}"
|
173
|
+
'Suppress parenthesizing arguments of matchers when converting',
|
174
|
+
'`should` with operator matcher to `expect` with non-operator matcher.',
|
175
|
+
'Note that it will be parenthesized even if this option is',
|
176
|
+
'specified when parentheses are necessary to keep the meaning of',
|
177
|
+
'the expression. By default, arguments of the following operator',
|
178
|
+
'matchers will be parenthesized.',
|
179
|
+
' `== 10` to `eq(10)`',
|
180
|
+
' `=~ /pattern/` to `match(/pattern/)`',
|
181
|
+
' `=~ [1, 2]` to `match_array([1, 2])`'
|
194
182
|
],
|
195
183
|
'--no-color' => [
|
196
184
|
'Disable color in the output.'
|
@@ -200,19 +188,39 @@ module Transpec
|
|
200
188
|
]
|
201
189
|
}
|
202
190
|
end
|
203
|
-
# rubocop:enable
|
191
|
+
# rubocop:enable AlignHash
|
192
|
+
|
193
|
+
def highlight_text(text)
|
194
|
+
text.gsub(/`.+?`/) { |code| code.gsub('`', '').underline }
|
195
|
+
.gsub(/\*.+?\*/) { |code| code.gsub('*', '').bright }
|
196
|
+
end
|
204
197
|
|
205
|
-
def
|
206
|
-
|
198
|
+
def convert_deprecated_options(raw_args)
|
199
|
+
raw_args.each_with_object([]) do |arg, args|
|
207
200
|
case arg
|
208
201
|
when '-m', '--generate-commit-message'
|
209
|
-
|
210
|
-
|
211
|
-
|
202
|
+
deprecate('-m/--generate-commit-message option')
|
203
|
+
when '-t', '--convert-stub-with-hash'
|
204
|
+
deprecate('-t/--convert-stub-with-hash', '`--convert stub_with_hash`')
|
205
|
+
args.concat(%w(--convert stub_with_hash))
|
212
206
|
else
|
213
|
-
|
207
|
+
args << arg
|
214
208
|
end
|
215
209
|
end
|
216
210
|
end
|
211
|
+
|
212
|
+
def deprecate(subject, alternative = nil)
|
213
|
+
message = "DEPRECATION: #{subject} is deprecated."
|
214
|
+
message << " Please use #{alternative} instead." if alternative
|
215
|
+
warn message
|
216
|
+
end
|
217
|
+
|
218
|
+
def configure_conversion(type_to_attr_map, inputted_types, boolean)
|
219
|
+
inputted_types.split(',').each do |type|
|
220
|
+
config_attr = type_to_attr_map[type.to_sym]
|
221
|
+
fail ArgumentError, "Unknown syntax type #{type.inspect}" unless config_attr
|
222
|
+
configuration.send(config_attr, boolean)
|
223
|
+
end
|
224
|
+
end
|
217
225
|
end
|
218
226
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'parser/current'
|
5
|
+
rescue NotImplementedError
|
6
|
+
warn 'Falling back to Ruby 2.1 parser.'
|
7
|
+
require 'parser/ruby21'
|
8
|
+
Parser::CurrentRuby = Parser::Ruby21 # rubocop:disable ConstantName
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'transpec/ast/builder'
|
12
|
+
|
13
|
+
module Transpec
|
14
|
+
class ProcessedSource
|
15
|
+
attr_reader :buffer, :ast, :path, :syntax_error
|
16
|
+
|
17
|
+
def self.parse_file(path)
|
18
|
+
source = File.read(path)
|
19
|
+
parse(source, path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse(source, path = nil)
|
23
|
+
buffer = Parser::Source::Buffer.new(path || '(string)')
|
24
|
+
buffer.source = source
|
25
|
+
|
26
|
+
builder = AST::Builder.new
|
27
|
+
parser = Parser::CurrentRuby.new(builder)
|
28
|
+
|
29
|
+
begin
|
30
|
+
ast = parser.parse(buffer)
|
31
|
+
new(buffer, ast, path)
|
32
|
+
rescue Parser::SyntaxError => error
|
33
|
+
new(buffer, nil, path, error)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(buffer, ast, path = nil, syntax_error = nil)
|
38
|
+
@buffer = buffer
|
39
|
+
@ast = ast
|
40
|
+
@path = path
|
41
|
+
@syntax_error = syntax_error
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
buffer.source
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/transpec/record.rb
CHANGED
@@ -4,7 +4,7 @@ require 'transpec/annotatable'
|
|
4
4
|
|
5
5
|
module Transpec
|
6
6
|
class Record
|
7
|
-
|
7
|
+
OVERRIDE_FORBIDDEN_METHODS = [
|
8
8
|
:original_syntax,
|
9
9
|
:original_syntax_type,
|
10
10
|
:converted_syntax,
|
@@ -62,7 +62,7 @@ module Transpec
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.method_added(method_name)
|
65
|
-
return unless
|
65
|
+
return unless OVERRIDE_FORBIDDEN_METHODS.include?(method_name)
|
66
66
|
fail "Do not override Record##{method_name}."
|
67
67
|
end
|
68
68
|
end
|
data/lib/transpec/report.rb
CHANGED
@@ -26,7 +26,11 @@ module Transpec
|
|
26
26
|
record_counts[record] += 1
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
sorted_record_counts = record_counts.sort_by do |record, count|
|
30
|
+
[-count, record.original_syntax_type, record.converted_syntax_type]
|
31
|
+
end
|
32
|
+
|
33
|
+
Hash[sorted_record_counts]
|
30
34
|
end
|
31
35
|
|
32
36
|
def colored_summary(options = nil)
|
data/lib/transpec/rspec_dsl.rb
CHANGED
@@ -3,18 +3,28 @@
|
|
3
3
|
# Aliases by Capybara:
|
4
4
|
# https://github.com/jnicklas/capybara/blob/2.2.0/lib/capybara/rspec/features.rb
|
5
5
|
|
6
|
+
# rubocop:disable LineLength
|
7
|
+
|
6
8
|
module Transpec
|
7
9
|
module RSpecDSL
|
10
|
+
# https://github.com/rspec/rspec-core/blob/77cc21e/lib/rspec/core/example_group.rb#L239-L265
|
11
|
+
# https://github.com/rspec/rspec-core/blob/77cc21e/lib/rspec/core/shared_example_group.rb#L50-L61
|
8
12
|
EXAMPLE_GROUP_METHODS = [
|
13
|
+
:example_group,
|
9
14
|
:describe, :context,
|
15
|
+
:xdescribe, :xcontext,
|
16
|
+
:fdescribe, :fcontext,
|
10
17
|
:shared_examples, :shared_context, :share_examples_for, :shared_examples_for,
|
11
18
|
:feature # Capybara
|
12
19
|
].freeze
|
13
20
|
|
21
|
+
# https://github.com/rspec/rspec-core/blob/77cc21e/lib/rspec/core/example_group.rb#L130-L171
|
14
22
|
EXAMPLE_METHODS = [
|
15
23
|
:example, :it, :specify,
|
16
|
-
:focus, :
|
17
|
-
:
|
24
|
+
:focus, :fexample, :fit, :fspecify,
|
25
|
+
:focused, # TODO: Support conversion
|
26
|
+
:xexample, :xit, :xspecify,
|
27
|
+
:skip, :pending,
|
18
28
|
:scenario, :xscenario # Capybara
|
19
29
|
].freeze
|
20
30
|
|
@@ -41,14 +41,15 @@ module Transpec
|
|
41
41
|
gem_version.to_s
|
42
42
|
end
|
43
43
|
|
44
|
-
define_feature :be_truthy,
|
45
|
-
define_feature :yielded_example,
|
44
|
+
define_feature :be_truthy, '2.99.0.beta1'
|
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
|
-
define_feature :oneliner_is_expected,
|
48
|
-
define_feature :skip,
|
49
|
-
define_feature :receive_messages,
|
50
|
-
define_feature :receive_message_chain,
|
51
|
-
define_feature :non_should_matcher_protocol,
|
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'
|
49
|
+
define_feature :receive_messages, '3.0.0.beta1'
|
50
|
+
define_feature :receive_message_chain, '3.0.0.beta2'
|
51
|
+
define_feature :non_should_matcher_protocol, '3.0.0.beta2'
|
52
|
+
define_feature :non_monkey_patch_example_group, '3.0.0.beta2'
|
52
53
|
|
53
54
|
RSPEC_2_99 = new('2.99.0.beta1')
|
54
55
|
RSPEC_3_0 = new('3.0.0.beta1')
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/file_finder'
|
4
|
+
require 'transpec/processed_source'
|
5
|
+
require 'transpec/syntax'
|
6
|
+
|
7
|
+
Transpec::Syntax.require_all
|
8
|
+
|
9
|
+
module Transpec
|
10
|
+
class SpecSuite
|
11
|
+
ANALYSIS_TARGET_CLASSES = [Syntax::Mixin::AnyInstanceBlock]
|
12
|
+
|
13
|
+
attr_reader :runtime_data
|
14
|
+
|
15
|
+
def initialize(base_paths = [], runtime_data = nil)
|
16
|
+
@base_paths = base_paths
|
17
|
+
@runtime_data = runtime_data
|
18
|
+
@analyzed = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def specs
|
22
|
+
@specs ||= begin
|
23
|
+
FileFinder.find(@base_paths).map do |path|
|
24
|
+
ProcessedSource.parse_file(path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def analyze
|
30
|
+
return if @analyzed
|
31
|
+
|
32
|
+
specs.each do |spec|
|
33
|
+
next unless spec.ast
|
34
|
+
spec.ast.each_node do |node|
|
35
|
+
dispatch_node(node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
@analyzed = true
|
40
|
+
end
|
41
|
+
|
42
|
+
def need_to_modify_yield_receiver_to_any_instance_implementation_blocks_config?
|
43
|
+
analyze
|
44
|
+
@need_to_modify_yield_receiver_to_any_instance_implementation_blocks_config
|
45
|
+
end
|
46
|
+
|
47
|
+
def main_rspec_configure_node?(node)
|
48
|
+
analyze
|
49
|
+
|
50
|
+
if @main_rspec_configure
|
51
|
+
@main_rspec_configure.node.equal?(node)
|
52
|
+
else
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def dispatch_node(node)
|
60
|
+
Syntax.standalone_syntaxes.each do |syntax_class|
|
61
|
+
syntax = syntax_class.new(node, nil, runtime_data)
|
62
|
+
next unless syntax.conversion_target?
|
63
|
+
dispatch_syntax(syntax)
|
64
|
+
break
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def dispatch_syntax(syntax)
|
69
|
+
invoke_handler(syntax.class, syntax)
|
70
|
+
|
71
|
+
syntax_mixins.each do |mixin|
|
72
|
+
next unless syntax.class.ancestors.include?(mixin)
|
73
|
+
invoke_handler(mixin, syntax)
|
74
|
+
end
|
75
|
+
|
76
|
+
syntax.dependent_syntaxes.each do |dependent_syntax|
|
77
|
+
next unless dependent_syntax.conversion_target?
|
78
|
+
dispatch_syntax(dependent_syntax)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def syntax_mixins
|
83
|
+
Syntax::Mixin.constants.map do |const_name|
|
84
|
+
Syntax::Mixin.const_get(const_name, false)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def invoke_handler(klass, syntax)
|
89
|
+
class_name = klass.name.split('::').last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
90
|
+
handler_name = "process_#{class_name}"
|
91
|
+
send(handler_name, syntax) if respond_to?(handler_name, true)
|
92
|
+
end
|
93
|
+
|
94
|
+
def process_any_instance_block(syntax)
|
95
|
+
@need_to_modify_yield_receiver_to_any_instance_implementation_blocks_config ||=
|
96
|
+
syntax.need_to_add_receiver_arg_to_any_instance_implementation_block?
|
97
|
+
end
|
98
|
+
|
99
|
+
def process_rspec_configure(rspec_configure)
|
100
|
+
return unless runtime_data
|
101
|
+
run_order = runtime_data[rspec_configure.node, :run_order]
|
102
|
+
return unless run_order
|
103
|
+
|
104
|
+
unless @main_rspec_configure
|
105
|
+
@main_rspec_configure = rspec_configure
|
106
|
+
return
|
107
|
+
end
|
108
|
+
|
109
|
+
if run_order < runtime_data[@main_rspec_configure.node, :run_order]
|
110
|
+
@main_rspec_configure = rspec_configure
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -1,36 +1,20 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
require 'transpec/syntax'
|
4
|
-
require 'transpec/syntax/mixin/
|
4
|
+
require 'transpec/syntax/mixin/context_sensitive'
|
5
5
|
require 'transpec/rspec_dsl'
|
6
6
|
|
7
7
|
module Transpec
|
8
8
|
class Syntax
|
9
9
|
class Example < Syntax
|
10
|
-
include Mixin::
|
11
|
-
|
12
|
-
define_dynamic_analysis do |rewriter|
|
13
|
-
code = "is_a?(Class) && ancestors.any? { |a| a.name == 'RSpec::Core::ExampleGroup' }"
|
14
|
-
rewriter.register_request(node, :example_group_context?, code, :context)
|
15
|
-
end
|
10
|
+
include Mixin::ContextSensitive, RSpecDSL
|
16
11
|
|
17
12
|
def dynamic_analysis_target?
|
18
13
|
super && receiver_node.nil? && EXAMPLE_METHODS.include?(method_name)
|
19
14
|
end
|
20
15
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
# Check whether the context is example group to differenciate
|
25
|
-
# RSpec::Core::ExampleGroup.pending (a relative of #it) and
|
26
|
-
# RSpec::Core::ExampleGroup#pending (marks the example as pending in #it block).
|
27
|
-
if runtime_data.run?(node)
|
28
|
-
# If we have runtime data, check with it.
|
29
|
-
runtime_data[node, :example_group_context?]
|
30
|
-
else
|
31
|
-
# Otherwise check statically.
|
32
|
-
static_context_inspector.scopes.last == :example_group
|
33
|
-
end
|
16
|
+
def should_be_in_example_group_context?
|
17
|
+
true
|
34
18
|
end
|
35
19
|
|
36
20
|
def convert_pending_to_skip!
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/syntax'
|
4
|
+
require 'transpec/syntax/mixin/context_sensitive'
|
5
|
+
require 'transpec/syntax/mixin/monkey_patch'
|
6
|
+
require 'transpec/rspec_dsl'
|
7
|
+
|
8
|
+
module Transpec
|
9
|
+
class Syntax
|
10
|
+
class ExampleGroup < Syntax
|
11
|
+
include Mixin::ContextSensitive, Mixin::MonkeyPatch, RSpecDSL
|
12
|
+
|
13
|
+
def dynamic_analysis_target?
|
14
|
+
super && receiver_node.nil? && EXAMPLE_GROUP_METHODS.include?(method_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def should_be_in_example_group_context?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def convert_to_non_monkey_patch!
|
22
|
+
insert_before(expression_range, 'RSpec.')
|
23
|
+
register_record
|
24
|
+
end
|
25
|
+
|
26
|
+
def register_record
|
27
|
+
original_syntax = method_name.to_s
|
28
|
+
converted_syntax = "RSpec.#{method_name}"
|
29
|
+
|
30
|
+
[original_syntax, converted_syntax].each do |syntax|
|
31
|
+
syntax << " 'something' { }"
|
32
|
+
end
|
33
|
+
|
34
|
+
@report.records << Record.new(original_syntax, converted_syntax)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/transpec/syntax/have.rb
CHANGED
@@ -28,12 +28,13 @@ module Transpec
|
|
28
28
|
[:have, :have_exactly, :have_at_least, :have_at_most].include?(method_name)
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
31
|
+
def conversion_target?
|
32
|
+
super && !runtime_subject_data(:project_requires_collection_matcher?)
|
33
|
+
end
|
33
34
|
|
35
|
+
def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
|
34
36
|
replace(expectation.subject_range, replacement_subject_source) if explicit_subject?
|
35
37
|
replace(matcher_range, source_builder.replacement_matcher_source(parenthesize_matcher_arg))
|
36
|
-
|
37
38
|
register_record if explicit_subject?
|
38
39
|
end
|
39
40
|
|
@@ -56,10 +57,6 @@ module Transpec
|
|
56
57
|
items_node.children[1]
|
57
58
|
end
|
58
59
|
|
59
|
-
def project_requires_collection_matcher?
|
60
|
-
runtime_subject_data(:project_requires_collection_matcher?)
|
61
|
-
end
|
62
|
-
|
63
60
|
def collection_accessor
|
64
61
|
if runtime_subject_data(:collection_accessor)
|
65
62
|
runtime_subject_data(:collection_accessor).to_sym
|
@@ -70,7 +67,7 @@ module Transpec
|
|
70
67
|
|
71
68
|
def subject_is_owner_of_collection?
|
72
69
|
return true if items_method_has_arguments?
|
73
|
-
|
70
|
+
runtime_subject_data(:collection_accessor)
|
74
71
|
end
|
75
72
|
|
76
73
|
def collection_accessor_is_private?
|
@@ -99,7 +96,7 @@ module Transpec
|
|
99
96
|
end
|
100
97
|
|
101
98
|
def accurate_conversion?
|
102
|
-
|
99
|
+
runtime_subject_data
|
103
100
|
end
|
104
101
|
|
105
102
|
def matcher_range
|
data/lib/transpec/syntax/its.rb
CHANGED
@@ -19,9 +19,11 @@ module Transpec
|
|
19
19
|
super && receiver_node.nil? && method_name == :its
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
22
|
+
def conversion_target?
|
23
|
+
super && !runtime_data[node, :project_requires_its?]
|
24
|
+
end
|
24
25
|
|
26
|
+
def convert_to_describe_subject_it!
|
25
27
|
front, rear = build_wrapper_codes
|
26
28
|
|
27
29
|
insert_before(beginning_of_line_range(block_node), front)
|
@@ -45,10 +47,6 @@ module Transpec
|
|
45
47
|
node.parent_node
|
46
48
|
end
|
47
49
|
|
48
|
-
def project_requires_its?
|
49
|
-
runtime_data[node, :project_requires_its?]
|
50
|
-
end
|
51
|
-
|
52
50
|
private
|
53
51
|
|
54
52
|
def build_wrapper_codes
|
@@ -74,7 +72,7 @@ module Transpec
|
|
74
72
|
|
75
73
|
def previous_line_is_blank?
|
76
74
|
return false unless previous_line_source
|
77
|
-
previous_line_source.empty? ||
|
75
|
+
previous_line_source.empty? || previous_line_source.match(/\A\s*\Z/)
|
78
76
|
end
|
79
77
|
|
80
78
|
def previous_and_current_line_are_same_indentation_level?
|