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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aaee41aa5d576b503c3e8ae1c8e99c144f5536d7975c724786ddd8331f41381f
4
- data.tar.gz: 15d291a353ca7f423144ca71cdc987462549635e53a282a7380545a61c04e829
3
+ metadata.gz: 0cfd83e0a3a67f3f946b6ce6cb841e1cc32b591a590c84f85152ebc15c0c9930
4
+ data.tar.gz: a75ffeb0933fa3fa30d80fa89a4eff6da2211420fb04ec2fddf6ea287573fcb2
5
5
  SHA512:
6
- metadata.gz: b29060e93d1eb05b1a199832c3ac4e62ad7ad60d9357d779a25da407e0e700fd718ad6c102888117695ed942a0669391371006908cc44b818e82fdff76d23bdc
7
- data.tar.gz: a71db0d5b3c2e34e33716e38c86579f7b180d8b0cea013a3945d1bb5e9fa1319b90d6b230c66f4c7ed0d051e7fd4acc9ff45c05ace702ae1c6a408de913e3f37
6
+ metadata.gz: 54886d022c1df32ce95b291769248387ef8d3044cda07f7ff9d314e91118c99dfacbe246b878e35f5bb7f8e1d4ddca88e5ebe387143d0eff3b7c446364c4d33c
7
+ data.tar.gz: 2437216d2611a1751e86f01c98d19442dbe33d6b21eee654711200dae3528a5000b23832a6b3b30cb91865a697d69605732143f56ba7cb6cb88949d8f27ef202
data/.rubocop.yml CHANGED
@@ -1,3 +1,7 @@
1
+ require:
2
+ - 'rubocop-rake'
3
+ - 'rubocop-rspec'
4
+
1
5
  Metrics/BlockLength:
2
6
  Exclude:
3
7
  - 'spec/**/*'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-html (0.2.19)
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.2.13'
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
- before { parse_html('<html><body>hello</body></html>') }
38
+ let(:document) { parse_html('<html><body>hello</body></html>') }
39
+
39
40
  it 'says hello' do
40
- expect(document.body).to contain_text 'hello'
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
- before { parse_html(ActionMailer::Base.deliveries.last.body.decoded) }
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 contain_text 'some text'
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 contain_text 'some text'
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 contain_text 'some text'
71
- expect(document.body.div(id: 'my-div').span(class: 'my-class my-other-class')).to contain_text 'some text'
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 contain_text 'some text'
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 contain_text 'some text'
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 contain_text 'my-class'
97
- expect(document.body.div.span['data-content']).to contain_text 'my content'
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 contain_text 'my-class'
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 contain_text 'my-class'
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 contain_text 'some text'
137
- expect(document.body.css('span.my-class')).to contain_text 'some text'
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.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module HTML
5
- VERSION = '0.2.20'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
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
- @document = RSpecHTML::Element.new(Nokogiri::HTML.parse(content), :document)
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.to_s
11
- actual.public_send(@expected, @options).present?
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)
@@ -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
@@ -9,6 +9,7 @@ require 'rspec_html/tags'
9
9
  require 'rspec_html/element'
10
10
  require 'rspec_html/search'
11
11
  require 'rspec_html/reconstituted_element'
12
+ require 'rspec_html/countable'
12
13
  require 'rspec_html/matchers'
13
14
 
14
15
  # Support module for rspec/html
@@ -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
- Expected <%= actual.reconstituted %> to match <%= expected.inspect %> but the element did not exist.
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
- Expected text in <%= actual.reconstituted %> <%= (actual.truncated_text).inspect %> to match <%= expected.inspect %> but it did not.
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.2.20
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-12 00:00:00.000000000 Z
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 %>