Module: Dommy::Rails::AriaSnapshotMatching
- Defined in:
- lib/dommy/rails/aria_snapshot_matching.rb
Overview
Compares a Playwright-compatible ARIA snapshot against an expected template using Playwright’s ‘toMatchAriaSnapshot` semantics: a SUBSET match. Every node listed in `expected` must appear in `actual`, in order, at the same nesting — but `actual` may contain extra nodes. A node matches when role + (optional) name + (subset of) flags agree. An expected name written as `/pattern/` is matched as a regular expression; an omitted name is a wildcard.
Defined Under Namespace
Classes: Node
Class Method Summary collapse
-
.children_match?(expected_children, actual_children) ⇒ Boolean
Greedy ordered-subsequence match: each expected child must match a later actual child, allowing extra actual children in between.
- .matches?(actual_text, expected_text) ⇒ Boolean
- .name_match?(expected, actual) ⇒ Boolean
-
.node_match?(expected, actual) ⇒ Boolean
— comparison —.
-
.parse(text) ⇒ Object
— parsing (indentation outline -> Node tree) —.
- .parse_line(body) ⇒ Object
- .scan_name(rest) ⇒ Object
- .unescape(text) ⇒ Object
- .unquote(text) ⇒ Object
Class Method Details
.children_match?(expected_children, actual_children) ⇒ Boolean
Greedy ordered-subsequence match: each expected child must match a later actual child, allowing extra actual children in between.
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 40 def children_match?(expected_children, actual_children) cursor = 0 expected_children.each do |expected| cursor += 1 until cursor >= actual_children.size || node_match?(expected, actual_children[cursor]) return false if cursor >= actual_children.size cursor += 1 end true end |
.matches?(actual_text, expected_text) ⇒ Boolean
17 18 19 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 17 def matches?(actual_text, expected_text) children_match?(parse(expected_text).children, parse(actual_text).children) end |
.name_match?(expected, actual) ⇒ Boolean
31 32 33 34 35 36 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 31 def name_match?(expected, actual) return actual.name.to_s.match?(expected.name_regex) if expected.name_regex return true if expected.name.nil? expected.name == actual.name end |
.node_match?(expected, actual) ⇒ Boolean
— comparison —
23 24 25 26 27 28 29 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 23 def node_match?(expected, actual) return false unless expected.role == actual.role return false unless name_match?(expected, actual) return false unless (expected.flags - actual.flags).empty? children_match?(expected.children, actual.children) end |
.parse(text) ⇒ Object
— parsing (indentation outline -> Node tree) —
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 53 def parse(text) root = Node.new(role: nil, flags: [], children: []) stack = [[-1, root]] text.to_s.each_line do |line| stripped = line.strip next if stripped.empty? || !stripped.start_with?("- ") indent = line[/\A */].length node = parse_line(stripped[2..]) stack.pop while stack.size > 1 && stack.last[0] >= indent stack.last[1].children << node stack.push([indent, node]) end root end |
.parse_line(body) ⇒ Object
69 70 71 72 73 74 75 76 77 78 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 69 def parse_line(body) body = body.sub(/:\s*\z/, "") return Node.new(role: "text", name: unquote(body.sub(/\Atext:\s*/, "")), flags: [], children: []) \ if body.start_with?("text:") role, rest = body.split(/\s+/, 2) name, name_regex, rest = scan_name(rest) flags = rest.to_s.scan(/\[([^\]]*)\]/).flatten Node.new(role: role, name: name, name_regex: name_regex, flags: flags, children: []) end |
.scan_name(rest) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 80 def scan_name(rest) return [nil, nil, rest] if rest.nil? if rest.start_with?('"') && (md = rest.match(/\A"((?:\\.|[^"\\])*)"/)) [unescape(md[1]), nil, rest[md.end(0)..].to_s.strip] elsif rest.start_with?("/") && (md = rest.match(%r{\A/((?:\\.|[^/\\])*)/})) [nil, Regexp.new(md[1]), rest[md.end(0)..].to_s.strip] else [nil, nil, rest] end end |
.unescape(text) ⇒ Object
97 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 97 def unescape(text) = text.gsub(/\\(.)/, '\1') |
.unquote(text) ⇒ Object
92 93 94 95 |
# File 'lib/dommy/rails/aria_snapshot_matching.rb', line 92 def unquote(text) md = text.to_s.strip.match(/\A"((?:\\.|[^"\\])*)"\z/) md ? unescape(md[1]) : text.to_s.strip end |