dommy-rails 0.9.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.
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "rspec/expectations"
5
+ rescue LoadError => e
6
+ raise LoadError, "dommy-rails RSpec integration requires rspec-expectations. Add `gem \"rspec\"` or `gem \"rspec-expectations\"` to your Gemfile.", e.backtrace
7
+ end
8
+
9
+ require "dommy/rspec/capy_style_matchers"
10
+ require_relative "../rails"
11
+ require_relative "rspec/matchers"
12
+ require_relative "rspec/integration"
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dommy
4
+ module Rails
5
+ module Stimulus
6
+ module_function
7
+
8
+ # ----- Scope-level predicates (any element in scope matches) -----
9
+
10
+ def controller?(scope, name)
11
+ scope.query_selector_all("[data-controller]").to_a.any? { |element| has_controller?(element, name) }
12
+ end
13
+
14
+ def action?(scope, action)
15
+ scope.query_selector_all("[data-action]").to_a.any? { |element| has_action?(element, action) }
16
+ end
17
+
18
+ def target?(scope, controller, target)
19
+ scope.query_selector_all("[data-#{controller}-target]").to_a.any? { |element| has_target?(element, controller, target) }
20
+ end
21
+
22
+ def value?(scope, controller, key, value)
23
+ scope.query_selector_all("[data-#{controller}-#{key}-value]").to_a.any? { |element| has_value?(element, controller, key, value) }
24
+ end
25
+
26
+ # ----- Element-level predicates -----
27
+
28
+ def has_controller?(element, name)
29
+ controllers = element.get_attribute("data-controller").to_s.split
30
+ controllers.include?(name.to_s)
31
+ end
32
+
33
+ def has_action?(element, action)
34
+ actions = element.get_attribute("data-action").to_s.split
35
+ actions.any? { |a| a == action.to_s || a.end_with?("->#{action}") }
36
+ end
37
+
38
+ def has_target?(element, controller, target)
39
+ element.has_attribute?("data-#{controller}-target") &&
40
+ element.get_attribute("data-#{controller}-target").to_s.split.include?(target.to_s)
41
+ end
42
+
43
+ def has_value?(element, controller, key, value)
44
+ attr_name = "data-#{controller}-#{key}-value"
45
+ return false unless element.has_attribute?(attr_name)
46
+ element.get_attribute(attr_name).to_s == value.to_s
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dommy
4
+ module Rails
5
+ module TurboStream
6
+ module_function
7
+
8
+ def parse(body)
9
+ Dommy.parse(body).document.query_selector_all("turbo-stream").to_a
10
+ end
11
+
12
+ def find(body, action:, target:)
13
+ parse(body).find do |stream|
14
+ stream.get_attribute("action") == action.to_s && stream.get_attribute("target") == target.to_s
15
+ end
16
+ end
17
+
18
+ def matches?(body, action:, target:)
19
+ !find(body, action: action, target: target).nil?
20
+ end
21
+
22
+ # The <template> payload of a stream element, parsed as its own
23
+ # document (nil when the stream has no template).
24
+ def fragment_document(stream)
25
+ fragment = stream.query_selector("template")&.inner_html
26
+ fragment ? Dommy.parse(fragment).document : nil
27
+ end
28
+
29
+ def fragment_for(body, action:, target:)
30
+ stream = find(body, action: action, target: target)
31
+ stream ? stream.query_selector("template")&.inner_html : nil
32
+ end
33
+
34
+ def fragment_document_for(body, action:, target:)
35
+ stream = find(body, action: action, target: target)
36
+ stream ? fragment_document(stream) : nil
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "url_normalizer"
4
+
5
+ module Dommy
6
+ module Rails
7
+ # Adapts Rails URL normalization to the matcher protocol accepted by
8
+ # Dommy::Internal::ElementMatching.attribute_matches?, so `href:` /
9
+ # `action:` criteria absorb scheme/host, query-parameter order,
10
+ # HTML-escaped `&amp;`, and trailing-slash differences.
11
+ class UrlMatcher
12
+ def initialize(expected)
13
+ @expected = expected
14
+ end
15
+
16
+ def matches?(actual)
17
+ case @expected
18
+ when Regexp
19
+ actual.to_s.match?(@expected)
20
+ else
21
+ UrlNormalizer.equal?(@expected, actual)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "uri"
5
+
6
+ module Dommy
7
+ module Rails
8
+ # Normalizes URLs so Rails URL-helper output and rendered HTML
9
+ # compare equal despite representational differences.
10
+ #
11
+ # Deliberately lenient: scheme and host are dropped, query
12
+ # parameters are sorted, HTML entities are unescaped, and trailing
13
+ # slashes are removed. Strict external-host matching is out of
14
+ # scope (see README).
15
+ module UrlNormalizer
16
+ module_function
17
+
18
+ def equal?(expected, actual)
19
+ normalize(expected) == normalize(actual)
20
+ end
21
+
22
+ def normalize(url)
23
+ url = url.to_s
24
+ return "" if url.empty?
25
+
26
+ # Attribute values may arrive HTML-escaped (e.g. &amp; in raw
27
+ # response bodies).
28
+ url = CGI.unescapeHTML(url)
29
+
30
+ uri = URI.parse(url)
31
+ path = uri.path.to_s
32
+ path = path.chomp("/") unless path == "/"
33
+
34
+ query = if uri.query
35
+ params = URI.decode_www_form(uri.query).sort_by { |k, _| k }
36
+ URI.encode_www_form(params)
37
+ end
38
+
39
+ [path, query].compact.join("?")
40
+ rescue URI::InvalidURIError
41
+ url
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ module Dommy
2
+ module Rails
3
+ VERSION = "0.9.0"
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ require "dommy"
2
+ require_relative "rails/version"
3
+ require_relative "rails/form_inspector"
4
+ require_relative "rails/turbo_stream"
5
+ require_relative "rails/stimulus"
6
+ require_relative "rails/lint"
7
+ require_relative "rails/url_normalizer"
8
+ require_relative "rails/url_matcher"
9
+ require_relative "rails/mail_part"
10
+ require_relative "rails/match_target"
11
+ require_relative "rails/dom_source"
12
+ require_relative "rails/page_inspector"
13
+ require_relative "rails/aria_snapshot_matching"
14
+
15
+ module Dommy
16
+ module Rails
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dommy-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - takahashim
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: dommy
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 0.9.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.9.0
26
+ description: Rails-specific matchers and assertions for Dommy, including form helper
27
+ understanding, Turbo Stream support, Stimulus attribute checking, and HTML quality
28
+ linting.
29
+ email:
30
+ - takahashimm@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - README.md
36
+ - lib/dommy/rails.rb
37
+ - lib/dommy/rails/aria_snapshot_matching.rb
38
+ - lib/dommy/rails/browser_spec.rb
39
+ - lib/dommy/rails/dom_source.rb
40
+ - lib/dommy/rails/form_inspector.rb
41
+ - lib/dommy/rails/lint.rb
42
+ - lib/dommy/rails/mail_part.rb
43
+ - lib/dommy/rails/match_target.rb
44
+ - lib/dommy/rails/minitest.rb
45
+ - lib/dommy/rails/minitest/assertions.rb
46
+ - lib/dommy/rails/minitest/integration.rb
47
+ - lib/dommy/rails/page_inspector.rb
48
+ - lib/dommy/rails/rspec.rb
49
+ - lib/dommy/rails/rspec/browser.rb
50
+ - lib/dommy/rails/rspec/integration.rb
51
+ - lib/dommy/rails/rspec/matchers.rb
52
+ - lib/dommy/rails/stimulus.rb
53
+ - lib/dommy/rails/turbo_stream.rb
54
+ - lib/dommy/rails/url_matcher.rb
55
+ - lib/dommy/rails/url_normalizer.rb
56
+ - lib/dommy/rails/version.rb
57
+ homepage: https://github.com/takahashim/dommy
58
+ licenses:
59
+ - MIT
60
+ metadata: {}
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.6.9
76
+ specification_version: 4
77
+ summary: Rails integration for Dommy DOM testing
78
+ test_files: []