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