transpec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rubocop.yml +13 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +9 -0
  6. data/Guardfile +14 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +37 -0
  9. data/Rakefile +27 -0
  10. data/bin/transpec +8 -0
  11. data/lib/transpec/ast/scanner.rb +51 -0
  12. data/lib/transpec/ast/scope_stack.rb +76 -0
  13. data/lib/transpec/cli.rb +162 -0
  14. data/lib/transpec/configuration.rb +40 -0
  15. data/lib/transpec/git.rb +24 -0
  16. data/lib/transpec/rewriter.rb +109 -0
  17. data/lib/transpec/syntax/double.rb +21 -0
  18. data/lib/transpec/syntax/matcher.rb +60 -0
  19. data/lib/transpec/syntax/method_stub.rb +142 -0
  20. data/lib/transpec/syntax/send_node_syntax.rb +39 -0
  21. data/lib/transpec/syntax/should.rb +49 -0
  22. data/lib/transpec/syntax/should_receive.rb +120 -0
  23. data/lib/transpec/syntax.rb +58 -0
  24. data/lib/transpec/util.rb +50 -0
  25. data/lib/transpec/version.rb +14 -0
  26. data/lib/transpec.rb +17 -0
  27. data/spec/.rubocop.yml +19 -0
  28. data/spec/spec_helper.rb +33 -0
  29. data/spec/spec_spec.rb +54 -0
  30. data/spec/support/file_helper.rb +25 -0
  31. data/spec/support/shared_context.rb +63 -0
  32. data/spec/transpec/ast/scanner_spec.rb +177 -0
  33. data/spec/transpec/ast/scope_stack_spec.rb +94 -0
  34. data/spec/transpec/cli_spec.rb +290 -0
  35. data/spec/transpec/configuration_spec.rb +52 -0
  36. data/spec/transpec/git_spec.rb +85 -0
  37. data/spec/transpec/rewriter_spec.rb +203 -0
  38. data/spec/transpec/syntax/double_spec.rb +88 -0
  39. data/spec/transpec/syntax/matcher_spec.rb +407 -0
  40. data/spec/transpec/syntax/method_stub_spec.rb +386 -0
  41. data/spec/transpec/syntax/should_receive_spec.rb +286 -0
  42. data/spec/transpec/syntax/should_spec.rb +262 -0
  43. data/spec/transpec/util_spec.rb +48 -0
  44. data/transpec.gemspec +32 -0
  45. metadata +233 -0
@@ -0,0 +1,63 @@
1
+ # coding: utf-8
2
+
3
+ require 'tmpdir'
4
+
5
+ # This context requires `source` to be defined with #let.
6
+ shared_context 'parsed objects' do
7
+ let(:source_buffer) do
8
+ buffer = Parser::Source::Buffer.new('(string)')
9
+ buffer.source = source
10
+ buffer
11
+ end
12
+
13
+ let(:ast) do
14
+ parser = Parser::CurrentRuby.new
15
+ ast = parser.parse(source_buffer)
16
+ ast
17
+ end
18
+
19
+ let(:source_rewriter) { Parser::Source::Rewriter.new(source_buffer) }
20
+
21
+ let(:rewritten_source) { source_rewriter.process }
22
+ end
23
+
24
+ shared_context 'should object' do
25
+ let(:should_object) do
26
+ Transpec::AST::Scanner.scan(ast) do |node, ancestor_nodes, in_example_group_context|
27
+ next unless Transpec::Syntax::Should.target_node?(node)
28
+ return Transpec::Syntax::Should.new(
29
+ node,
30
+ ancestor_nodes,
31
+ in_example_group_context?,
32
+ source_rewriter
33
+ )
34
+ end
35
+
36
+ fail 'No should node is found!'
37
+ end
38
+
39
+ let(:in_example_group_context?) { true }
40
+ end
41
+
42
+ shared_context 'isolated environment' do
43
+ around do |example|
44
+ Dir.mktmpdir do |tmpdir|
45
+ original_home = ENV['HOME']
46
+
47
+ begin
48
+ virtual_home = File.expand_path(File.join(tmpdir, 'home'))
49
+ Dir.mkdir(virtual_home)
50
+ ENV['HOME'] = virtual_home
51
+
52
+ working_dir = File.join(tmpdir, 'work')
53
+ Dir.mkdir(working_dir)
54
+
55
+ Dir.chdir(working_dir) do
56
+ example.run
57
+ end
58
+ ensure
59
+ ENV['HOME'] = original_home
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,177 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Transpec
6
+ module AST
7
+ describe Scanner do
8
+ include_context 'parsed objects'
9
+ include ::AST::Sexp
10
+
11
+ let(:source) do
12
+ <<-END
13
+ some_var = 1
14
+
15
+ RSpec.configure do |config|
16
+ config.before do
17
+ prepare_something
18
+ end
19
+ end
20
+
21
+ module SomeModule
22
+ SOME_CONST = 1
23
+
24
+ describe 'something' do
25
+ def some_method(some_arg)
26
+ do_something
27
+ end
28
+
29
+ it 'is 1' do
30
+ something.should == 1
31
+ end
32
+ end
33
+ end
34
+ END
35
+ end
36
+
37
+ # (begin
38
+ # (lvasgn :some_var
39
+ # (int 1))
40
+ # (block
41
+ # (send
42
+ # (const nil :RSpec) :configure)
43
+ # (args
44
+ # (arg :config))
45
+ # (block
46
+ # (send
47
+ # (lvar :config) :before)
48
+ # (args)
49
+ # (send nil :prepare_something)))
50
+ # (module
51
+ # (const nil :SomeModule)
52
+ # (begin
53
+ # (casgn nil :SOME_CONST
54
+ # (int 1))
55
+ # (block
56
+ # (send nil :describe
57
+ # (str "something"))
58
+ # (args)
59
+ # (begin
60
+ # (def :some_method
61
+ # (args
62
+ # (arg :some_arg))
63
+ # (send nil :do_something))
64
+ # (block
65
+ # (send nil :it
66
+ # (str "is 1"))
67
+ # (args)
68
+ # (send
69
+ # (send
70
+ # (send nil :something) :should) :==
71
+ # (int 1))))))))
72
+
73
+ describe '.scan' do
74
+ it 'scans nodes with depth first order' do
75
+ expected_node_type_order = [
76
+ :begin,
77
+ :lvasgn,
78
+ :int,
79
+ :block,
80
+ :send,
81
+ :const,
82
+ :args
83
+ ]
84
+
85
+ index = 0
86
+
87
+ Scanner.scan(ast) do |node|
88
+ expected_node_type = expected_node_type_order[index]
89
+ node.type.should == expected_node_type if expected_node_type
90
+ index += 1
91
+ end
92
+
93
+ index.should_not == 0
94
+ end
95
+
96
+ it 'passes ancestor nodes of the current node to the block' do
97
+ each_expected_ancestor_nodes_types = [
98
+ [],
99
+ [:begin],
100
+ [:begin, :lvasgn],
101
+ [:begin],
102
+ [:begin, :block],
103
+ [:begin, :block, :send],
104
+ [:begin, :block],
105
+ [:begin, :block, :args]
106
+ ]
107
+
108
+ index = 0
109
+
110
+ Scanner.scan(ast) do |node, ancestor_nodes|
111
+ expected_ancestor_node_types = each_expected_ancestor_nodes_types[index]
112
+ if expected_ancestor_node_types
113
+ ancestor_node_types = ancestor_nodes.map(&:type)
114
+ ancestor_node_types.should == expected_ancestor_node_types
115
+ end
116
+ index += 1
117
+ end
118
+
119
+ index.should_not == 0
120
+ end
121
+ end
122
+
123
+ describe '#scope_stack' do
124
+ def brief_of_node(node)
125
+ brief = node.type.to_s
126
+ node.children.each do |child|
127
+ break if child.is_a?(Parser::AST::Node)
128
+ brief << " #{child.inspect}"
129
+ end
130
+ brief
131
+ end
132
+
133
+ it 'returns current scope stack' do
134
+ scanner = Scanner.new do |node|
135
+ expected_scope_stack = begin
136
+ case brief_of_node(node)
137
+ when 'lvasgn :some_var'
138
+ []
139
+ when 'send nil :prepare_something'
140
+ [:rspec_configure, :block]
141
+ when 'module'
142
+ []
143
+ when 'const nil :SomeModule'
144
+ # [:module] # TODO
145
+ when 'casgn nil :SOME_CONST'
146
+ [:module]
147
+ when 'send nil :describe'
148
+ # [:module] # TODO
149
+ when 'def :some_method'
150
+ [:module, :example_group]
151
+ when 'arg :some_arg'
152
+ # [:module, :example_group] # TODO
153
+ when 'send nil :do_something'
154
+ [:module, :example_group, :def]
155
+ when 'send nil :it'
156
+ # [:module, :example_group] # TODO
157
+ when 'str "is 1"'
158
+ # [:module, :example_group] # TODO
159
+ when 'send nil :something'
160
+ [:module, :example_group, :block]
161
+ end
162
+ end
163
+
164
+ # TODO: Some scope nodes have special child nodes
165
+ # such as their arguments or their subject.
166
+ # But from scope point of view, the child nodes are not in the parent's scope,
167
+ # they should be in the next outer scope.
168
+
169
+ scanner.scope_stack.should == expected_scope_stack if expected_scope_stack
170
+ end
171
+
172
+ scanner.scan(ast, true)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,94 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Transpec
6
+ module AST
7
+ describe ScopeStack do
8
+ describe '#in_example_group_context?' do
9
+ subject { ScopeStack.new(scopes).in_example_group_context? }
10
+
11
+ context 'when in top level' do
12
+ let(:scopes) { [] }
13
+ it { should be_false }
14
+ end
15
+
16
+ context 'when in an instance method in top level' do
17
+ let(:scopes) { [:def] }
18
+ it { should be_true }
19
+ end
20
+
21
+ context 'when in a block in an instance method in top level' do
22
+ let(:scopes) { [:def, :block] }
23
+ it { should be_true }
24
+ end
25
+
26
+ context 'when in #describe block in top level' do
27
+ let(:scopes) { [:example_group] }
28
+ it { should be_false }
29
+ end
30
+
31
+ context 'when in #describe block in a module' do
32
+ let(:scopes) { [:module, :example_group] }
33
+ it { should be_false }
34
+ end
35
+
36
+ context 'when in an instance method in #describe block' do
37
+ let(:scopes) { [:example_group, :def] }
38
+ it { should be_true }
39
+ end
40
+
41
+ context 'when in an instance method in #describe block in a module' do
42
+ let(:scopes) { [:module, :example_group, :def] }
43
+ it { should be_true }
44
+ end
45
+
46
+ context 'when in a block in #describe block' do
47
+ let(:scopes) { [:example_group, :block] }
48
+ it { should be_true }
49
+ end
50
+
51
+ context 'when in a block in #describe block in a module' do
52
+ let(:scopes) { [:module, :example_group, :block] }
53
+ it { should be_true }
54
+ end
55
+
56
+ context 'when in a class in a block in #describe block' do
57
+ let(:scopes) { [:example_group, :block, :class] }
58
+ it { should be_false }
59
+ end
60
+
61
+ context 'when in an instance method in a class in a block in #describe block' do
62
+ let(:scopes) { [:example_group, :block, :class, :def] }
63
+ it { should be_false }
64
+ end
65
+
66
+ context 'when in an instance method in a module' do
67
+ # Instance methods of module can be used by `include SomeModule` in #describe block.
68
+ let(:scopes) { [:module, :def] }
69
+ it { should be_true }
70
+ end
71
+
72
+ context 'when in an instance method in a class' do
73
+ let(:scopes) { [:class, :def] }
74
+ it { should be_false }
75
+ end
76
+
77
+ context 'when in RSpec.configure' do
78
+ let(:scopes) { [:rspec_configure] }
79
+ it { should be_false }
80
+ end
81
+
82
+ context 'when in a block in RSpec.configure' do
83
+ let(:scopes) { [:rspec_configure, :block] }
84
+ it { should be_true }
85
+ end
86
+
87
+ context 'when in an instance method in RSpec.configure' do
88
+ let(:scopes) { [:rspec_configure, :def] }
89
+ it { should be_true }
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,290 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Transpec
6
+ describe CLI do
7
+ subject(:cli) { CLI.new }
8
+
9
+ describe '.run' do
10
+ it 'invokes #run' do
11
+ args = ['foo', 'bar']
12
+ CLI.any_instance.should_receive(:run).with(args)
13
+ CLI.run(args)
14
+ end
15
+ end
16
+
17
+ describe '#forced?' do
18
+ subject { cli.forced? }
19
+
20
+ context 'by default' do
21
+ it { should be_false }
22
+ end
23
+ end
24
+
25
+ describe '#run' do
26
+ before do
27
+ cli.stub(:puts)
28
+ cli.stub(:warn)
29
+ cli.stub(:target_files).and_return(args)
30
+ Rewriter.stub(:new).and_return(rewriter)
31
+ end
32
+
33
+ subject { cli.run(args) }
34
+ let(:args) { ['some_file.rb'] }
35
+ let(:rewriter) { double('rewriter').as_null_object }
36
+
37
+ shared_examples 'rewrites files' do
38
+ it 'rewrites files' do
39
+ rewriter.should_receive(:rewrite_file!)
40
+ cli.run(args)
41
+ end
42
+
43
+ it 'returns true' do
44
+ should be_true
45
+ end
46
+ end
47
+
48
+ context 'when git is available' do
49
+ before { Git.stub(:command_available?).and_return(true) }
50
+
51
+ context 'and inside of a repository' do
52
+ before { Git.stub(:inside_of_repository?).and_return(true) }
53
+
54
+ context 'and the repository is not clean' do
55
+ before { Git.stub(:clean?).and_return(false) }
56
+
57
+ context '#forced? is false' do
58
+ before { cli.stub(:forced?).and_return(false) }
59
+
60
+ it 'aborts processing' do
61
+ rewriter.should_not_receive(:rewrite_file!)
62
+ cli.run(args).should be_false
63
+ end
64
+
65
+ it 'warns to the user' do
66
+ cli.should_receive(:warn) do |arg|
67
+ arg.should include('clean')
68
+ end
69
+
70
+ cli.run(args)
71
+ end
72
+ end
73
+
74
+ context '#forced? is true' do
75
+ before { cli.stub(:forced?).and_return(true) }
76
+ include_examples 'rewrites files'
77
+ end
78
+ end
79
+
80
+ context 'and the repository is clean' do
81
+ before { Git.stub(:clean?).and_return(true) }
82
+ include_examples 'rewrites files'
83
+ end
84
+ end
85
+
86
+ context 'and not inside of a repository' do
87
+ before { Git.stub(:inside_of_repository?).and_return(false) }
88
+ include_examples 'rewrites files'
89
+ end
90
+ end
91
+
92
+ context 'when git is not available' do
93
+ before { Git.stub(:command_available?).and_return(false) }
94
+ include_examples 'rewrites files'
95
+ end
96
+
97
+ context 'when an exception is raised while running' do
98
+ before do
99
+ cli.stub(:parse_options).and_raise(ArgumentError, 'No such file or directory - non-existent-file')
100
+ end
101
+
102
+ it 'return false' do
103
+ should be_false
104
+ end
105
+
106
+ it 'prints message of the exception' do
107
+ cli.should_receive(:warn).with('No such file or directory - non-existent-file')
108
+ cli.run([])
109
+ end
110
+ end
111
+
112
+ context 'when no target paths are specified' do
113
+ include_context 'isolated environment'
114
+
115
+ let(:args) { [] }
116
+
117
+ context 'and there is "spec" directory' do
118
+ before { Dir.mkdir('spec') }
119
+
120
+ it 'targets files in the "spec" directoy' do
121
+ cli.should_receive(:target_files).with(['spec'])
122
+ cli.run(args)
123
+ end
124
+ end
125
+
126
+ context 'and there is not "spec" directory' do
127
+ it 'aborts' do
128
+ should be_false
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '#parse_options' do
135
+ subject { cli.parse_options(args) }
136
+ let(:args) { ['some_file', '--negative-form', 'to_not', 'some_dir'] }
137
+
138
+ it 'return non-option arguments' do
139
+ should == ['some_file', 'some_dir']
140
+ end
141
+
142
+ it 'does not mutate the passed array' do
143
+ cli.parse_options(args)
144
+ args.should == ['some_file', '--negative-form', 'to_not', 'some_dir']
145
+ end
146
+
147
+ describe '--force option' do
148
+ let(:args) { ['--force'] }
149
+
150
+ it 'sets #forced? true' do
151
+ cli.parse_options(args)
152
+ cli.should be_forced
153
+ end
154
+ end
155
+
156
+ describe '-d/--disable option' do
157
+ [
158
+ ['expect_to_matcher', :convert_to_expect_to_matcher?],
159
+ ['expect_to_receive', :convert_to_expect_to_receive?],
160
+ ['allow_to_receive', :convert_to_allow_to_receive?],
161
+ ['deprecated', :replace_deprecated_method?]
162
+ ].each do |cli_type, config_attr|
163
+ context "when #{cli_type.inspect} is specified" do
164
+ let(:args) { ['--disable', cli_type] }
165
+
166
+ it "sets configuration ##{config_attr} false" do
167
+ cli.parse_options(args)
168
+ cli.configuration.send(config_attr).should be_false
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'when multiple types are specified with comma' do
174
+ let(:args) { ['--disable', 'allow_to_receive,deprecated'] }
175
+
176
+ it 'handles all of them' do
177
+ cli.parse_options(args)
178
+ cli.configuration.convert_to_allow_to_receive?.should be_false
179
+ cli.configuration.replace_deprecated_method?.should be_false
180
+ end
181
+ end
182
+
183
+ context 'when unknown type is specified' do
184
+ let(:args) { ['--disable', 'unknown'] }
185
+
186
+ it 'raises error' do
187
+ -> { cli.parse_options(args) }.should raise_error(ArgumentError) { |error|
188
+ error.message.should == 'Unknown conversion type "unknown"'
189
+ }
190
+ end
191
+ end
192
+ end
193
+
194
+ describe '-n/--negative-form option' do
195
+ ['not_to', 'to_not'].each do |form|
196
+ context "when #{form.inspect} is specified" do
197
+ let(:args) { ['--negative-form', form] }
198
+
199
+ it "sets configuration #negative_form_of_to? #{form.inspect}" do
200
+ cli.parse_options(args)
201
+ cli.configuration.negative_form_of_to.should == form
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ describe '-p/--no-parentheses-matcher-arg option' do
208
+ let(:args) { ['--no-parentheses-matcher-arg'] }
209
+
210
+ it 'sets configuration #parenthesize_matcher_arg? false' do
211
+ cli.parse_options(args)
212
+ cli.configuration.parenthesize_matcher_arg.should be_false
213
+ end
214
+ end
215
+
216
+ describe '--version option' do
217
+ before do
218
+ cli.stub(:puts)
219
+ cli.stub(:exit)
220
+ end
221
+
222
+ let(:args) { ['--version'] }
223
+
224
+ it 'shows version' do
225
+ cli.should_receive(:puts).with(Version.to_s)
226
+ cli.parse_options(args)
227
+ end
228
+
229
+ it 'exits' do
230
+ cli.should_receive(:exit)
231
+ cli.parse_options(args)
232
+ end
233
+ end
234
+ end
235
+
236
+ describe '#target_files' do
237
+ include_context 'isolated environment'
238
+
239
+ before do
240
+ ['file', 'file.rb', 'dir/file', 'dir/file.rb'].each do |path|
241
+ FileHelper.create_file(path, '')
242
+ end
243
+ end
244
+
245
+ subject(:target_files) { cli.target_files(paths) }
246
+
247
+ context 'when no path is passed' do
248
+ let(:paths) { [] }
249
+
250
+ it 'returns empty array' do
251
+ should be_empty
252
+ end
253
+ end
254
+
255
+ context 'when a file path with .rb extension is passed' do
256
+ let(:paths) { ['file.rb'] }
257
+
258
+ it 'returns the path' do
259
+ should == ['file.rb']
260
+ end
261
+ end
262
+
263
+ context 'when a file path without extension is passed' do
264
+ let(:paths) { ['file'] }
265
+
266
+ it 'returns the path' do
267
+ should == ['file']
268
+ end
269
+ end
270
+
271
+ context 'when a non-existent path is passed' do
272
+ let(:paths) { ['non-existent-file'] }
273
+
274
+ it 'raises error' do
275
+ -> { target_files }.should raise_error(ArgumentError) { |error|
276
+ error.message.should == 'No such file or directory "non-existent-file"'
277
+ }
278
+ end
279
+ end
280
+
281
+ context 'when a directory path is passed' do
282
+ let(:paths) { ['dir'] }
283
+
284
+ it 'returns file paths with .rb extension in the directory' do
285
+ should == ['dir/file.rb']
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Transpec
6
+ describe Configuration do
7
+ subject(:configuration) { Configuration.new }
8
+
9
+ context 'by default' do
10
+ [
11
+ :convert_to_expect_to_matcher?,
12
+ :convert_to_expect_to_receive?,
13
+ :convert_to_allow_to_receive?,
14
+ :replace_deprecated_method?,
15
+ :parenthesize_matcher_arg?
16
+ ].each do |attribute|
17
+ describe "##{attribute}" do
18
+ subject { configuration.send(attribute) }
19
+
20
+ it 'is true' do
21
+ should be_true
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#negative_form_of_to' do
27
+ it 'is "not_to"' do
28
+ configuration.negative_form_of_to.should == 'not_to'
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#negative_form_of_to=' do
34
+ ['not_to', 'to_not'] .each do |form|
35
+ context "when #{form.inspect} is passed" do
36
+ it "sets #{form.inspect}" do
37
+ configuration.negative_form_of_to = form
38
+ configuration.negative_form_of_to.should == form
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'when a form other than "not_to" or "to_not" is passed' do
44
+ it 'raises error' do
45
+ proc do
46
+ configuration.negative_form_of_to = 'foo'
47
+ end.should raise_error(ArgumentError)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end