capybara-experience 0.3.4 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84a97377aa40b425565277472cb8508fd305f9288b77876f40e760291b623eda
4
- data.tar.gz: c47a957680c97755d48f47df1430b111e920bf79a06e1e64ba0e9e53a4260b41
3
+ metadata.gz: a53a92480b4d79fbf507661277b1c0cfa31379910365cf770ee299875141d68a
4
+ data.tar.gz: d782f04061591b1923d046fd0300a470751fa2a58b273a079b5b93dbcb9b5b33
5
5
  SHA512:
6
- metadata.gz: b5b283908c6c0076883a8bad1e6dab873cb643e5109fb071f539a4cc12fe3844f52e4e92a5c4b96b0739ff00989e6abdb170725045f850ac22ab0de223c4ee6b
7
- data.tar.gz: d544901d7cd5b30600b37f8ebfce1e074e028c805b776a4813a034f71ef4f79193e52a3b42d63d74ea64cf3f16c2fe2ba3875c9f592bb585ebbbcbe1b67251ce
6
+ metadata.gz: cd3a3f5bd81acb16ec0e449f04bb8ea0d71c66d15d382216a927823d145bd1fa8f39d55d6f868250ef65b42f32451a158e5f6c9898225ad554a7f1fd3907f610
7
+ data.tar.gz: 7ada0747083edb9c37edf820045f963f24dc39b5b769d6e5a79808baeadadcf91d4023564632e29ad72fc3d91850cb818ed60cdf1b1ec234660b37bf627f7893
data/CLAUDE.md ADDED
@@ -0,0 +1,29 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What This Is
6
+
7
+ capybara-experience is a Ruby gem that extends Capybara with session pooling, multi-user experience objects, and a behavior DSL for feature tests. It lets tests instantiate multiple `Capybara::Experience` objects that each get their own browser session from a shared pool, enabling tests that compare different user journeys side by side.
8
+
9
+ ## Commands
10
+
11
+ - **Run all tests:** `bundle exec rake` or `bundle exec rspec`
12
+ - **Run a single test:** `bundle exec rspec spec/capybara/experience_spec.rb`
13
+ - **Install deps:** `bundle install`
14
+ - **Build gem:** `bundle exec rake build`
15
+ - **Release gem:** `bundle exec rake release`
16
+
17
+ ## Architecture
18
+
19
+ Three core components, all under `Capybara::Experience`:
20
+
21
+ **`Experience`** (`lib/capybara/experience.rb`) — Includes `Capybara::DSL`. Each instance lazily acquires a session from the Pool via `page`. The `UnifySessionPool` module is prepended onto `Capybara`'s singleton class to route Capybara's global `session_pool` through `Experience::Pool`, unifying session management. Integrates with `capybara-screenshot` by setting `final_session_name`.
22
+
23
+ **`Pool`** (`lib/capybara/experience/pool.rb`) — Singleton that extends `Hash`. Manages session lifecycle: `take` returns an idle session matching the driver+app or creates a new one; `reset_idle!` marks all sessions as reusable. Sessions are keyed as `"driver:object_id:app_object_id"`. The `[]` override auto-creates sessions on cache miss, which is how Capybara's `session_pool` integration works.
24
+
25
+ **`BehaviorDSL`** (`lib/capybara/experience/rspec.rb`) — RSpec helper that provides `behavior` blocks for grouping steps within a single `it` block. Dynamically updates RSpec metadata so the test runner shows the current behavior name. Also registers an `after(:each)` hook on `type: :feature, js: true` tests to call `Pool#reset_idle!`.
26
+
27
+ ## Test Setup
28
+
29
+ Tests use Capybara's built-in `TestApp` (Sinatra) as the rack app. A custom `:javascript_test` driver is registered as a RackTest driver to test driver-switching logic without requiring a real browser. The single feature spec covers pool allocation, session reuse across reset cycles, and driver switching.
data/README.md CHANGED
@@ -29,9 +29,51 @@ Or install it yourself as:
29
29
 
30
30
  ## Usage
31
31
 
32
- ### Basic
32
+ ### Setup
33
+
34
+ #### RSpec
35
+ ```ruby
36
+ # spec/spec_helper.rb
37
+ require "capybara/experience"
38
+ require "capybara/experience/rspec"
39
+ ```
40
+
41
+ This automatically includes `Capybara::Experience::DSL` and `BehaviorDSL` in feature specs.
42
+
43
+ #### Minitest
44
+ ```ruby
45
+ # test/test_helper.rb
46
+ require "capybara/experience"
47
+ require "capybara/experience/minitest"
48
+ ```
49
+
50
+ Include the modules in your test class:
51
+ ```ruby
52
+ class ApplicationIntegrationTest < Minitest::Test
53
+ include Capybara::DSL
54
+ include Capybara::Minitest::Assertions
55
+ include Capybara::Experience::MinitestHooks # includes DSL and resets pool after each test
56
+ end
57
+ ```
58
+
59
+ ### Test Context & DSL
60
+
61
+ Experience instances accept a test context as their first argument, enabling assertions to be called from within Experience subclasses. When you subclass `Capybara::Experience`, a factory method is automatically added to `Capybara::Experience::DSL` that passes `self` as the test context:
62
+
63
+ ```ruby
64
+ class UserExperience < Capybara::Experience
65
+ # creates UserExperience() factory method on Capybara::Experience::DSL
66
+ end
67
+
68
+ # In a test:
69
+ ux = UserExperience() # equivalent to UserExperience.new(self)
70
+ ```
71
+
72
+ ### Basic Example
73
+
33
74
  This scenario is for your standard user/admin.
34
- 1. We need to create a file for each experience
75
+
76
+ 1. Create a file for each experience:
35
77
 
36
78
  ```ruby
37
79
  # spec/support/experiences/user_experience.rb
@@ -42,7 +84,8 @@ class UserExperience < Capybara::Experience
42
84
  @user = user
43
85
  login_as user, scope: :user
44
86
  visit '/'
45
- assert_text "#{@user.name} Welcome!"
87
+ # assertions work inside Experience when test context is provided
88
+ expect(page).to have_content("#{@user.name} Welcome!")
46
89
  self
47
90
  end
48
91
 
@@ -53,17 +96,13 @@ end
53
96
  ```
54
97
 
55
98
  ```ruby
56
- # spec/support/experiences/user_experience.rb
99
+ # spec/support/experiences/admin_experience.rb
57
100
  class AdminExperience < Capybara::Experience
58
- def initialize(*args)
59
- super
60
- end
61
-
62
101
  def login(user)
63
102
  @user = user
64
103
  login_as user, scope: :admin
65
104
  visit '/admin/login'
66
- assert_text "Home"
105
+ expect(page).to have_content("Home")
67
106
  end
68
107
 
69
108
  private
@@ -72,7 +111,7 @@ class AdminExperience < Capybara::Experience
72
111
  end
73
112
  ```
74
113
 
75
- 2. now we need to create some capabilities. These capability files associate to each page, imagine each capability is an api to a page.
114
+ 2. Create some capabilities. These capability files associate to each page, imagine each capability is an api to a page.
76
115
 
77
116
  ```ruby
78
117
  # spec/support/capabilities/sign_up.rb
@@ -86,21 +125,19 @@ module Capabilities::SignUp
86
125
  fill_in "password", with: password
87
126
  fill_in "password_confirmation", with: password
88
127
  click_button "Submit"
89
- assert_text "Welcome #{email}"
128
+ expect(page).to have_content("Welcome #{email}")
90
129
  end
91
130
  end
92
131
  ```
93
132
 
94
- 3. write the spec
133
+ 3. Write the spec using the DSL factory methods:
134
+
95
135
  ```ruby
96
136
  # spec/features/sign_up_flow_spec.rb
97
- RSpec.describe "sign up flow", feature: true do
98
- let(:guest_experience) { GuestExperience.new.extend(Capabilities::SignUp) }
99
- let(:admin_experience) { AdminExperience.new.extend(Capabilities::Admin::ManageUser) }
100
-
137
+ RSpec.describe "sign up flow", type: :feature do
101
138
  it "works" do
102
- behavior "user can sign up" do # behaviors are an added DSL by capybara-experiences to group interactions & assertions
103
- guest_ux = GuestExperience.new
139
+ behavior "user can sign up" do
140
+ guest_ux = GuestExperience() # factory method passes self as test context
104
141
  guest_ux.navigate_to_sign_up
105
142
 
106
143
  guest_ux.sign_up(
@@ -112,9 +149,9 @@ RSpec.describe "sign up flow", feature: true do
112
149
  end
113
150
 
114
151
  behavior "admin can see user" do
115
- admin_ux = AdminExperience.new
152
+ admin_ux = AdminExperience()
116
153
  admin_ux.login
117
- admin_ux.to have_content "user@example.com"
154
+ expect(admin_ux).to have_content "user@example.com"
118
155
  end
119
156
  end
120
157
  end
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "rake", "~> 10.0"
30
30
  spec.add_development_dependency "rspec", "~> 3.0"
31
31
  spec.add_development_dependency "sinatra", ">= 3.0.2"
32
+ spec.add_development_dependency "minitest"
32
33
 
33
34
  spec.add_runtime_dependency "capybara", "~> 3.0"
34
35
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ require "capybara/minitest"
3
+
4
+ module Capybara
5
+ class Experience
6
+ module MinitestContext
7
+ Capybara::Minitest::Assertions.instance_methods(false).each do |method_name|
8
+ define_method(method_name) do |*args, **kwargs, &block|
9
+ test_context.send(method_name, page, *args, **kwargs, &block)
10
+ end
11
+ end
12
+ end
13
+
14
+ module MinitestBehaviorDSL
15
+ def behavior(name)
16
+ @_behavior_stack ||= []
17
+ @_behavior_stack.push(name)
18
+ yield
19
+ ensure
20
+ @_behavior_stack.pop
21
+ end
22
+
23
+ def current_behavior
24
+ @_behavior_stack ||= []
25
+ @_behavior_stack.join(" ")
26
+ end
27
+ end
28
+
29
+ module MinitestHooks
30
+ include Capybara::Experience::DSL
31
+ include Capybara::Experience::MinitestBehaviorDSL
32
+
33
+ def after_teardown
34
+ Capybara::Experience::Pool.instance.reset_idle!
35
+ super
36
+ end
37
+ end
38
+
39
+ include MinitestContext
40
+ end
41
+ end
@@ -27,6 +27,8 @@ module Capybara
27
27
  session
28
28
  end
29
29
 
30
+ attr_accessor :last_session
31
+
30
32
  def idle
31
33
  @idle ||= []
32
34
  end
@@ -41,6 +43,7 @@ module Capybara
41
43
  end
42
44
  replace(new_hash)
43
45
  @idle = values
46
+ @last_session = nil
44
47
 
45
48
  nil
46
49
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "capybara/experience"
4
+
5
+ if defined?(RSpec)
6
+ require "capybara/experience/rspec"
7
+ elsif defined?(Minitest)
8
+ require "capybara/experience/minitest"
9
+ end
10
+
11
+ module Capybara
12
+ class Experience
13
+ module ScreenshotOverride
14
+ def page
15
+ @_capybara_experience_page_override || super
16
+ end
17
+
18
+ private
19
+
20
+ def take_failed_screenshot
21
+ last_session = Experience::Pool.instance.last_session
22
+ return super unless last_session
23
+
24
+ @_capybara_experience_page_override = last_session
25
+ super
26
+ ensure
27
+ @_capybara_experience_page_override = nil
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ if defined?(ActiveSupport)
34
+ ActiveSupport.on_load(:action_dispatch_system_test_case) do
35
+ ActionDispatch::SystemTestCase.prepend(Capybara::Experience::ScreenshotOverride)
36
+ end
37
+ end
@@ -29,8 +29,23 @@ module Capybara
29
29
  end
30
30
  end
31
31
 
32
+ module RSpecContext
33
+ def expect(...)
34
+ test_context.expect(...)
35
+ end
36
+
37
+ Capybara::RSpecMatchers.instance_methods(false).each do |method_name|
38
+ define_method(method_name) do |*args, **kwargs, &block|
39
+ test_context.send(method_name, *args, **kwargs, &block)
40
+ end
41
+ end
42
+ end
43
+
44
+ include RSpecContext
45
+
32
46
  RSpec.configure do |config|
33
47
  config.include BehaviorDSL
48
+ config.include Capybara::Experience::DSL, type: :feature
34
49
  config.after :each, type: :feature, js: true do
35
50
  Capybara::Experience::Pool.instance.reset_idle!
36
51
  end
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  class Experience
3
- VERSION = "0.3.4"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -12,10 +12,13 @@ module Capybara
12
12
  end
13
13
  end
14
14
 
15
- def initialize(driver_name: nil)
15
+ def initialize(test_context = nil, driver_name: nil)
16
+ @test_context = test_context
16
17
  @driver_name = driver_name
17
18
  end
18
19
 
20
+ attr_reader :test_context
21
+
19
22
  def reload_page
20
23
  visit current_url
21
24
  end
@@ -31,6 +34,7 @@ module Capybara
31
34
  def page
32
35
  @page ||= Experience::Pool.instance.take(driver: driver_name)
33
36
 
37
+ Experience::Pool.instance.last_session = @page
34
38
  Capybara::Screenshot.final_session_name = @page.object_id if defined?(Capybara::Screenshot)
35
39
 
36
40
  @page
@@ -42,13 +46,27 @@ module Capybara
42
46
  end
43
47
  end
44
48
 
49
+ module DSL
50
+ end
51
+
52
+ def self.inherited(subclass)
53
+ super
54
+ return unless subclass.name
55
+
56
+ method_name = subclass.name.split("::").last
57
+ subclass_ref = subclass
58
+ DSL.define_method(method_name) do |**kwargs|
59
+ subclass_ref.new(self, **kwargs)
60
+ end
61
+ end
62
+
45
63
  class Error < StandardError; end
46
64
 
47
65
  module UnifySessionPool
48
66
  def page
49
- Capybara::Screenshot.final_session_name = Capybara.session_name if defined?(Capybara::Screenshot)
50
-
51
- super
67
+ Experience::Pool.instance.last_session = super.tap do
68
+ Capybara::Screenshot.final_session_name = Capybara.session_name if defined?(Capybara::Screenshot)
69
+ end
52
70
  end
53
71
 
54
72
  private
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-experience
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Ong
8
8
  - Britt Lewis
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2022-10-12 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
@@ -67,6 +66,20 @@ dependencies:
67
66
  - - ">="
68
67
  - !ruby/object:Gem::Version
69
68
  version: 3.0.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
70
83
  - !ruby/object:Gem::Dependency
71
84
  name: capybara
72
85
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +106,7 @@ files:
93
106
  - ".gitignore"
94
107
  - ".rspec"
95
108
  - ".travis.yml"
109
+ - CLAUDE.md
96
110
  - Gemfile
97
111
  - README.md
98
112
  - Rakefile
@@ -101,7 +115,9 @@ files:
101
115
  - capybara-experience.gemspec
102
116
  - lib/capybara-experience.rb
103
117
  - lib/capybara/experience.rb
118
+ - lib/capybara/experience/minitest.rb
104
119
  - lib/capybara/experience/pool.rb
120
+ - lib/capybara/experience/rails.rb
105
121
  - lib/capybara/experience/rspec.rb
106
122
  - lib/capybara/experience/version.rb
107
123
  homepage: https://github.com/ryanong/capybara-experience
@@ -109,7 +125,6 @@ licenses: []
109
125
  metadata:
110
126
  homepage_uri: https://github.com/ryanong/capybara-experience
111
127
  source_code_uri: https://github.com/ryanong/capybara-experience
112
- post_install_message:
113
128
  rdoc_options: []
114
129
  require_paths:
115
130
  - lib
@@ -124,8 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
139
  - !ruby/object:Gem::Version
125
140
  version: '0'
126
141
  requirements: []
127
- rubygems_version: 3.3.17
128
- signing_key:
142
+ rubygems_version: 4.0.6
129
143
  specification_version: 4
130
144
  summary: Simplify Capybara testing for larger applications
131
145
  test_files: []