rspec-html 0.2.2 → 0.2.3

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: 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