hexp 0.0.1 → 0.2.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 (69) hide show
  1. data/.travis.yml +12 -3
  2. data/Changelog.md +9 -0
  3. data/Gemfile +3 -5
  4. data/Gemfile.devtools +20 -18
  5. data/Gemfile.lock +97 -84
  6. data/Rakefile +16 -0
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/reek.yml +42 -18
  10. data/config/rubocop.yml +31 -0
  11. data/config/yardstick.yml +39 -1
  12. data/examples/from_nokogiri.rb +77 -0
  13. data/examples/selector_rewriter_chaining.rb +14 -0
  14. data/examples/todo.rb +138 -0
  15. data/examples/widget.rb +64 -0
  16. data/hexp.gemspec +8 -3
  17. data/lib/hexp.rb +103 -2
  18. data/lib/hexp/builder.rb +256 -0
  19. data/lib/hexp/css_selector.rb +205 -0
  20. data/lib/hexp/css_selector/parser.rb +74 -0
  21. data/lib/hexp/css_selector/sass_parser.rb +22 -0
  22. data/lib/hexp/dom.rb +0 -2
  23. data/lib/hexp/dsl.rb +27 -0
  24. data/lib/hexp/errors.rb +21 -0
  25. data/lib/hexp/h.rb +5 -2
  26. data/lib/hexp/list.rb +67 -9
  27. data/lib/hexp/node.rb +197 -41
  28. data/lib/hexp/node/attributes.rb +176 -0
  29. data/lib/hexp/node/children.rb +44 -0
  30. data/lib/hexp/node/css_selection.rb +73 -0
  31. data/lib/hexp/node/domize.rb +52 -6
  32. data/lib/hexp/node/normalize.rb +19 -9
  33. data/lib/hexp/node/pp.rb +32 -0
  34. data/lib/hexp/node/rewriter.rb +52 -0
  35. data/lib/hexp/node/selector.rb +59 -0
  36. data/lib/hexp/nokogiri/equality.rb +61 -0
  37. data/lib/hexp/nokogiri/reader.rb +27 -0
  38. data/lib/hexp/sass/selector_parser.rb +4 -0
  39. data/lib/hexp/text_node.rb +129 -9
  40. data/lib/hexp/version.rb +1 -1
  41. data/notes +34 -0
  42. data/spec/shared_helper.rb +6 -0
  43. data/spec/spec_helper.rb +2 -6
  44. data/spec/unit/hexp/builder_spec.rb +101 -0
  45. data/spec/unit/hexp/css_selector/attribute_spec.rb +137 -0
  46. data/spec/unit/hexp/css_selector/class_spec.rb +15 -0
  47. data/spec/unit/hexp/css_selector/comma_sequence_spec.rb +20 -0
  48. data/spec/unit/hexp/css_selector/element_spec.rb +11 -0
  49. data/spec/unit/hexp/css_selector/parser_spec.rb +51 -0
  50. data/spec/unit/hexp/css_selector/simple_sequence_spec.rb +48 -0
  51. data/spec/unit/hexp/dsl_spec.rb +55 -0
  52. data/spec/unit/hexp/h_spec.rb +38 -0
  53. data/spec/unit/hexp/list_spec.rb +19 -0
  54. data/spec/unit/hexp/node/attr_spec.rb +55 -0
  55. data/spec/unit/hexp/node/attributes_spec.rb +125 -0
  56. data/spec/unit/hexp/node/children_spec.rb +33 -0
  57. data/spec/unit/hexp/node/class_spec.rb +37 -0
  58. data/spec/unit/hexp/node/css_selection_spec.rb +86 -0
  59. data/spec/unit/hexp/node/normalize_spec.rb +12 -6
  60. data/spec/unit/hexp/node/rewrite_spec.rb +67 -30
  61. data/spec/unit/hexp/node/selector_spec.rb +78 -0
  62. data/spec/unit/hexp/node/text_spec.rb +7 -0
  63. data/spec/unit/hexp/node/to_dom_spec.rb +1 -1
  64. data/spec/unit/hexp/nokogiri/reader_spec.rb +8 -0
  65. data/spec/unit/hexp/parse_spec.rb +23 -0
  66. data/spec/unit/hexp/text_node_spec.rb +25 -0
  67. data/spec/unit/hexp_spec.rb +33 -0
  68. metadata +129 -16
  69. data/lib/hexp/format_error.rb +0 -8
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'H notation' do
4
+ context 'if H is already defined' do
5
+ before do
6
+ @old_H = Object.send(:remove_const, :H)
7
+ @old_stderr, $stderr = $stderr, StringIO.new
8
+ ::H = 'foo'.freeze
9
+ end
10
+
11
+ after do
12
+ Object.send(:remove_const, :H)
13
+ $stderr = @old_stderr
14
+ ::H = @old_H
15
+ end
16
+
17
+ it 'should not override H' do
18
+ expect(H).to eq('foo')
19
+ end
20
+
21
+ it 'should print out a warning on STDERR' do
22
+ load 'hexp/h.rb'
23
+ expect($stderr.string).to match(/WARN/)
24
+ end
25
+
26
+ end
27
+
28
+ context 'if H is not set yet' do
29
+ before do
30
+ Object.send(:remove_const, :H)
31
+ end
32
+
33
+ it 'should set H to Hexp::Node' do
34
+ load 'hexp/h.rb'
35
+ expect(H).to eq(Hexp::Node)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::List do
4
+ it 'should be equal to an Array with the same contents' do
5
+ expect(Hexp::List[ H[:div] ]).to eq [ H[:div] ]
6
+ end
7
+
8
+ describe 'value and type equality' do
9
+ it 'should not be #eql? to an Array with the same contents' do
10
+ expect(Hexp::List[ H[:div] ]).to_not eql [ H[:div] ]
11
+ end
12
+ end
13
+
14
+ describe 'inspect' do
15
+ it 'should look exactly like an Array' do
16
+ expect(Hexp::List[ H[:div] ].inspect).to eq '[H[:div]]'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::Node, 'attr' do
4
+ subject { hexp.attr(*args) }
5
+ let(:hexp) { H[:div, class: 'foo'] }
6
+
7
+ context 'with a single string argument' do
8
+ let(:args) { ['class'] }
9
+
10
+ it 'should return the attribute value by that name' do
11
+ expect(subject).to eq('foo')
12
+ end
13
+ end
14
+
15
+ context 'with a single symbol argument' do
16
+ let(:args) { [:class] }
17
+
18
+ it 'should return the attribute value by that name' do
19
+ expect(subject).to eq('foo')
20
+ end
21
+ end
22
+
23
+ context 'with two argument' do
24
+ let(:args) { ['data-id', '7'] }
25
+
26
+ it 'should return a new Hexp::Node' do
27
+ expect(subject).to be_instance_of(Hexp::Node)
28
+ end
29
+
30
+ it 'should set the attribute value' do
31
+ expect(subject.attributes['data-id']).to eq('7')
32
+ end
33
+
34
+ it 'should leave other attributes untouched' do
35
+ expect(subject.attributes['class']).to eq('foo')
36
+ end
37
+
38
+ context 'with a nil value' do
39
+ let(:args) { ['class', nil] }
40
+
41
+ it 'should unset the attribute' do
42
+ expect(subject.attributes).to eq({})
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'with too many arguments' do
48
+ let(:args) { ['class', 'baz', 'bar'] }
49
+
50
+ it 'should raise an ArgumentError' do
51
+ expect{ subject }.to raise_error(ArgumentError)
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::Node::Attributes do
4
+ describe 'attr?' do
5
+ it 'should return true if the attribute is present' do
6
+ expect(H[:a, href: '/foo'].has_attr?(:href)).to be_true
7
+ end
8
+
9
+ it 'should return true if the attribute is present and empty' do
10
+ expect(H[:a, href: ''].has_attr?(:href)).to be_true
11
+ end
12
+
13
+ it 'should return false if the attribute is not present' do
14
+ expect(H[:a].has_attr?(:href)).to be_false
15
+ end
16
+
17
+ it 'should work with a string argument' do
18
+ expect(H[:a, href: '/foo'].has_attr?('href')).to be_true
19
+ end
20
+ end
21
+
22
+ describe 'class_list' do
23
+ context 'for a node without a class attribute' do
24
+ subject(:node) { H[:div] }
25
+
26
+ it 'should return an empty string' do
27
+ expect(node.class_list).to eq []
28
+ end
29
+ end
30
+
31
+ context 'for a node with an empty class attribute' do
32
+ subject(:node) { H[:div, class: ''] }
33
+
34
+ it 'should return an empty string' do
35
+ expect(node.class_list).to eq []
36
+ end
37
+ end
38
+
39
+ context 'for a node with a single class' do
40
+ subject(:node) { H[:div, class: 'daklazz'] }
41
+
42
+ it 'should return a list with the single class' do
43
+ expect(node.class_list).to eq ['daklazz']
44
+ end
45
+ end
46
+
47
+ context 'for a node with multiple classes' do
48
+ subject(:node) { H[:div, class: 'daklazz otherklazz foo'] }
49
+
50
+ it 'should return a list with the single class' do
51
+ expect(node.class_list).to eq ['daklazz', 'otherklazz', 'foo']
52
+ end
53
+ end
54
+ end
55
+
56
+ describe 'remove_class' do
57
+ context 'for a node without a class list' do
58
+ it 'should be idempotent' do
59
+ expect(H[:div].remove_class('foo')).to eq H[:div]
60
+ end
61
+ end
62
+
63
+ context 'for a node with an empty class list' do
64
+ it 'should remove the attribute' do
65
+ expect(H[:div, class: ''].remove_class('foo')).to eq H[:div]
66
+ end
67
+ end
68
+
69
+ context 'for a node with one class' do
70
+ it 'should remove the class attribute when the class is removed' do
71
+ expect(H[:div, class: 'foo'].remove_class('foo')).to eq H[:div]
72
+ end
73
+
74
+ it 'should return the node itself when an other class is removed' do
75
+ expect(H[:div, class: 'foo'].remove_class('bar')).to eq H[:div, class: 'foo']
76
+ end
77
+ end
78
+
79
+ context 'with a class appearing multiple times in a class list' do
80
+ it 'should remove all instances of the class' do
81
+ expect(H[:div, class: 'foo foo'].remove_class('foo')).to eq H[:div]
82
+ end
83
+ end
84
+ end
85
+
86
+ describe 'set_attrs' do
87
+ it 'should set attributes' do
88
+ expect(H[:foo].set_attrs(class: 'bar')).to eq H[:foo, class: 'bar']
89
+ end
90
+
91
+ it 'should override attributes' do
92
+ expect(H[:foo, class: 'baz'].set_attrs(class: 'bar')).to eq H[:foo, class: 'bar']
93
+ end
94
+
95
+ it 'should merge keep both old and new attributes' do
96
+ expect(H[:foo, class: 'baz'].set_attrs(src: 'bar')).to eq H[:foo, class: 'baz', src: 'bar']
97
+ end
98
+ end
99
+
100
+ describe 'merge_attrs' do
101
+ describe 'when passing in a Hash' do
102
+ it 'should set attributes' do
103
+ expect(H[:foo].merge_attrs(class: 'bar')).to eq H[:foo, class: 'bar']
104
+ end
105
+
106
+ it 'should merge class lists' do
107
+ expect(H[:foo, class: 'baz'].merge_attrs(class: 'bar')).to eq H[:foo, class: 'baz bar']
108
+ end
109
+
110
+ it 'should override attributes that are not class' do
111
+ expect(H[:foo, src: 'baz'].set_attrs(src: 'bar')).to eq H[:foo, src: 'bar']
112
+ end
113
+
114
+ it 'should merge keep both old and new attributes' do
115
+ expect(H[:foo, class: 'baz'].merge_attrs(src: 'bar')).to eq H[:foo, class: 'baz', src: 'bar']
116
+ end
117
+ end
118
+
119
+ describe 'when passing in a Hexp::Node' do
120
+ it 'should take the nodes attributes to merge with' do
121
+ expect(H[:foo, class: 'klz1'].merge_attrs(H[:bla, class: 'klz2'])).to eq H[:foo, class: 'klz1 klz2']
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::Node::Children do
4
+ describe 'empty?' do
5
+ context 'for an empty node' do
6
+ subject { H[:div] }
7
+ it { should be_empty }
8
+ end
9
+
10
+ context 'for a node with children' do
11
+ subject { H[:div, [H[:span, "hello"]]] }
12
+ it { should_not be_empty }
13
+ end
14
+ end
15
+
16
+ describe 'add_child' do
17
+ it 'should return a new node with the child added' do
18
+ expect(H[:div].add_child(H[:span])).to eq H[:div, H[:span]]
19
+ end
20
+ end
21
+
22
+ describe 'text' do
23
+ it 'should return all text nodes that are descendant of this node, combined' do
24
+ expect(H[:div, [
25
+ "Hello,",
26
+ H[:span, {class: 'big'}, "World!"]
27
+ ]
28
+ ].text
29
+ ).to eq "Hello,World!"
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::Node, 'class?' do
4
+ context 'with no class attribute set' do
5
+ it 'should return false' do
6
+ expect(H[:p].class?('strong')).to be_false
7
+ end
8
+ end
9
+
10
+ context 'with a single class set' do
11
+ it 'should return true if the class name is the same' do
12
+ expect(H[:p, class: 'strong'].class?('strong')).to be_true
13
+ end
14
+
15
+ it 'should return false if the class name is not same' do
16
+ expect(H[:p, class: 'strong'].class?('foo')).to be_false
17
+ end
18
+
19
+ it 'should return false if the class name is a partial match' do
20
+ expect(H[:p, class: 'strong'].class?('stron')).to be_false
21
+ end
22
+ end
23
+
24
+ context 'with multiple classes set' do
25
+ it 'should return true if the class name is part of the class list' do
26
+ expect(H[:p, class: 'banner strong'].class?('strong')).to be_true
27
+ end
28
+
29
+ it 'should return false if the class name is not in the class list' do
30
+ expect(H[:p, class: 'banner strong'].class?('foo')).to be_false
31
+ end
32
+
33
+ it 'should return false if the class name is a partial match' do
34
+ expect(H[:p, class: 'banner strong'].class?('er str')).to be_false
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::Node::CssSelection do
4
+ subject(:selection) { described_class.new(hexp, selector) }
5
+
6
+ context 'given a single tag' do
7
+ let(:selector) { 'span' }
8
+
9
+ context 'with a depth of 1' do
10
+ let(:hexp) { H[:span] }
11
+
12
+ it 'should match all nodes of that tag' do
13
+ expect(selection.to_a).to eq [ H[:span] ]
14
+ end
15
+ end
16
+
17
+ context 'with a depth of 2' do
18
+ let(:hexp) { H[:span, {id: 'span-1'}, H[:span, id: 'span-2']] }
19
+
20
+ it 'should match all nodes of that tag' do
21
+ expect(selection.to_a).to eq [ H[:span, id: 'span-2'], hexp ]
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'given a tag and class' do
27
+ let(:selector) { 'span.foo' }
28
+
29
+ context 'with a depth of 1' do
30
+ context 'with a matching tag and class' do
31
+ let(:hexp) { H[:span, class: 'foo bar'] }
32
+ its(:to_a) { should eq [ hexp ] }
33
+ end
34
+
35
+ context 'with only a matching tag' do
36
+ let(:hexp) { H[:span] }
37
+ its(:to_a) { should eq [] }
38
+ end
39
+
40
+ context 'with only a matching class' do
41
+ let(:hexp) { H[:div, class: 'foo'] }
42
+ its(:to_a) { should eq [] }
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'given a sequence of tags' do
48
+ let(:selector) { 'ul li' }
49
+
50
+ context 'with a minimal matching tag' do
51
+ let(:hexp) { H[:ul, H[:li]] }
52
+ its(:to_a) { should eq [ H[:li] ] }
53
+ end
54
+
55
+ context 'with other tags in between' do
56
+ let(:hexp) do
57
+ H[:body, [
58
+ H[:ul, [
59
+ H[:span, [
60
+ H[:li, id: 'foo'],
61
+ H[:li, id: 'bar']]],
62
+ H[:li, id: 'baz']]]]]
63
+ end
64
+ its(:to_a) { should eq %w[foo bar baz].map{|id| H[:li, id: id] } }
65
+ end
66
+ end
67
+
68
+ describe 'rewrite' do
69
+ let(:hexp) do
70
+ H[:ul, [
71
+ H[:li, [
72
+ H[:a, href: '/foo']]],
73
+ H[:a, href: '/moo']]]
74
+ end
75
+ let(:selector) { 'li a' }
76
+
77
+ it 'should only affect nodes that match the selection' do
78
+ expect(selection.rewrite {|node| node.add_class('selected')}).to eq(
79
+ H[:ul, [
80
+ H[:li, [
81
+ H[:a, href: '/foo', class: 'selected']]],
82
+ H[:a, href: '/moo']]]
83
+ )
84
+ end
85
+ end
86
+ end
@@ -1,24 +1,24 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hexp::Node::Normalize, '#call' do
4
- subject { Hexp::Node[*node] }
4
+ subject(:normalized) { Hexp::Node[*node] }
5
5
 
6
6
  describe 'with a single element' do
7
- let (:node) { [:div] }
7
+ let(:node) { [:div] }
8
8
 
9
9
  it 'should treat the first as the tag' do
10
- subject.tag.should == :div
10
+ expect(normalized.tag).to eq :div
11
11
  end
12
12
  it 'should set an empty attribute list' do
13
- subject.attributes.should == {}
13
+ expect(normalized.attributes).to eq Hash[]
14
14
  end
15
15
  it 'should set an empty children list' do
16
- subject.children.should == Hexp::List[]
16
+ expect(normalized.children).to eql Hexp::List[]
17
17
  end
18
18
  end
19
19
 
20
20
  describe 'with two parameters' do
21
- let (:node) { [:div, {class: 'foo'}] }
21
+ let(:node) { [:div, {class: 'foo'}] }
22
22
 
23
23
  it 'should treat the first as the tag' do
24
24
  subject.tag.should == :div
@@ -75,7 +75,13 @@ describe Hexp::Node::Normalize, '#call' do
75
75
  Hexp::Node[:em, {}, Hexp::List["I am in your hexpz"] ]
76
76
  ]
77
77
  end
78
+ end
78
79
 
80
+ context 'with a nil in the child list' do
81
+ let(:node) { [:div, [nil]] }
82
+ it 'should raise an exception' do
83
+ expect{normalized}.to raise_exception(Hexp::FormatError)
84
+ end
79
85
  end
80
86
 
81
87
  end
@@ -1,29 +1,29 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hexp::Node, 'rewrite' do
4
- subject { hexp.rewrite(&blk) }
4
+ subject(:rewriter) { Hexp::Node::Rewriter.new(hexp, block) }
5
+
5
6
  let :hexp do
6
7
  H[:div, [
7
8
  [:a],
8
9
  [:p],
9
- [:br]
10
- ]
11
- ]
10
+ [:br]]]
12
11
  end
13
12
 
14
13
  context 'without a block' do
15
- let(:blk) { nil }
14
+ subject { hexp.rewrite(&block) }
15
+ let(:block) { nil }
16
16
 
17
- it 'returns an Enumerator' do
18
- expect(subject).to be_instance_of(Enumerator)
17
+ it 'returns a Rewriter' do
18
+ expect(subject).to be_instance_of(Hexp::Node::Rewriter)
19
19
  end
20
20
  end
21
21
 
22
22
  context 'with a block that returns [child]' do
23
- let(:blk) { proc {|child, parent| [child] } }
23
+ let(:block) { proc {|child, parent| [child] } }
24
24
 
25
- it 'should return an identical hexp' do
26
- expect(subject).to eq(hexp)
25
+ it 'should return an identical hexpable' do
26
+ expect(subject.to_hexp).to eq(hexp)
27
27
  end
28
28
  end
29
29
 
@@ -32,21 +32,22 @@ describe Hexp::Node, 'rewrite' do
32
32
  H[:span, [super()]]
33
33
  end
34
34
 
35
- let :blk do
35
+ let :block do
36
36
  proc do |child, parent|
37
37
  @tags << [child.tag, parent.tag]
38
+ nil
38
39
  end
39
40
  end
40
41
 
41
42
  it 'should traverse depth-first' do
42
43
  @tags = []
43
- subject
44
+ rewriter.to_hexp
44
45
  expect(@tags).to eq([[:a, :div], [:p, :div], [:br, :div], [:div, :span]])
45
46
  end
46
47
  end
47
48
 
48
49
  context 'when adding nodes' do
49
- let :blk do
50
+ let :block do
50
51
  proc do |child, parent|
51
52
  raise 'got my own node back' if child.tag == :blockquote
52
53
  # wrap paragraphs in a <blockquote>
@@ -59,15 +60,11 @@ describe Hexp::Node, 'rewrite' do
59
60
  end
60
61
 
61
62
  it 'should not pass those nodes again to the block' do
62
- subject.should == H[:div, [
63
+ rewriter.to_hexp.should == H[:div, [
63
64
  [:a],
64
65
  [:blockquote, [
65
- [:p]
66
- ]
67
- ],
68
- [:br]
69
- ]
70
- ]
66
+ [:p]]],
67
+ [:br]]]
71
68
  end
72
69
  end
73
70
 
@@ -76,7 +73,7 @@ describe Hexp::Node, 'rewrite' do
76
73
  H[:parent, [[:child]]]
77
74
  end
78
75
 
79
- let :blk do
76
+ let :block do
80
77
  proc do |child|
81
78
  expect(child).to eq(H[:child])
82
79
  [child]
@@ -84,47 +81,87 @@ describe Hexp::Node, 'rewrite' do
84
81
  end
85
82
 
86
83
  it 'should receive the child node as its argument' do
87
- subject
84
+ rewriter.to_hexp
88
85
  end
89
86
  end
90
87
 
91
88
  describe 'block response types' do
92
89
  context 'when responding with a single node' do
93
- let :blk do
90
+ let :block do
94
91
  proc do |child|
95
92
  H[:br]
96
93
  end
97
94
  end
98
95
 
99
96
  it 'should replace the existing node' do
100
- expect(subject).to eq H[:div, [ [:br] ]*3 ]
97
+ expect(rewriter.to_hexp).to eq H[:div, [ [:br] ]*3 ]
101
98
  end
102
99
  end
103
100
 
104
101
  context 'when responding with an array that starts with a Symbol' do
105
- let :blk do
102
+ let :block do
106
103
  proc do |child|
107
104
  [:br, {class: 'foo'} ]
108
105
  end
109
106
  end
110
107
 
111
108
  it 'should treat it as a node and replace the existing one' do
112
- expect(subject).to eq H[:div, [ [:br, {'class' => 'foo'}] ]*3 ]
109
+ expect(rewriter.to_hexp).to eq H[:div, [ [:br, {'class' => 'foo'}] ]*3 ]
110
+ end
111
+ end
112
+
113
+ context 'when responding with a String' do
114
+ let :hexp do
115
+ H[:div, [
116
+ [:p]
117
+ ]
118
+ ]
119
+ end
120
+
121
+ let :block do
122
+ proc do |child|
123
+ "Hello"
124
+ end
125
+ end
126
+
127
+ it 'should convert it to a text node' do
128
+ expect(rewriter.to_hexp).to eq H[:div, [ Hexp::TextNode.new("Hello") ] ]
113
129
  end
114
130
  end
115
131
 
132
+
116
133
  context 'when responding with nil' do
117
- let :blk do
134
+ let :block do
118
135
  proc do |node|
119
- [] if node.tag == :a
136
+ node if [:p, :br].include? node.tag
120
137
  end
121
138
  end
122
139
 
123
- it 'should keep the original node' do
124
- expect(subject).to eq H[:div, [ H[:p], H[:br] ]]
140
+ it 'should remove the original node' do
141
+ expect(rewriter.to_hexp).to eq H[:div, [ H[:p], H[:br] ]]
142
+ end
143
+ end
144
+ end
145
+
146
+ context 'when responding with something else than a Hexp, Array or String' do
147
+ let :block do
148
+ proc do |node|
149
+ Object.new
125
150
  end
126
151
  end
127
152
 
153
+ it 'should raise a FormatError' do
154
+ expect{rewriter.to_hexp}.to raise_exception(Hexp::FormatError)
155
+ end
156
+ end
157
+
158
+ context 'with a css selector argument' do
159
+ let(:selector) { 'p.foo' }
160
+
161
+ it 'should delegate to CssSelection, rather than Rewriter' do
162
+ expect(Hexp::Node::CssSelection).to receive(:new).with(hexp, selector).and_return(double(:rewrite => hexp))
163
+ hexp.rewrite(selector)
164
+ end
128
165
  end
129
166
 
130
167
  end