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.
- data/.travis.yml +12 -3
- data/Changelog.md +9 -0
- data/Gemfile +3 -5
- data/Gemfile.devtools +20 -18
- data/Gemfile.lock +97 -84
- data/Rakefile +16 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +42 -18
- data/config/rubocop.yml +31 -0
- data/config/yardstick.yml +39 -1
- data/examples/from_nokogiri.rb +77 -0
- data/examples/selector_rewriter_chaining.rb +14 -0
- data/examples/todo.rb +138 -0
- data/examples/widget.rb +64 -0
- data/hexp.gemspec +8 -3
- data/lib/hexp.rb +103 -2
- data/lib/hexp/builder.rb +256 -0
- data/lib/hexp/css_selector.rb +205 -0
- data/lib/hexp/css_selector/parser.rb +74 -0
- data/lib/hexp/css_selector/sass_parser.rb +22 -0
- data/lib/hexp/dom.rb +0 -2
- data/lib/hexp/dsl.rb +27 -0
- data/lib/hexp/errors.rb +21 -0
- data/lib/hexp/h.rb +5 -2
- data/lib/hexp/list.rb +67 -9
- data/lib/hexp/node.rb +197 -41
- data/lib/hexp/node/attributes.rb +176 -0
- data/lib/hexp/node/children.rb +44 -0
- data/lib/hexp/node/css_selection.rb +73 -0
- data/lib/hexp/node/domize.rb +52 -6
- data/lib/hexp/node/normalize.rb +19 -9
- data/lib/hexp/node/pp.rb +32 -0
- data/lib/hexp/node/rewriter.rb +52 -0
- data/lib/hexp/node/selector.rb +59 -0
- data/lib/hexp/nokogiri/equality.rb +61 -0
- data/lib/hexp/nokogiri/reader.rb +27 -0
- data/lib/hexp/sass/selector_parser.rb +4 -0
- data/lib/hexp/text_node.rb +129 -9
- data/lib/hexp/version.rb +1 -1
- data/notes +34 -0
- data/spec/shared_helper.rb +6 -0
- data/spec/spec_helper.rb +2 -6
- data/spec/unit/hexp/builder_spec.rb +101 -0
- data/spec/unit/hexp/css_selector/attribute_spec.rb +137 -0
- data/spec/unit/hexp/css_selector/class_spec.rb +15 -0
- data/spec/unit/hexp/css_selector/comma_sequence_spec.rb +20 -0
- data/spec/unit/hexp/css_selector/element_spec.rb +11 -0
- data/spec/unit/hexp/css_selector/parser_spec.rb +51 -0
- data/spec/unit/hexp/css_selector/simple_sequence_spec.rb +48 -0
- data/spec/unit/hexp/dsl_spec.rb +55 -0
- data/spec/unit/hexp/h_spec.rb +38 -0
- data/spec/unit/hexp/list_spec.rb +19 -0
- data/spec/unit/hexp/node/attr_spec.rb +55 -0
- data/spec/unit/hexp/node/attributes_spec.rb +125 -0
- data/spec/unit/hexp/node/children_spec.rb +33 -0
- data/spec/unit/hexp/node/class_spec.rb +37 -0
- data/spec/unit/hexp/node/css_selection_spec.rb +86 -0
- data/spec/unit/hexp/node/normalize_spec.rb +12 -6
- data/spec/unit/hexp/node/rewrite_spec.rb +67 -30
- data/spec/unit/hexp/node/selector_spec.rb +78 -0
- data/spec/unit/hexp/node/text_spec.rb +7 -0
- data/spec/unit/hexp/node/to_dom_spec.rb +1 -1
- data/spec/unit/hexp/nokogiri/reader_spec.rb +8 -0
- data/spec/unit/hexp/parse_spec.rb +23 -0
- data/spec/unit/hexp/text_node_spec.rb +25 -0
- data/spec/unit/hexp_spec.rb +33 -0
- metadata +129 -16
- 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
|
7
|
+
let(:node) { [:div] }
|
8
8
|
|
9
9
|
it 'should treat the first as the tag' do
|
10
|
-
|
10
|
+
expect(normalized.tag).to eq :div
|
11
11
|
end
|
12
12
|
it 'should set an empty attribute list' do
|
13
|
-
|
13
|
+
expect(normalized.attributes).to eq Hash[]
|
14
14
|
end
|
15
15
|
it 'should set an empty children list' do
|
16
|
-
|
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
|
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 {
|
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
|
-
|
14
|
+
subject { hexp.rewrite(&block) }
|
15
|
+
let(:block) { nil }
|
16
16
|
|
17
|
-
it 'returns
|
18
|
-
expect(subject).to be_instance_of(
|
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(:
|
23
|
+
let(:block) { proc {|child, parent| [child] } }
|
24
24
|
|
25
|
-
it 'should return an identical
|
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 :
|
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
|
-
|
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 :
|
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
|
-
|
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 :
|
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
|
-
|
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 :
|
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(
|
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 :
|
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(
|
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 :
|
134
|
+
let :block do
|
118
135
|
proc do |node|
|
119
|
-
[]
|
136
|
+
node if [:p, :br].include? node.tag
|
120
137
|
end
|
121
138
|
end
|
122
139
|
|
123
|
-
it 'should
|
124
|
-
expect(
|
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
|