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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffb46cc469517211466583fd590e8d77a39368eba859513771a43438508c3985
4
- data.tar.gz: db46d8cc7a56d00e91517d8df9465d7c5915f82d4fc35b9314ba02f043921c1b
3
+ metadata.gz: cf565ab0af76a2408b1185d8516aa6a18ac94bdf3521bd76235ffeab2cf85c84
4
+ data.tar.gz: cceb245c801bb10e3a84d942a8101014a585ac5b00e958bd682b6b0a3599143a
5
5
  SHA512:
6
- metadata.gz: d11b514814d2e1d3d1cbbe3dae17a36f2630863694cb6869accda703b873de201bbaa20b31ff01ff1368d7dae7906950c6d4cbfeb6e51700d05f8408e94f92c9
7
- data.tar.gz: 16a6c1d7a286940171c8feafbd35f0732906e1adeb4b17737f38f0db393f47684fc3909ddacdcfc81297e90ed75fdfd7e6d18a3cdf8c68ed5f17972947715d73
6
+ metadata.gz: 07ea3aedf2366de58c8c39af7ff8fcf17ff273ea1df71799215a0f19fdd029a0e32a8517b154e140175b6211d258d6376ef4faa40fdcb645ca4183196a4cec96
7
+ data.tar.gz: ca57887c542a4817181d1da196fc05f9739be992aa7a10b2cdb7aae293d066ddfc6c2009aeb204cfc315ac2745ef36ac1792fa29b22f11a09cb02cb441a7d14b
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-html (0.2.2)
4
+ rspec-html (0.2.3)
5
5
  nokogiri (~> 1.10)
6
6
  rspec (~> 3.0)
7
7
 
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.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
- CSS classes are treated as a special case: to select an element matching a specific class (or array of classes) pass the `class` parameter:
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: ['my-class', 'my-other-class'])).to contain_text 'some text'
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
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module HTML
5
- VERSION = '0.2.2'
5
+ VERSION = '0.2.3'
6
6
  end
7
7
  end
@@ -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
@@ -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
- def initialize(element, name, siblings: [])
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
- def self.reconstituted(tag, options = {})
25
- name = tag.to_s.downcase
26
- mapped_attributes = options.reject { |key| key.to_sym == :text }.map do |key, value|
27
- next %(#{key}="#{value}") unless key.to_sym == :class && value.is_a?(Array)
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
- %(#{key}="#{value.join(' ')}")
35
+ @search.new_from_where(tag.downcase, options)
30
36
  end
37
+ end
31
38
 
32
- attributes = mapped_attributes.empty? ? nil : " #{mapped_attributes.join(' ')}"
33
- return "<#{name}#{attributes} />" unless options.key?(:text)
39
+ def reconstituted
40
+ self.class.reconstituted(name, @options)
41
+ end
34
42
 
35
- "<#{name}#{attributes}>#{options[:text]}</#{name}>"
43
+ def self.reconstituted(tag, options = {})
44
+ ReconstitutedElement.new(tag, options).to_s
36
45
  end
37
46
  end
38
47
  end
@@ -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
- @actual = rspec_html_matcher.actual
18
- rspec_html_matcher.match(actual)
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 { |actual| rspec_html_matcher.failure_message(actual) }
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 :actual
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(actual)
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
 
@@ -9,7 +9,7 @@ module RSpecHTML
9
9
  diffable
10
10
 
11
11
  def match(actual)
12
- @actual = actual&.text&.strip
12
+ @rspec_actual = actual&.text
13
13
  (actual&.text || '').include?(@expected.to_s)
14
14
  end
15
15
  end
@@ -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
- # Mixin module providing methods for searching text content of HTML entities
5
- module Searchable
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
- private
52
-
53
- def method_missing(tag, *args)
54
- return super unless Tags.include?(tag)
55
- return self.class.new(find(tag), tag, siblings: find(tag, all: true)) if args.empty?
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
- self.class.new(where(tag, args.first), tag, siblings: where(tag, args.first, all: true))
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
- selector = class_or_classes.is_a?(Array) ? class_or_classes.join('.') : class_or_classes
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
@@ -7,6 +7,10 @@ module RSpecHTML
7
7
  tags.include?(val.to_s.upcase)
8
8
  end
9
9
 
10
+ def self.each(&block)
11
+ tags.each { |tag| block.call(tag) }
12
+ end
13
+
10
14
  # rubocop:disable Metrics/MethodLength
11
15
  def self.tags
12
16
  %w[
@@ -1,5 +1,5 @@
1
1
  <% if actual.element.nil? %>
2
- Expected <%= actual.name %> to contain <%= RSpecHTML::Element.reconstituted(expected, @options) %> but the element did not exist.
2
+ Expected <%= reconstituted(actual, @options) %> to contain <%= reconstituted(expected, @options) %> but the element did not exist.
3
3
  <% else %>
4
- Expected <<%= actual.name %>> to contain <%= RSpecHTML::Element.reconstituted(expected, @options) %> but it did not.
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.name %> to contain <%= expected.inspect %> but the element did not exist.
2
+ Expected <%= actual.reconstituted %> to contain <%= expected.inspect %> but the element did not exist.
3
3
  <% else %>
4
- Expected text in <<%= actual.name %>> <%= (actual.text&.strip || '').inspect %> to contain <%= expected.inspect %> but it did not.
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.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-12 00:00:00.000000000 Z
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/searchable.rb
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