Module: Browserctl::Replay::SnapshotDiff

Defined in:
lib/browserctl/replay/snapshot_diff.rb

Overview

Stable digest + element-set comparison for post-step snapshots.

The digest is intentionally cheap and stable across cosmetic DOM noise: only the (selector, role, tag) triples drive the hash, sorted to remove ordering effects. That’s enough to flag structural drift (a step that used to land on /dashboard now lands on /login) without flapping on every reflow or class rename.

Class Method Summary collapse

Class Method Details

.compare(prev, current) ⇒ Object

Returns { added: […], removed: […] } of element selectors that differ between two snapshots. Empty arrays mean structurally identical.



26
27
28
29
30
31
32
33
# File 'lib/browserctl/replay/snapshot_diff.rb', line 26

def compare(prev, current)
  prev_set    = element_set(prev)
  current_set = element_set(current)
  {
    added: (current_set - prev_set).sort,
    removed: (prev_set - current_set).sort
  }
end

.digest(snapshot) ⇒ Object



17
18
19
20
21
22
# File 'lib/browserctl/replay/snapshot_diff.rb', line 17

def digest(snapshot)
  return nil if snapshot.nil?

  keys = Array(snapshot).map { |el| identity_tuple(el) }.compact.sort
  Digest::SHA1.hexdigest(keys.join("\n"))[0, 16]
end

.element_set(snapshot) ⇒ Object



46
47
48
# File 'lib/browserctl/replay/snapshot_diff.rb', line 46

def element_set(snapshot)
  Array(snapshot).map { |entry| entry[:selector] || entry["selector"] }.compact
end

.identity_tuple(entry) ⇒ Object



35
36
37
38
39
40
41
42
43
44
# File 'lib/browserctl/replay/snapshot_diff.rb', line 35

def identity_tuple(entry)
  return nil unless entry.is_a?(Hash)

  sel = entry[:selector] || entry["selector"]
  role = entry[:role] || entry["role"]
  tag = entry[:tag] || entry["tag"]
  return nil unless sel

  "#{sel}|#{role}|#{tag}"
end