hexp 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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