transpec 0.0.1

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