rspec-html 0.2.20 → 0.3.0

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