transpec 0.2.6 → 1.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +10 -0
- data/README.md +111 -56
- data/README.md.erb +117 -62
- data/lib/transpec/ast/node.rb +41 -0
- data/lib/transpec/base_rewriter.rb +55 -0
- data/lib/transpec/cli.rb +43 -153
- data/lib/transpec/configuration.rb +13 -9
- data/lib/transpec/{rewriter.rb → converter.rb} +44 -71
- data/lib/transpec/dynamic_analyzer/rewriter.rb +94 -0
- data/lib/transpec/dynamic_analyzer/runtime_data.rb +27 -0
- data/lib/transpec/dynamic_analyzer.rb +166 -0
- data/lib/transpec/file_finder.rb +53 -0
- data/lib/transpec/option_parser.rb +166 -0
- data/lib/transpec/{context.rb → static_context_inspector.rb} +2 -2
- data/lib/transpec/syntax/be_close.rb +7 -9
- data/lib/transpec/syntax/double.rb +6 -10
- data/lib/transpec/syntax/expect.rb +35 -0
- data/lib/transpec/syntax/have.rb +195 -0
- data/lib/transpec/syntax/method_stub.rb +22 -27
- data/lib/transpec/syntax/mixin/allow_no_message.rb +73 -0
- data/lib/transpec/syntax/mixin/any_instance.rb +22 -0
- data/lib/transpec/syntax/mixin/expectizable.rb +26 -0
- data/lib/transpec/syntax/mixin/have_matcher.rb +23 -0
- data/lib/transpec/syntax/mixin/monkey_patch.rb +37 -0
- data/lib/transpec/syntax/mixin/send.rb +109 -0
- data/lib/transpec/syntax/{matcher.rb → operator_matcher.rb} +27 -14
- data/lib/transpec/syntax/raise_error.rb +6 -10
- data/lib/transpec/syntax/rspec_configure.rb +29 -28
- data/lib/transpec/syntax/should.rb +45 -15
- data/lib/transpec/syntax/should_receive.rb +44 -16
- data/lib/transpec/syntax.rb +29 -21
- data/lib/transpec/util.rb +12 -2
- data/lib/transpec/version.rb +3 -3
- data/spec/spec_helper.rb +8 -6
- data/spec/support/cache_helper.rb +50 -0
- data/spec/support/shared_context.rb +49 -1
- data/spec/transpec/ast/node_spec.rb +65 -0
- data/spec/transpec/cli_spec.rb +33 -242
- data/spec/transpec/commit_message_spec.rb +2 -2
- data/spec/transpec/configuration_spec.rb +12 -8
- data/spec/transpec/{rewriter_spec.rb → converter_spec.rb} +198 -148
- data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +183 -0
- data/spec/transpec/dynamic_analyzer_spec.rb +164 -0
- data/spec/transpec/file_finder_spec.rb +118 -0
- data/spec/transpec/option_parser_spec.rb +185 -0
- data/spec/transpec/{context_spec.rb → static_context_inspector_spec.rb} +27 -12
- data/spec/transpec/syntax/be_close_spec.rb +8 -4
- data/spec/transpec/syntax/double_spec.rb +105 -12
- data/spec/transpec/syntax/expect_spec.rb +83 -0
- data/spec/transpec/syntax/have_spec.rb +599 -0
- data/spec/transpec/syntax/method_stub_spec.rb +276 -115
- data/spec/transpec/syntax/{matcher_spec.rb → operator_matcher_spec.rb} +277 -98
- data/spec/transpec/syntax/raise_error_spec.rb +92 -46
- data/spec/transpec/syntax/should_receive_spec.rb +298 -92
- data/spec/transpec/syntax/should_spec.rb +230 -44
- data/spec/transpec/util_spec.rb +2 -9
- data/tasks/lib/transpec_demo.rb +1 -1
- data/tasks/lib/transpec_test.rb +5 -7
- data/tasks/test.rake +5 -1
- data/transpec.gemspec +1 -1
- metadata +46 -22
- data/lib/transpec/syntax/able_to_allow_no_message.rb +0 -73
- data/lib/transpec/syntax/able_to_target_any_instance.rb +0 -24
- data/lib/transpec/syntax/expectizable.rb +0 -27
- data/lib/transpec/syntax/send_node_syntax.rb +0 -57
@@ -1,22 +1,58 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
require 'transpec/syntax'
|
4
|
-
require 'transpec/syntax/
|
5
|
-
require 'transpec/syntax/
|
6
|
-
require 'transpec/syntax/
|
4
|
+
require 'transpec/syntax/mixin/send'
|
5
|
+
require 'transpec/syntax/mixin/monkey_patch'
|
6
|
+
require 'transpec/syntax/mixin/expectizable'
|
7
|
+
require 'transpec/syntax/mixin/allow_no_message'
|
8
|
+
require 'transpec/syntax/mixin/any_instance'
|
7
9
|
|
8
10
|
module Transpec
|
9
11
|
class Syntax
|
10
12
|
class ShouldReceive < Syntax
|
11
|
-
include
|
13
|
+
include Mixin::Send
|
14
|
+
include Mixin::MonkeyPatch
|
15
|
+
include Mixin::Expectizable
|
16
|
+
include Mixin::AllowNoMessage
|
17
|
+
include Mixin::AnyInstance
|
12
18
|
|
13
19
|
alias_method :useless_expectation?, :allow_no_message?
|
14
20
|
|
21
|
+
def self.target_method?(receiver_node, method_name)
|
22
|
+
!receiver_node.nil? && [:should_receive, :should_not_receive].include?(method_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def register_request_for_dynamic_analysis(rewriter)
|
26
|
+
register_request_of_syntax_availability_inspection(
|
27
|
+
rewriter,
|
28
|
+
:expect_to_receive_available?,
|
29
|
+
[:expect, :receive]
|
30
|
+
)
|
31
|
+
|
32
|
+
register_request_of_syntax_availability_inspection(
|
33
|
+
rewriter,
|
34
|
+
:allow_to_receive_available?,
|
35
|
+
[:allow, :receive]
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def expect_to_receive_available?
|
40
|
+
check_syntax_availability(__method__)
|
41
|
+
end
|
42
|
+
|
43
|
+
def allow_to_receive_available?
|
44
|
+
check_syntax_availability(__method__)
|
45
|
+
end
|
46
|
+
|
15
47
|
def positive?
|
16
48
|
method_name == :should_receive
|
17
49
|
end
|
18
50
|
|
19
51
|
def expectize!(negative_form = 'not_to')
|
52
|
+
unless expect_to_receive_available?
|
53
|
+
fail InvalidContextError.new(selector_range, "##{method_name}", '#expect')
|
54
|
+
end
|
55
|
+
|
20
56
|
convert_to_syntax!('expect', negative_form)
|
21
57
|
register_record(:expect, negative_form)
|
22
58
|
end
|
@@ -24,6 +60,10 @@ module Transpec
|
|
24
60
|
def allowize_useless_expectation!(negative_form = 'not_to')
|
25
61
|
return unless useless_expectation?
|
26
62
|
|
63
|
+
unless allow_to_receive_available?
|
64
|
+
fail InvalidContextError.new(selector_range, "##{method_name}", '#allow')
|
65
|
+
end
|
66
|
+
|
27
67
|
convert_to_syntax!('allow', negative_form)
|
28
68
|
remove_allowance_for_no_message!
|
29
69
|
|
@@ -42,10 +82,6 @@ module Transpec
|
|
42
82
|
private
|
43
83
|
|
44
84
|
def convert_to_syntax!(syntax, negative_form)
|
45
|
-
unless context.non_monkey_patch_mock_available?
|
46
|
-
fail InvalidContextError.new(selector_range, "##{method_name}", "##{syntax}")
|
47
|
-
end
|
48
|
-
|
49
85
|
if any_instance?
|
50
86
|
wrap_class_with_any_instance_of!(syntax)
|
51
87
|
else
|
@@ -68,14 +104,6 @@ module Transpec
|
|
68
104
|
end
|
69
105
|
end
|
70
106
|
|
71
|
-
def self.target_receiver_node?(node)
|
72
|
-
!node.nil?
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.target_method_names
|
76
|
-
[:should_receive, :should_not_receive]
|
77
|
-
end
|
78
|
-
|
79
107
|
def wrap_class_with_any_instance_of!(syntax)
|
80
108
|
insert_before(subject_range, "#{syntax}_any_instance_of(")
|
81
109
|
map = subject_node.loc
|
data/lib/transpec/syntax.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require 'transpec/
|
4
|
-
require 'transpec/report'
|
3
|
+
require 'transpec/static_context_inspector'
|
5
4
|
require 'transpec/record'
|
5
|
+
require 'transpec/report'
|
6
6
|
|
7
7
|
module Transpec
|
8
8
|
class Syntax
|
9
|
-
attr_reader :node, :ancestor_nodes, :source_rewriter, :report
|
9
|
+
attr_reader :node, :ancestor_nodes, :source_rewriter, :runtime_data, :report
|
10
|
+
|
11
|
+
def self.inherited(subclass)
|
12
|
+
all_syntaxes << subclass
|
13
|
+
end
|
10
14
|
|
11
|
-
def self.
|
15
|
+
def self.all_syntaxes
|
12
16
|
@subclasses ||= []
|
13
17
|
end
|
14
18
|
|
15
|
-
def self.
|
16
|
-
|
19
|
+
def self.standalone_syntaxes
|
20
|
+
@standalone_syntaxes ||= all_syntaxes.select(&:standalone?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.standalone?
|
24
|
+
true
|
17
25
|
end
|
18
26
|
|
19
27
|
def self.snake_case_name
|
@@ -23,22 +31,26 @@ module Transpec
|
|
23
31
|
end
|
24
32
|
end
|
25
33
|
|
26
|
-
def self.
|
27
|
-
return false unless node.type == :send
|
28
|
-
receiver_node, method_name, *_ = *node
|
29
|
-
return false unless target_receiver_node?(receiver_node)
|
30
|
-
target_method_names.include?(method_name)
|
34
|
+
def self.register_request_for_dynamic_analysis(node, rewriter)
|
31
35
|
end
|
32
36
|
|
33
|
-
def
|
37
|
+
def self.target_node?(node, runtime_data = nil)
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(node, ancestor_nodes, source_rewriter = nil, runtime_data = nil, report = nil)
|
34
42
|
@node = node
|
35
43
|
@ancestor_nodes = ancestor_nodes
|
36
44
|
@source_rewriter = source_rewriter
|
37
|
-
@
|
45
|
+
@runtime_data = runtime_data
|
46
|
+
@report = report || Report.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def register_request_for_dynamic_analysis(rewriter)
|
38
50
|
end
|
39
51
|
|
40
|
-
def
|
41
|
-
@
|
52
|
+
def static_context_inspector
|
53
|
+
@static_context_inspector ||= StaticContextInspector.new(@ancestor_nodes)
|
42
54
|
end
|
43
55
|
|
44
56
|
def parent_node
|
@@ -51,12 +63,8 @@ module Transpec
|
|
51
63
|
|
52
64
|
private
|
53
65
|
|
54
|
-
def
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.target_method_names
|
59
|
-
[]
|
66
|
+
def runtime_node_data(node)
|
67
|
+
@runtime_data && @runtime_data[node]
|
60
68
|
end
|
61
69
|
|
62
70
|
def remove(range)
|
data/lib/transpec/util.rb
CHANGED
@@ -44,14 +44,24 @@ module Transpec
|
|
44
44
|
map.begin.source.start_with?('<<')
|
45
45
|
end
|
46
46
|
|
47
|
+
def contain_here_document?(node)
|
48
|
+
here_document?(node) || node.each_descendent_node.any? { |n| here_document?(n) }
|
49
|
+
end
|
50
|
+
|
47
51
|
def in_parentheses?(node)
|
48
52
|
return false unless node.type == :begin
|
49
53
|
source = node.loc.expression.source
|
50
54
|
source[0] == '(' && source[-1] == ')'
|
51
55
|
end
|
52
56
|
|
53
|
-
def indentation_of_line(
|
54
|
-
|
57
|
+
def indentation_of_line(arg)
|
58
|
+
range = case arg
|
59
|
+
when AST::Node then arg.loc.expression
|
60
|
+
when Parser::Source::Range then arg
|
61
|
+
else fail ArgumentError, "Invalid argument #{arg}"
|
62
|
+
end
|
63
|
+
|
64
|
+
line = range.source_line
|
55
65
|
/^(?<indentation>\s*)\S/ =~ line
|
56
66
|
indentation
|
57
67
|
end
|
data/lib/transpec/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
RSpec.configure do |config|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
unless ENV['TRANSPEC_TEST']
|
5
|
+
# Yes, I'm writing specs in should syntax intentionally!
|
6
|
+
config.expect_with :rspec do |c|
|
7
|
+
c.syntax = :should
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
config.mock_with :rspec do |c|
|
11
|
+
c.syntax = :should
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
config.color_enabled = true
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
module CacheHelper
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def with_cache(key)
|
9
|
+
cache_file_path = cache_file_path(key)
|
10
|
+
|
11
|
+
if File.exist?(cache_file_path)
|
12
|
+
load_cache(cache_file_path)
|
13
|
+
else
|
14
|
+
data = yield
|
15
|
+
save_cache(cache_file_path, data)
|
16
|
+
data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_cache(path)
|
21
|
+
File.open(path) do |file|
|
22
|
+
Marshal.load(file)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def save_cache(path, data)
|
27
|
+
File.open(path, 'w') do |file|
|
28
|
+
Marshal.dump(data, file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cache_file_path(key)
|
33
|
+
filename = Digest::SHA1.hexdigest(key)
|
34
|
+
File.join(cache_dir, filename)
|
35
|
+
end
|
36
|
+
|
37
|
+
def cache_dir
|
38
|
+
@cache_dir ||= begin
|
39
|
+
spec_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
40
|
+
cache_dir = File.join(spec_dir, 'cache')
|
41
|
+
|
42
|
+
unless Dir.exist?(cache_dir)
|
43
|
+
require 'fileutils'
|
44
|
+
FileUtils.mkdir_p(cache_dir)
|
45
|
+
end
|
46
|
+
|
47
|
+
cache_dir
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
require 'transpec/dynamic_analyzer'
|
3
4
|
require 'transpec/ast/builder'
|
4
5
|
require 'transpec/ast/scanner'
|
5
6
|
require 'transpec/syntax/should'
|
7
|
+
require 'transpec/syntax/expect'
|
6
8
|
require 'parser'
|
7
9
|
require 'parser/current'
|
8
10
|
require 'tmpdir'
|
@@ -26,6 +28,31 @@ shared_context 'parsed objects' do
|
|
26
28
|
let(:rewritten_source) { source_rewriter.process }
|
27
29
|
end
|
28
30
|
|
31
|
+
# This context requires `source` to be defined with #let.
|
32
|
+
shared_context 'dynamic analysis objects' do
|
33
|
+
include_context 'isolated environment'
|
34
|
+
|
35
|
+
let(:source_path) { 'spec/example_spec.rb' }
|
36
|
+
|
37
|
+
let(:source_buffer) do
|
38
|
+
buffer = Parser::Source::Buffer.new(source_path)
|
39
|
+
buffer.source = source
|
40
|
+
buffer
|
41
|
+
end
|
42
|
+
|
43
|
+
runtime_data_cache = {}
|
44
|
+
|
45
|
+
let(:runtime_data) do
|
46
|
+
if runtime_data_cache[source]
|
47
|
+
runtime_data_cache[source]
|
48
|
+
else
|
49
|
+
FileHelper.create_file(source_path, source)
|
50
|
+
dynamic_analyzer = Transpec::DynamicAnalyzer.new(silent: true)
|
51
|
+
runtime_data_cache[source] = dynamic_analyzer.analyze
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
29
56
|
shared_context 'should object' do
|
30
57
|
let(:should_object) do
|
31
58
|
Transpec::AST::Scanner.scan(ast) do |node, ancestor_nodes|
|
@@ -33,12 +60,33 @@ shared_context 'should object' do
|
|
33
60
|
return Transpec::Syntax::Should.new(
|
34
61
|
node,
|
35
62
|
ancestor_nodes,
|
36
|
-
source_rewriter
|
63
|
+
source_rewriter,
|
64
|
+
runtime_data
|
37
65
|
)
|
38
66
|
end
|
39
67
|
|
40
68
|
fail 'No should node is found!'
|
41
69
|
end
|
70
|
+
|
71
|
+
let(:runtime_data) { nil }
|
72
|
+
end
|
73
|
+
|
74
|
+
shared_context 'expect object' do
|
75
|
+
let(:expect_object) do
|
76
|
+
Transpec::AST::Scanner.scan(ast) do |node, ancestor_nodes|
|
77
|
+
next unless Transpec::Syntax::Expect.target_node?(node)
|
78
|
+
return Transpec::Syntax::Expect.new(
|
79
|
+
node,
|
80
|
+
ancestor_nodes,
|
81
|
+
source_rewriter,
|
82
|
+
runtime_data
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
fail 'No expect node is found!'
|
87
|
+
end
|
88
|
+
|
89
|
+
let(:runtime_data) { nil }
|
42
90
|
end
|
43
91
|
|
44
92
|
shared_context 'isolated environment' do
|
@@ -30,6 +30,71 @@ module Transpec
|
|
30
30
|
# (send nil :do_something
|
31
31
|
# (lvar :arg_a))))
|
32
32
|
|
33
|
+
describe '#parent_node' do
|
34
|
+
context 'when the node has parent' do
|
35
|
+
let(:target_node) do
|
36
|
+
ast.each_descendent_node do |node|
|
37
|
+
return node if node == s(:args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns the parent node' do
|
42
|
+
target_node.parent_node.type.should == :block
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when the node has parent' do
|
47
|
+
it 'returns nil' do
|
48
|
+
ast.parent_node.should be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#each_ancestor_node' do
|
54
|
+
let(:target_node) do
|
55
|
+
ast.each_descendent_node do |node|
|
56
|
+
return node if node == s(:args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:expected_types) { [:block, :def] }
|
61
|
+
|
62
|
+
context 'when a block is given' do
|
63
|
+
it 'yields each ancestor node' do
|
64
|
+
index = 0
|
65
|
+
|
66
|
+
target_node.each_ancestor_node do |node|
|
67
|
+
expected_type = expected_types[index]
|
68
|
+
node.type.should == expected_type
|
69
|
+
index += 1
|
70
|
+
end
|
71
|
+
|
72
|
+
index.should_not == 0
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns itself' do
|
76
|
+
returned_value = target_node.each_ancestor_node { }
|
77
|
+
returned_value.should be(target_node)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when no block is given' do
|
82
|
+
it 'returns enumerator' do
|
83
|
+
target_node.each_ancestor_node.should be_a(Enumerator)
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'the returned enumerator' do
|
87
|
+
it 'enumerates the ancestor nodes' do
|
88
|
+
enumerator = target_node.each_ancestor_node
|
89
|
+
|
90
|
+
expected_types.each do |expected_type|
|
91
|
+
enumerator.next.type.should == expected_type
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
33
98
|
describe '#each_child_node' do
|
34
99
|
let(:expected_types) { [:args, :block] }
|
35
100
|
|