Module: Capybara::Storyboard

Defined in:
lib/capybara/storyboard.rb,
lib/capybara/storyboard/context.rb,
lib/capybara/storyboard/session.rb,
lib/capybara/storyboard/version.rb,
lib/capybara/storyboard/test_helper.rb,
lib/capybara/storyboard/configuration.rb,
lib/capybara/storyboard/page_stability.rb,
lib/capybara/storyboard/policies/env_policy.rb,
lib/capybara/storyboard/policies/target_list_policy.rb,
sig/capybara/storyboard.rbs

Overview

Automatic Capybara screenshots for RSpec system specs, gated by a replaceable policy (see Capybara::Storyboard.policy).

Defined Under Namespace

Modules: PageStability, Policies, TestHelper Classes: Configuration, Context, Error, Session

Constant Summary collapse

VERSION =

Returns:

  • (String)
'0.1.0'

Class Method Summary collapse

Class Method Details

.clear_output!Object

Empties the output root once per process so a run's screenshots never mix with stale files left by a previous run. Meant to be wired into a before(:suite) hook by the host app (see the README); not called automatically, so nothing is registered on RSpec just by requiring the gem.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/capybara/storyboard.rb', line 44

def clear_output!
  # Run-once guard: idempotent even if the before(:suite) hook fires more
  # than once (e.g. one hook per RSpec process under parallel_tests).
  return if @output_cleared

  # Arm gate: same bare-SCREENSHOTS probe as default_policy's own gate
  # (EnvPolicy ignores its argument, so call(nil) is context-free). We
  # deliberately do NOT use configuration.policy here: a custom policy
  # assumes a per-example Context and may read the target list (raising
  # when SCREENSHOT_TESTS_FILE is bad). A suite-wide clear must key off
  # the global arm state alone, never per-example data.
  return unless Policies::EnvPolicy.new.call(nil)

  # Honor the run-once contract BEFORE touching the filesystem: if the
  # delete fails, we must not keep retrying (and re-deleting) on later
  # registrations of the same hook.
  @output_cleared = true

  # Resolve the root against the shared base so a relative output_dir
  # (kept relative by Configuration) maps to a single absolute target.
  root = configuration.output_dir.expand_path(rails_root_or_pwd)
  return unless safe_to_clear?(root)
  # First run / nothing captured yet: silently skip. Session#ensure_dir!
  # lazily recreates the tree on the first capture, so a missing root is
  # not an error.
  return unless root.exist?

  FileUtils.rm_rf(root)
end

.configurationObject

The single Configuration holding user overrides (output root, policy).



16
17
18
# File 'lib/capybara/storyboard.rb', line 16

def configuration
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields the Configuration for block-style setup:

Capybara::Storyboard.configure { |config| config.output_dir = ... }

Yields:



22
23
24
# File 'lib/capybara/storyboard.rb', line 22

def configure
  yield(configuration)
end

.default_policyObject

The single place that builds the default policy. Internal API, public only so Configuration#policy can call it without send; use policy.

When SCREENSHOTS is unset the mechanism is disarmed: return EnvPolicy alone WITHOUT touching the target list. This honors the "disabled -> nothing happens" contract even when SCREENSHOT_TESTS_FILE points at a missing file (its existence check must not blow up a disarmed suite).

When armed: no target list configured (neither ENV set) -> EnvPolicy alone, preserving the gist's "SCREENSHOTS=1 captures everything". A target list configured (even an empty file) -> EnvPolicy AND TargetListPolicy; an empty set then means "explicitly zero targets".



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/capybara/storyboard.rb', line 127

def default_policy
  env = Policies::EnvPolicy.new
  # env.call(nil) probes the bare SCREENSHOTS switch (same gate
  # clear_output! uses), inlined here so we can reuse this exact `env`
  # instance as the return value below instead of building a second one.
  # Disarmed -> skip reading/validating the target list entirely.
  return env unless env.call(nil)

  raw_targets = raw_target_list
  return env if raw_targets.nil?

  # Drop blank entries BEFORE normalizing: an empty string normalizes to
  # "." (the base dir), which is not blank and would leak into the set.
  targets = raw_targets.map { |path| path.to_s.strip }.compact_blank.map { |path| normalize_test_path(path) }
  target = Policies::TargetListPolicy.new(targets)

  # Composition style: a plain lambda. A proc responds to #call, so it
  # satisfies the #call(context) -> Boolean policy contract, and it can
  # still be replaced wholesale via #policy=.
  ->(context) { env.call(context) && target.call(context) }
end

.normalize_test_path(path) ⇒ Object

Normalizes a test file path to a base-relative string so that target list entries and Context#test_file compare equal regardless of leading ./, absolute vs relative form, or surrounding whitespace/newlines. Shared by default_policy (list side) and TargetListPolicy#call (context side) so both sides always agree on the canonical form.



96
97
98
99
100
101
102
# File 'lib/capybara/storyboard.rb', line 96

def normalize_test_path(path)
  base = rails_root_or_pwd
  Pathname(path.to_s.strip)
    .expand_path(base)
    .relative_path_from(base)
    .to_s
end

.policyObject

An object responding to #call(context) -> Boolean. Assign nil (or call reset_policy!) to restore the default. Delegated to the Configuration so there is a single source of truth.



77
78
79
# File 'lib/capybara/storyboard.rb', line 77

def policy
  configuration.policy
end

.policy=(value) ⇒ Object



81
82
83
# File 'lib/capybara/storyboard.rb', line 81

def policy=(value)
  configuration.policy = value
end

.rails_root_or_pwdObject

The single place that resolves the base directory: Rails.root when available (the gem stays Rails-optional), else the current directory. Session#default_output_root joins its own subpath onto this.



107
108
109
110
111
112
113
# File 'lib/capybara/storyboard.rb', line 107

def rails_root_or_pwd
  if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
    Rails.root
  else
    Pathname(Dir.pwd)
  end
end

.reset_configuration!Object

Drops every override (output_dir and policy) in one shot, restoring the gem's defaults. Handy in after hooks so a customized run never leaks into later examples.



29
30
31
# File 'lib/capybara/storyboard.rb', line 29

def reset_configuration!
  @configuration = nil
end

.reset_output_cleared!Object

Test-hygiene helper mirroring reset_configuration! / reset_policy!: clears the run-once flag so clear_output! can be exercised again within the same process (a real run never needs this).



36
37
38
# File 'lib/capybara/storyboard.rb', line 36

def reset_output_cleared!
  @output_cleared = nil
end

.reset_policy!Object

Equivalent to self.policy = nil; named for readable after hooks that prevent a custom policy from leaking into later examples.



87
88
89
# File 'lib/capybara/storyboard.rb', line 87

def reset_policy!
  configuration.reset_policy!
end