rspec-html 0.2.20 → 0.3.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/.rubocop.yml +4 -0
- data/Gemfile.lock +2 -4
- data/README.md +34 -16
- data/lib/rspec/html/version.rb +1 -1
- data/lib/rspec/html.rb +1 -3
- data/lib/rspec_html/countable.rb +51 -0
- data/lib/rspec_html/matchers/base.rb +6 -0
- data/lib/rspec_html/matchers/contain_tag.rb +9 -3
- data/lib/rspec_html/matchers/match_text.rb +12 -3
- data/lib/rspec_html/matchers.rb +4 -3
- data/lib/rspec_html.rb +1 -0
- data/templates/failure/contain_tag.erb +8 -0
- data/templates/failure/match_text.erb +10 -2
- metadata +3 -5
- data/lib/rspec_html/matchers/contain_text.rb +0 -19
- data/templates/description/contain_text.erb +0 -1
- data/templates/failure/contain_text.erb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cfd83e0a3a67f3f946b6ce6cb841e1cc32b591a590c84f85152ebc15c0c9930
|
4
|
+
data.tar.gz: a75ffeb0933fa3fa30d80fa89a4eff6da2211420fb04ec2fddf6ea287573fcb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54886d022c1df32ce95b291769248387ef8d3044cda07f7ff9d314e91118c99dfacbe246b878e35f5bb7f8e1d4ddca88e5ebe387143d0eff3b7c446364c4d33c
|
7
|
+
data.tar.gz: 2437216d2611a1751e86f01c98d19442dbe33d6b21eee654711200dae3528a5000b23832a6b3b30cb91865a697d69605732143f56ba7cb6cb88949d8f27ef202
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rspec-html (0.
|
4
|
+
rspec-html (0.3.0)
|
5
5
|
nokogiri (~> 1.10)
|
6
6
|
rspec (~> 3.0)
|
7
7
|
|
@@ -21,10 +21,8 @@ GEM
|
|
21
21
|
i18n (1.13.0)
|
22
22
|
concurrent-ruby (~> 1.0)
|
23
23
|
json (2.6.3)
|
24
|
-
mini_portile2 (2.8.2)
|
25
24
|
minitest (5.18.0)
|
26
|
-
nokogiri (1.13.10)
|
27
|
-
mini_portile2 (~> 2.8.0)
|
25
|
+
nokogiri (1.13.10-x86_64-linux)
|
28
26
|
racc (~> 1.4)
|
29
27
|
paint (2.3.0)
|
30
28
|
parallel (1.23.0)
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ _RSpec::HTML_ provides a simple object interface to HTML responses from [_RSpec
|
|
7
7
|
Add the gem to your `Gemfile`:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem 'rspec-html', '~> 0.
|
10
|
+
gem 'rspec-html', '~> 0.3.0'
|
11
11
|
```
|
12
12
|
|
13
13
|
And rebuild your bundle:
|
@@ -35,28 +35,31 @@ The top-level object `document` is available in all tests which reflects the cur
|
|
35
35
|
If you need to parse _HTML_ manually you can use the provided `parse_html` helper and then access `document` as normal:
|
36
36
|
|
37
37
|
```ruby
|
38
|
-
|
38
|
+
let(:document) { parse_html('<html><body>hello</body></html>') }
|
39
|
+
|
39
40
|
it 'says hello' do
|
40
|
-
expect(document.body).to
|
41
|
+
expect(document.body).to match_text 'hello'
|
41
42
|
end
|
42
43
|
```
|
43
44
|
|
44
45
|
This method can also be used for _ActionMailer_ emails:
|
45
46
|
```ruby
|
46
|
-
|
47
|
+
let(:document) { parse_html(ActionMailer::Base.deliveries.last.body.decoded) }
|
47
48
|
```
|
48
49
|
|
50
|
+
**Changed in 0.3.0**: `parse_html` no longer sets the `document` automatically, you must use a `let` block to assign it yourself.
|
51
|
+
|
49
52
|
To navigate the _DOM_ by a sequence of tag names use chained method calls on the `document` object:
|
50
53
|
|
51
54
|
#### Tag Traversal
|
52
55
|
```ruby
|
53
|
-
expect(document.body.div.span).to
|
56
|
+
expect(document.body.div.span).to match_text 'some text'
|
54
57
|
```
|
55
58
|
|
56
59
|
#### Attribute Matching
|
57
60
|
To select an element matching certain attributes pass a hash to any of the chained methods:
|
58
61
|
```ruby
|
59
|
-
expect(document.body.div(id: 'my-div').span(align: 'left')).to
|
62
|
+
expect(document.body.div(id: 'my-div').span(align: 'left')).to match_text 'some text'
|
60
63
|
```
|
61
64
|
|
62
65
|
Special attributes like `checked` can be found using the `#attributes` method:
|
@@ -67,8 +70,8 @@ expect(document.input(type: 'checkbox', name: 'my-name')).attributes).to include
|
|
67
70
|
#### Class Matching
|
68
71
|
_CSS_ classes are treated as a special case: to select an element matching a set of classes pass the `class` parameter:
|
69
72
|
```ruby
|
70
|
-
expect(document.body.div(id: 'my-div').span(class: 'my-class')).to
|
71
|
-
expect(document.body.div(id: 'my-div').span(class: 'my-class my-other-class')).to
|
73
|
+
expect(document.body.div(id: 'my-div').span(class: 'my-class')).to match_text 'some text'
|
74
|
+
expect(document.body.div(id: 'my-div').span(class: 'my-class my-other-class')).to match_text 'some text'
|
72
75
|
```
|
73
76
|
|
74
77
|
Classes can be provided in any order, i.e. `'my-class my-other-class'` is equivalent to `'my-other-class my-class'`.
|
@@ -76,12 +79,25 @@ Classes can be provided in any order, i.e. `'my-class my-other-class'` is equiva
|
|
76
79
|
#### Simple CSS Matching
|
77
80
|
To use a simple CSS selector when no other attributes are needed, pass a string to the tag method:
|
78
81
|
```ruby
|
79
|
-
expect(document.body.div('#my-id.my-class1.my-class2')).to
|
82
|
+
expect(document.body.div('#my-id.my-class1.my-class2')).to match_text 'some text'
|
80
83
|
```
|
81
84
|
|
82
85
|
This is effectively shorthand for:
|
83
86
|
```ruby
|
84
|
-
expect(document.body.div(id: 'my-id', class: 'my-class1 my-class2')).to
|
87
|
+
expect(document.body.div(id: 'my-id', class: 'my-class1 my-class2')).to match_text 'some text'
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Counting Matchers
|
91
|
+
Use `once`, `twice`, `times`, `at_least`, and `at_most` to match element counts:
|
92
|
+
```ruby
|
93
|
+
expect(document.div('.my-class')).to match_text('some text').once
|
94
|
+
expect(document.div('.my-class')).to match_text('some other text').twice
|
95
|
+
expect(document.div('.my-class')).to match_text('some').times(3)
|
96
|
+
expect(document.div('.my-class')).to match_text('some').at_least(:twice)
|
97
|
+
expect(document.div('.my-class')).to match_text('some').at_least.times(3)
|
98
|
+
expect(document.div('.my-class')).to match_text('some text').at_most.once
|
99
|
+
expect(document.div('.my-class')).to match_text('some other text').at_most.twice
|
100
|
+
expect(document.div('.my-class')).to match_text('text').at_most.times(3)
|
85
101
|
```
|
86
102
|
|
87
103
|
#### Text Matching
|
@@ -93,8 +109,8 @@ expect(document.body.div(text: 'some text').input[:value]).to eql 'some-value'
|
|
93
109
|
#### Attribute Retrieval
|
94
110
|
To select an attribute from an element use the hash-style interface:
|
95
111
|
```ruby
|
96
|
-
expect(document.body.div.span[:class]).to
|
97
|
-
expect(document.body.div.span['data-content']).to
|
112
|
+
expect(document.body.div.span[:class]).to match_text 'my-class'
|
113
|
+
expect(document.body.div.span['data-content']).to match_text 'my content'
|
98
114
|
```
|
99
115
|
|
100
116
|
#### Retrieve all matching elements
|
@@ -106,13 +122,13 @@ expect(document.span.all.map(&:text)).to eql ['foo', 'bar', 'baz']
|
|
106
122
|
#### Indexing a Matching Set
|
107
123
|
To select an index from a set of matched elements use the array-style interface (the first matching element is `1`, not `0`):
|
108
124
|
```ruby
|
109
|
-
expect(document.body.div[1].span[1][:class]).to
|
125
|
+
expect(document.body.div[1].span[1][:class]).to match_text 'my-class'
|
110
126
|
```
|
111
127
|
|
112
128
|
Alternatively, use `#first`, `#last` or, when using _ActiveSupport_, `#second`, `#third`, etc. are also available:
|
113
129
|
|
114
130
|
```ruby
|
115
|
-
expect(document.body.div.first.span.last[:class]).to
|
131
|
+
expect(document.body.div.first.span.last[:class]).to match_text 'my-class'
|
116
132
|
```
|
117
133
|
|
118
134
|
|
@@ -133,8 +149,8 @@ expect(document.body.div.length).to eql 3
|
|
133
149
|
#### XPath / CSS Selectors
|
134
150
|
If you need something more specific you can always use the _Nokogiri_ `#xpath` and `#css` methods on any element:
|
135
151
|
```ruby
|
136
|
-
expect(document.body.xpath('//span[@class="my-class"]')).to
|
137
|
-
expect(document.body.css('span.my-class')).to
|
152
|
+
expect(document.body.xpath('//span[@class="my-class"]')).to match_text 'some text'
|
153
|
+
expect(document.body.css('span.my-class')).to match_text 'some text'
|
138
154
|
```
|
139
155
|
|
140
156
|
To simply check that an _XPath_ or _CSS_ selector exists use `have_xpath` and `have_css`:
|
@@ -153,6 +169,8 @@ expect(document.input(type: 'checkbox')).to be_checked
|
|
153
169
|
### Custom Matchers
|
154
170
|
<a name="matchers"></a>
|
155
171
|
|
172
|
+
**Changed in 0.3.0**: The `contain_text` matcher has been removed. Use `match_text` instead.
|
173
|
+
|
156
174
|
#### match_text
|
157
175
|
|
158
176
|
Use the `match_text` matcher to locate text within a _DOM_ element. All mark-up elements are stripped when using this matcher.
|
data/lib/rspec/html/version.rb
CHANGED
data/lib/rspec/html.rb
CHANGED
@@ -9,15 +9,13 @@ module RSpec
|
|
9
9
|
# Module extension for RSpec::SharedContext
|
10
10
|
module HTML
|
11
11
|
def document
|
12
|
-
return @document if @document
|
13
|
-
|
14
12
|
RSpecHTML::Element.new(Nokogiri::HTML.parse(response.body), :document)
|
15
13
|
rescue NameError
|
16
14
|
raise RSpecHTML::NoResponseError, 'No `response` object found. Make a request first.'
|
17
15
|
end
|
18
16
|
|
19
17
|
def parse_html(content)
|
20
|
-
|
18
|
+
RSpecHTML::Element.new(Nokogiri::HTML.parse(content), :document)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecHTML
|
4
|
+
# DSL module for all matchers, provides element counting utilities.
|
5
|
+
module Countable
|
6
|
+
def once
|
7
|
+
@expected_count = 1
|
8
|
+
@expected_count_type ||= :exact
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def twice
|
13
|
+
@expected_count = 2
|
14
|
+
@expected_count_type ||= :exact
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def times(count)
|
19
|
+
@expected_count = count
|
20
|
+
@expected_count_type ||= :exact
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def at_least(count = nil)
|
25
|
+
@expected_count = { once: 1, twice: 2 }.fetch(count, nil)
|
26
|
+
@expected_count_type = :at_least
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def at_most(count = nil)
|
31
|
+
@expected_count = { once: 1, twice: 2 }.fetch(count, nil)
|
32
|
+
@expected_count_type = :at_most
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def count_match?
|
39
|
+
case @expected_count_type
|
40
|
+
when :exact
|
41
|
+
@actual_count == @expected_count
|
42
|
+
when :at_least
|
43
|
+
@actual_count >= @expected_count
|
44
|
+
when :at_most
|
45
|
+
@actual_count <= @expected_count
|
46
|
+
else
|
47
|
+
raise ArgumentError, 'Unknown count comparison type'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -42,6 +42,12 @@ module RSpecHTML
|
|
42
42
|
RSpecHTML::Element.reconstituted(element, options)
|
43
43
|
end
|
44
44
|
|
45
|
+
def pluralize(number, string)
|
46
|
+
return "#{number} #{string}" if number == 1
|
47
|
+
|
48
|
+
"#{number} #{string}s"
|
49
|
+
end
|
50
|
+
|
45
51
|
private
|
46
52
|
|
47
53
|
def template(type, options, expected, actual = nil)
|
@@ -5,10 +5,16 @@ module RSpecHTML
|
|
5
5
|
# Matches elements within a given DOM element.
|
6
6
|
class ContainTag
|
7
7
|
include Base
|
8
|
+
include Countable
|
8
9
|
|
9
|
-
def match(actual)
|
10
|
-
@actual = actual
|
11
|
-
|
10
|
+
def match(actual, expected_count:, expected_count_type:)
|
11
|
+
@actual = actual
|
12
|
+
@expected_count = expected_count
|
13
|
+
@expected_count_type = expected_count_type
|
14
|
+
return actual.public_send(@expected, @options).present? if @expected_count.nil?
|
15
|
+
|
16
|
+
@actual_count = actual.public_send(@expected, @options).size
|
17
|
+
count_match?
|
12
18
|
end
|
13
19
|
end
|
14
20
|
end
|
@@ -5,11 +5,14 @@ module RSpecHTML
|
|
5
5
|
# Matches text or regex within a given DOM element (strips HTML tags and compares text content only).
|
6
6
|
class MatchText
|
7
7
|
include Base
|
8
|
+
include Countable
|
8
9
|
|
9
10
|
diffable
|
10
11
|
|
11
|
-
def match(actual)
|
12
|
+
def match(actual, expected_count:, expected_count_type:)
|
12
13
|
raise_argument_error unless actual.is_a?(RSpecHTML::Element)
|
14
|
+
@expected_count = expected_count
|
15
|
+
@expected_count_type = expected_count_type
|
13
16
|
@rspec_actual = actual&.text
|
14
17
|
return regexp_match?(actual) || regexp_siblings_match?(actual) if @expected.is_a?(Regexp)
|
15
18
|
|
@@ -19,7 +22,10 @@ module RSpecHTML
|
|
19
22
|
private
|
20
23
|
|
21
24
|
def regexp_match?(actual)
|
22
|
-
@expected.match(actual&.text || '')
|
25
|
+
return @expected.match(actual&.text || '') if @expected_count.nil?
|
26
|
+
|
27
|
+
@actual_count = actual&.text&.scan(@expected)&.size
|
28
|
+
count_match?
|
23
29
|
end
|
24
30
|
|
25
31
|
def regexp_siblings_match?(actual)
|
@@ -27,7 +33,10 @@ module RSpecHTML
|
|
27
33
|
end
|
28
34
|
|
29
35
|
def string_match?(actual)
|
30
|
-
(actual&.text || '').include?(@expected.to_s)
|
36
|
+
return (actual&.text || '').include?(@expected.to_s) if @expected_count.nil?
|
37
|
+
|
38
|
+
@actual_count = actual&.text&.scan(@expected)&.size
|
39
|
+
count_match?
|
31
40
|
end
|
32
41
|
|
33
42
|
def string_siblings_match?(actual)
|
data/lib/rspec_html/matchers.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rspec_html/matchers/base'
|
4
|
-
require 'rspec_html/matchers/contain_text'
|
5
4
|
require 'rspec_html/matchers/contain_tag'
|
6
5
|
require 'rspec_html/matchers/match_text'
|
7
6
|
|
@@ -18,17 +17,19 @@ module RSpecHTML
|
|
18
17
|
match do |actual|
|
19
18
|
rspec_html_matcher
|
20
19
|
.save_actual(actual)
|
21
|
-
.match(actual)
|
20
|
+
.match(actual, expected_count: @expected_count, expected_count_type: @expected_count_type)
|
22
21
|
.tap { @actual = rspec_html_matcher.rspec_actual }
|
23
22
|
end
|
24
23
|
description { rspec_html_matcher.description }
|
24
|
+
|
25
|
+
include Countable
|
26
|
+
|
25
27
|
failure_message { rspec_html_matcher.failure_message }
|
26
28
|
diffable if class_.diffable?
|
27
29
|
end
|
28
30
|
end
|
29
31
|
# rubocop:enable Metrics/MethodLength
|
30
32
|
|
31
|
-
define_matcher(:contain_text, ContainText)
|
32
33
|
define_matcher(:contain_tag, ContainTag)
|
33
34
|
define_matcher(:match_text, MatchText)
|
34
35
|
end
|
data/lib/rspec_html.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
<% if actual.element.nil? %>
|
2
|
+
<% if @expected_count.nil? %>
|
2
3
|
Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> but the element did not exist.
|
4
|
+
<% else %>
|
5
|
+
Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> <%= @expected_count_type.to_s.tr('_', ' ') %> <%= pluralize(@expected_count, 'time') %> but the element did not exist.
|
6
|
+
<% end %>
|
3
7
|
<% else %>
|
8
|
+
<% if @expected_count.nil? %>
|
4
9
|
Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> but it did not.
|
10
|
+
<% else %>
|
11
|
+
Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> <%= @expected_count_type.to_s.tr('_', ' ') %> <%= pluralize(@expected_count, 'time') %> but it was found <%= pluralize(@actual_count, 'time') %>.
|
12
|
+
<% end %>
|
5
13
|
<% end %>
|
@@ -1,5 +1,13 @@
|
|
1
1
|
<% unless actual.exist? %>
|
2
|
-
|
2
|
+
<% if @expected_count.nil? %>
|
3
|
+
Expected <%= actual.reconstituted %> to match <%= expected.inspect %> but the element did not exist.
|
4
|
+
<% else %>
|
5
|
+
Expected <%= actual.reconstituted %> to match <%= expected.inspect %> <%= @expected_count_type.to_s.tr('_', ' ') %> <%= pluralize(@expected_count, 'time') %> but the element did not exist.
|
6
|
+
<% end %>
|
3
7
|
<% else %>
|
4
|
-
|
8
|
+
<% if @expected_count.nil? %>
|
9
|
+
Expected text in <%= actual.reconstituted %> <%= (actual.truncated_text).inspect %> to match <%= expected.inspect %> but it did not.
|
10
|
+
<% else %>
|
11
|
+
Expected text in <%= actual.reconstituted %> <%= (actual.truncated_text).inspect %> to match <%= @expected_count_type.to_s.tr('_', ' ') %> <%= expected.inspect %> exactly <%= pluralize(@expected_count, 'time') %> but it was matched <%= pluralize(@actual_count, 'time') %>.
|
12
|
+
<% end %>
|
5
13
|
<% end %>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-html
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Farrell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -61,21 +61,19 @@ files:
|
|
61
61
|
- lib/rspec/html.rb
|
62
62
|
- lib/rspec/html/version.rb
|
63
63
|
- lib/rspec_html.rb
|
64
|
+
- lib/rspec_html/countable.rb
|
64
65
|
- lib/rspec_html/element.rb
|
65
66
|
- lib/rspec_html/matchers.rb
|
66
67
|
- lib/rspec_html/matchers/base.rb
|
67
68
|
- lib/rspec_html/matchers/contain_tag.rb
|
68
|
-
- lib/rspec_html/matchers/contain_text.rb
|
69
69
|
- lib/rspec_html/matchers/match_text.rb
|
70
70
|
- lib/rspec_html/reconstituted_element.rb
|
71
71
|
- lib/rspec_html/search.rb
|
72
72
|
- lib/rspec_html/tags.rb
|
73
73
|
- rspec-html.gemspec
|
74
74
|
- templates/description/contain_tag.erb
|
75
|
-
- templates/description/contain_text.erb
|
76
75
|
- templates/description/match_text.erb
|
77
76
|
- templates/failure/contain_tag.erb
|
78
|
-
- templates/failure/contain_text.erb
|
79
77
|
- templates/failure/match_text.erb
|
80
78
|
homepage: https://github.com/bobf/rspec-html
|
81
79
|
licenses:
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RSpecHTML
|
4
|
-
module Matchers
|
5
|
-
# Matches text within a given DOM element (strips HTML tags and compares text content only).
|
6
|
-
# (Deprecated in favour of `#match_text`.
|
7
|
-
class ContainText
|
8
|
-
include Base
|
9
|
-
|
10
|
-
diffable
|
11
|
-
|
12
|
-
def match(actual)
|
13
|
-
warn('[rspec-html] The `contain_text` matcher is deprecated. Use `match_text` instead.')
|
14
|
-
@rspec_actual = actual&.text
|
15
|
-
(actual&.text || '').include?(@expected.to_s)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
contain text <%= expected.inspect %>
|
@@ -1,5 +0,0 @@
|
|
1
|
-
<% unless actual.exist? %>
|
2
|
-
Expected <%= actual.reconstituted %> to contain <%= expected.inspect %> but the element did not exist.
|
3
|
-
<% else %>
|
4
|
-
Expected text in <%= actual.reconstituted %> <%= (actual.truncated_text).inspect %> to contain <%= expected.inspect %> but it did not.
|
5
|
-
<% end %>
|