rspec-html 0.2.2 → 0.2.3
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/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/lib/rspec/html/version.rb +1 -1
- data/lib/rspec_html.rb +5 -2
- data/lib/rspec_html/element.rb +20 -11
- data/lib/rspec_html/matchers.rb +8 -4
- data/lib/rspec_html/matchers/base.rb +15 -6
- data/lib/rspec_html/matchers/contain_text.rb +1 -1
- data/lib/rspec_html/reconstituted_element.rb +36 -0
- data/lib/rspec_html/{searchable.rb → search.rb} +28 -17
- data/lib/rspec_html/tags.rb +4 -0
- data/templates/failure/contain_tag.erb +2 -2
- data/templates/failure/contain_text.erb +2 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf565ab0af76a2408b1185d8516aa6a18ac94bdf3521bd76235ffeab2cf85c84
|
4
|
+
data.tar.gz: cceb245c801bb10e3a84d942a8101014a585ac5b00e958bd682b6b0a3599143a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07ea3aedf2366de58c8c39af7ff8fcf17ff273ea1df71799215a0f19fdd029a0e32a8517b154e140175b6211d258d6376ef4faa40fdcb645ca4183196a4cec96
|
7
|
+
data.tar.gz: ca57887c542a4817181d1da196fc05f9739be992aa7a10b2cdb7aae293d066ddfc6c2009aeb204cfc315ac2745ef36ac1792fa29b22f11a09cb02cb441a7d14b
|
data/Gemfile.lock
CHANGED
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.2.
|
10
|
+
gem 'rspec-html', '~> 0.2.3'
|
11
11
|
```
|
12
12
|
|
13
13
|
And rebuild your bundle:
|
@@ -55,12 +55,14 @@ expect(document.body.div(id: 'my-div').span(align: 'left')).to contain_text 'som
|
|
55
55
|
```
|
56
56
|
|
57
57
|
#### Class Matching
|
58
|
-
|
58
|
+
_CSS_ classes are treated as a special case: to select an element matching a set of classes pass the `class` parameter:
|
59
59
|
```ruby
|
60
60
|
expect(document.body.div(id: 'my-div').span(class: 'my-class')).to contain_text 'some text'
|
61
|
-
expect(document.body.div(id: 'my-div').span(class:
|
61
|
+
expect(document.body.div(id: 'my-div').span(class: 'my-class my-other-class')).to contain_text 'some text'
|
62
62
|
```
|
63
63
|
|
64
|
+
Classes can be provided in any order, i.e. `'my-class my-other-class'` is equivalent to `'my-other-class my-class'`.
|
65
|
+
|
64
66
|
#### Text Matching
|
65
67
|
To select an element that includes a given text string (i.e. excluding mark-up) use the `text` option:
|
66
68
|
```ruby
|
@@ -131,7 +133,7 @@ expect(document.div(class: 'my-class')).to contain_tag :span
|
|
131
133
|
```
|
132
134
|
|
133
135
|
With options:
|
134
|
-
```
|
136
|
+
```ruby
|
135
137
|
expect(document.form(class: 'my-form')).to contain_tag :input, name: 'email', class: 'email-input'
|
136
138
|
```
|
137
139
|
|
data/lib/rspec/html/version.rb
CHANGED
data/lib/rspec_html.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'nokogiri'
|
4
|
+
|
4
5
|
require 'pathname'
|
6
|
+
require 'forwardable'
|
5
7
|
|
6
|
-
require 'rspec_html/searchable'
|
7
|
-
require 'rspec_html/element'
|
8
8
|
require 'rspec_html/tags'
|
9
|
+
require 'rspec_html/element'
|
10
|
+
require 'rspec_html/search'
|
11
|
+
require 'rspec_html/reconstituted_element'
|
9
12
|
require 'rspec_html/matchers'
|
10
13
|
|
11
14
|
# Support module for rspec/html
|
data/lib/rspec_html/element.rb
CHANGED
@@ -3,14 +3,20 @@
|
|
3
3
|
module RSpecHTML
|
4
4
|
# HTML DOM element abstraction
|
5
5
|
class Element
|
6
|
-
include Searchable
|
7
|
-
|
8
6
|
attr_reader :name, :element
|
9
7
|
|
10
|
-
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@search,
|
11
|
+
:has_css?, :has_xpath?, :include?, :present?, :exist?,
|
12
|
+
:text, :size, :length, :[]
|
13
|
+
|
14
|
+
def initialize(element, name, options: {}, siblings: [])
|
11
15
|
@name = name
|
12
16
|
@element = element
|
17
|
+
@options = options
|
13
18
|
@siblings = siblings
|
19
|
+
@search = Search.new(@element, @siblings)
|
14
20
|
end
|
15
21
|
|
16
22
|
def inspect
|
@@ -21,18 +27,21 @@ module RSpecHTML
|
|
21
27
|
@element.to_s
|
22
28
|
end
|
23
29
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
Tags.each do |tag|
|
31
|
+
define_method tag.downcase do |*args|
|
32
|
+
options = args.first
|
33
|
+
return @search.new_from_find(tag.downcase, options) if options.nil?
|
28
34
|
|
29
|
-
|
35
|
+
@search.new_from_where(tag.downcase, options)
|
30
36
|
end
|
37
|
+
end
|
31
38
|
|
32
|
-
|
33
|
-
|
39
|
+
def reconstituted
|
40
|
+
self.class.reconstituted(name, @options)
|
41
|
+
end
|
34
42
|
|
35
|
-
|
43
|
+
def self.reconstituted(tag, options = {})
|
44
|
+
ReconstitutedElement.new(tag, options).to_s
|
36
45
|
end
|
37
46
|
end
|
38
47
|
end
|
data/lib/rspec_html/matchers.rb
CHANGED
@@ -10,18 +10,22 @@ module RSpecHTML
|
|
10
10
|
extend RSpec::Matchers::DSL
|
11
11
|
extend RSpec::Matchers::DSL::Macros
|
12
12
|
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
13
14
|
def self.define_matcher(name, class_)
|
14
15
|
matcher name do |expected, options|
|
15
|
-
rspec_html_matcher = class_.new(expected, options)
|
16
|
+
rspec_html_matcher = class_.new(expected, options || {})
|
16
17
|
match do |actual|
|
17
|
-
|
18
|
-
|
18
|
+
rspec_html_matcher
|
19
|
+
.save_actual(actual)
|
20
|
+
.match(actual)
|
21
|
+
.tap { @actual = rspec_html_matcher.rspec_actual }
|
19
22
|
end
|
20
23
|
description { rspec_html_matcher.description }
|
21
|
-
failure_message {
|
24
|
+
failure_message { rspec_html_matcher.failure_message }
|
22
25
|
diffable if class_.diffable?
|
23
26
|
end
|
24
27
|
end
|
28
|
+
# rubocop:enable Metrics/MethodLength
|
25
29
|
|
26
30
|
define_matcher(:contain_text, ContainText)
|
27
31
|
define_matcher(:contain_tag, ContainTag)
|
@@ -18,24 +18,33 @@ module RSpecHTML
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
attr_reader :
|
21
|
+
attr_reader :rspec_actual
|
22
22
|
|
23
|
-
def initialize(expected, options
|
23
|
+
def initialize(expected, options)
|
24
24
|
@expected = expected
|
25
25
|
@options = options
|
26
26
|
end
|
27
27
|
|
28
28
|
def description
|
29
|
-
template(:description, @expected)
|
29
|
+
template(:description, @options, @expected)
|
30
30
|
end
|
31
31
|
|
32
|
-
def failure_message
|
33
|
-
template(:failure, @expected, actual)
|
32
|
+
def failure_message
|
33
|
+
template(:failure, @options, @expected, @actual)
|
34
|
+
end
|
35
|
+
|
36
|
+
def save_actual(actual)
|
37
|
+
@actual = actual
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def reconstituted(element, options)
|
42
|
+
RSpecHTML::Element.reconstituted(element, options)
|
34
43
|
end
|
35
44
|
|
36
45
|
private
|
37
46
|
|
38
|
-
def template(type, expected, actual = nil)
|
47
|
+
def template(type, options, expected, actual = nil)
|
39
48
|
ERB.new(template_path(type).read).result(binding)
|
40
49
|
end
|
41
50
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecHTML
|
4
|
+
# Reconstructs an HTML representation of an element from provided parameters.
|
5
|
+
class ReconstitutedElement
|
6
|
+
def initialize(tag, options)
|
7
|
+
@tag = tag
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
name = @tag.to_s.downcase
|
13
|
+
return '#document' if name == 'document'
|
14
|
+
return name if name == 'document'
|
15
|
+
return "<#{name}#{formatted_attributes} />" unless @options&.key?(:text)
|
16
|
+
|
17
|
+
"<#{name}#{formatted_attributes}>#{@options[:text]}</#{name}>"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def mapped_attributes
|
23
|
+
return [] if @options.nil?
|
24
|
+
|
25
|
+
@options.reject { |key| key.to_sym == :text }.map do |key, value|
|
26
|
+
next %(#{key}="#{value}") unless key.to_sym == :class && value.is_a?(Array)
|
27
|
+
|
28
|
+
%(#{key}="#{value.join(' ')}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def formatted_attributes
|
33
|
+
mapped_attributes.empty? ? nil : " #{mapped_attributes.join(' ')}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,8 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RSpecHTML
|
4
|
-
#
|
5
|
-
|
4
|
+
# Provides element/attribute/text searching for HTML entities
|
5
|
+
class Search
|
6
|
+
def initialize(element, siblings)
|
7
|
+
@element = element
|
8
|
+
@siblings = siblings
|
9
|
+
end
|
10
|
+
|
6
11
|
def include?(val)
|
7
12
|
text.include?(val)
|
8
13
|
end
|
@@ -38,7 +43,7 @@ module RSpecHTML
|
|
38
43
|
end
|
39
44
|
|
40
45
|
def text
|
41
|
-
@element&.text || ''
|
46
|
+
@element&.text&.gsub(/\s+/, ' ')&.strip || ''
|
42
47
|
end
|
43
48
|
|
44
49
|
def size
|
@@ -48,18 +53,29 @@ module RSpecHTML
|
|
48
53
|
end
|
49
54
|
alias length size
|
50
55
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
+
def new_from_find(tag, options)
|
57
|
+
Element.new(
|
58
|
+
find(tag),
|
59
|
+
tag,
|
60
|
+
options: options,
|
61
|
+
siblings: find(tag, all: true)
|
62
|
+
)
|
63
|
+
end
|
56
64
|
|
57
|
-
|
65
|
+
def new_from_where(tag, options)
|
66
|
+
Element.new(
|
67
|
+
where(tag, options),
|
68
|
+
tag,
|
69
|
+
options: options,
|
70
|
+
siblings: where(tag, options, all: true)
|
71
|
+
)
|
58
72
|
end
|
59
73
|
|
74
|
+
private
|
75
|
+
|
60
76
|
def index(val)
|
61
77
|
zero_index_error if val.zero?
|
62
|
-
self.class.new(@siblings[val - 1], name)
|
78
|
+
self.class.new(@siblings[val - 1], @element.name)
|
63
79
|
end
|
64
80
|
|
65
81
|
def range(val)
|
@@ -97,7 +113,8 @@ module RSpecHTML
|
|
97
113
|
end
|
98
114
|
|
99
115
|
def where_class(tag, class_or_classes)
|
100
|
-
|
116
|
+
classes = class_or_classes.is_a?(Array) ? class_or_classes : class_or_classes.to_s.split
|
117
|
+
selector = classes.map(&:to_s).join('.')
|
101
118
|
@element&.css("#{tag}.#{selector}")
|
102
119
|
end
|
103
120
|
|
@@ -106,11 +123,5 @@ module RSpecHTML
|
|
106
123
|
|
107
124
|
@element&.css(tag.to_s)
|
108
125
|
end
|
109
|
-
|
110
|
-
# rubocop:disable Lint/MissingSuper
|
111
|
-
def respond_to_missing?(method_name, *_)
|
112
|
-
Tags.include?(method_name)
|
113
|
-
end
|
114
|
-
# rubocop:enable Lint/MissingSuper
|
115
126
|
end
|
116
127
|
end
|
data/lib/rspec_html/tags.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<% if actual.element.nil? %>
|
2
|
-
Expected <%= actual
|
2
|
+
Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> but the element did not exist.
|
3
3
|
<% else %>
|
4
|
-
Expected
|
4
|
+
Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> but it did not.
|
5
5
|
<% end %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% if actual.element.nil? %>
|
2
|
-
Expected <%= actual.
|
2
|
+
Expected <%= actual.reconstituted %> to contain <%= expected.inspect %> but the element did not exist.
|
3
3
|
<% else %>
|
4
|
-
Expected text in
|
4
|
+
Expected text in <%= actual.reconstituted %> <%= (actual.text&.strip || '').inspect %> to contain <%= expected.inspect %> but it did not.
|
5
5
|
<% 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.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Farrell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -192,7 +192,8 @@ files:
|
|
192
192
|
- lib/rspec_html/matchers/base.rb
|
193
193
|
- lib/rspec_html/matchers/contain_tag.rb
|
194
194
|
- lib/rspec_html/matchers/contain_text.rb
|
195
|
-
- lib/rspec_html/
|
195
|
+
- lib/rspec_html/reconstituted_element.rb
|
196
|
+
- lib/rspec_html/search.rb
|
196
197
|
- lib/rspec_html/tags.rb
|
197
198
|
- rspec-html.gemspec
|
198
199
|
- templates/description/contain_tag.erb
|