capybara-presenter 0.1.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 +7 -0
- data/.rubocop.yml +31 -0
- data/CHANGELOG.md +45 -0
- data/CLAUDE.md +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +157 -0
- data/Rakefile +83 -0
- data/lefthook.yml +8 -0
- data/lib/capybara/presenter/capybara_extensions.rb +76 -0
- data/lib/capybara/presenter/configuration.rb +70 -0
- data/lib/capybara/presenter/notifications.rb +176 -0
- data/lib/capybara/presenter/version.rb +7 -0
- data/lib/capybara/presenter.rb +100 -0
- data/sig/capybara/demo.rbs +70 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 641d4e0c4f6f27c908eff20417912e7e98e8f610d95c0f448e0709fab2f93349
|
4
|
+
data.tar.gz: 8ed8c25c782cfdb7498a439a00ff88e3e0b402a395b54b38549b5bd600bf672f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b08f1f0be9dc7ac77b0b7773fbe730ab77308314177dba62e38b6ac1edf88b4c38bb54d936d9c90e0135ece22bfc4beecc547566dd9c1d648936d0e84c654ad
|
7
|
+
data.tar.gz: ac9b3f4e0d5a026faad59fc7f2528f55343860ed26f140a8660838fd680600e3de462341c28ac01618435a2970ba2847c8f8a2b4c6a0e2459c0c387910183c47
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.2
|
3
|
+
SuggestExtensions: false
|
4
|
+
NewCops: enable
|
5
|
+
|
6
|
+
plugins:
|
7
|
+
- rubocop-minitest
|
8
|
+
|
9
|
+
# Exclude test directories from metrics
|
10
|
+
Metrics:
|
11
|
+
Exclude:
|
12
|
+
- 'test/**/*'
|
13
|
+
- 'examples/**/*'
|
14
|
+
|
15
|
+
# Disable problematic cops
|
16
|
+
Lint/MissingCopEnableDirective:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Lint/DuplicateBranch:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Naming/PredicateName:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Gemspec/DevelopmentDependencies:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
# Allow multiple assertions in system tests
|
29
|
+
Minitest/MultipleAssertions:
|
30
|
+
Exclude:
|
31
|
+
- 'examples/**/*'
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Input sanitization for JavaScript injection prevention in notifications
|
12
|
+
- Environment variable validation with proper error handling and warnings
|
13
|
+
- Comprehensive development infrastructure (CI, RuboCop, pre-commit hooks)
|
14
|
+
- Support for presenter mode with visual notifications and delays
|
15
|
+
- Automatic delays between Capybara actions for better presentations
|
16
|
+
- Browser notifications for test milestones and progress
|
17
|
+
- Configurable notification positions (top, center, bottom)
|
18
|
+
- Sample application demonstrating gem functionality
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Renamed gem from `capybara-demo` to `capybara-presenter` for clarity
|
22
|
+
- Renamed API methods from `demo_*` to `presenter_*` for consistency
|
23
|
+
- Renamed environment variables from `DEMO_*` to `PRESENTER_*`
|
24
|
+
- Updated all documentation and examples to use presenter terminology
|
25
|
+
- **BREAKING**: Raised minimum Ruby version from 3.1.0 to 3.2.0 for selenium-webdriver compatibility
|
26
|
+
|
27
|
+
### Security
|
28
|
+
- Added proper input sanitization to prevent XSS attacks in notifications
|
29
|
+
- Implemented safe HTML and JavaScript escaping for user content
|
30
|
+
|
31
|
+
### Development
|
32
|
+
- Added GitHub Actions CI for Ruby 3.1, 3.2, and 3.3
|
33
|
+
- Configured RuboCop with minimal overrides for code quality
|
34
|
+
- Set up Lefthook for pre-commit hooks (RuboCop and tests)
|
35
|
+
- Removed unused RSpec dependency in favor of Minitest
|
36
|
+
- Added proper .gitignore for generated files
|
37
|
+
|
38
|
+
## [0.1.0] - 2025-05-27
|
39
|
+
|
40
|
+
### Added
|
41
|
+
- Initial release of Capybara::Presenter gem
|
42
|
+
- Basic presenter mode functionality
|
43
|
+
- Configuration system with environment variable support
|
44
|
+
- Integration with Capybara test framework
|
45
|
+
- Documentation and examples
|
data/CLAUDE.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# CLAUDE.md
|
2
|
+
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
4
|
+
|
5
|
+
## Common Development Commands
|
6
|
+
|
7
|
+
### Testing
|
8
|
+
- `rake test` - Run gem unit tests
|
9
|
+
- `rake presenter:test` - Run sample app system tests
|
10
|
+
- `bundle exec ruby test/test_capybara_demo.rb` - Run specific test file
|
11
|
+
|
12
|
+
### Development Workflow
|
13
|
+
- `bin/console` - Start IRB console with gem loaded
|
14
|
+
- `bundle exec rubocop` - Run linting (enforced via pre-commit hooks)
|
15
|
+
- `bundle exec rake install` - Install gem locally for testing
|
16
|
+
- `bundle exec rake build` - Build gem package
|
17
|
+
|
18
|
+
### Demo/Presentation Commands
|
19
|
+
- `rake presenter:run` - Run live demo with browser automation
|
20
|
+
- `rake presenter:browser` - Run browser-only demo
|
21
|
+
- `rake presenter:help` - Show all available presenter options
|
22
|
+
|
23
|
+
## Architecture Overview
|
24
|
+
|
25
|
+
**Capybara::Presenter** is a Ruby gem that transforms standard Capybara system tests into presentation-ready demos. The gem operates by:
|
26
|
+
|
27
|
+
1. **Environment-based activation**: Only active when `PRESENTER_MODE=true`
|
28
|
+
2. **Zero overhead when disabled**: No performance impact in regular test runs
|
29
|
+
3. **Visual feedback**: Displays notifications in browser during test execution
|
30
|
+
4. **Configurable timing**: Adds delays between actions for recording-friendly pacing
|
31
|
+
|
32
|
+
### Core Components
|
33
|
+
|
34
|
+
- **Main module** (`lib/capybara/presenter.rb`): Entry point and configuration setup
|
35
|
+
- **Configuration** (`lib/capybara/presenter/configuration.rb`): Global settings management
|
36
|
+
- **Delays** (`lib/capybara/presenter/delays.rb`): Timing control between actions
|
37
|
+
- **Notifications** (`lib/capybara/presenter/notifications.rb`): Browser notification system
|
38
|
+
- **Capybara Extensions** (`lib/capybara/presenter/capybara_extensions.rb`): Monkey-patches Capybara methods
|
39
|
+
|
40
|
+
### Key Patterns
|
41
|
+
|
42
|
+
- **Method wrapping**: Core functionality wraps existing Capybara methods without breaking existing tests
|
43
|
+
- **JavaScript injection**: Notifications are displayed via injected JavaScript in the browser
|
44
|
+
- **Configuration precedence**: Environment variables override global configuration
|
45
|
+
- **Sample app**: Complete working example in `examples/sample_app/` using Sinatra
|
46
|
+
|
47
|
+
### Setup Requirements
|
48
|
+
|
49
|
+
For full functionality, tests need:
|
50
|
+
1. **Parallel testing disabled**: `parallelize(workers: 1)` when PRESENTER_MODE=true
|
51
|
+
2. **Non-headless browser**: `driven_by :selenium, using: :chrome` for notifications
|
52
|
+
3. **Test start notifications**: Call `presenter_test_start_notification(self.class, @NAME)` in setup
|
53
|
+
4. **Automatic delays**: Call `setup_presenter_delays` in setup for action delays
|
54
|
+
|
55
|
+
### Development Notes
|
56
|
+
|
57
|
+
- **Ruby version**: Requires >= 3.2.0
|
58
|
+
- **Dependencies**: Capybara >= 3.0, Selenium >= 4.0
|
59
|
+
- **Testing**: Uses Minitest with comprehensive mocking for browser interactions
|
60
|
+
- **Code quality**: RuboCop enforced via Lefthook pre-commit hooks
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 José Coelho
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# Capybara::Presenter
|
2
|
+
|
3
|
+
Transform your Capybara system tests into a presentations with automatic delays and browser notifications. Perfect for creating test recordings and demos.
|
4
|
+
|
5
|
+
I had this in a project and it helps me give quick updates about the progress of a feature. I like to [communicate often](https://josecoelho.com/Software-Engineer/Communicate-often), the easier, the better. Decided to extract it to a gem with some help from Claude. I hope it's useful for you as well. :)
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'capybara-presenter', group: :test
|
15
|
+
```
|
16
|
+
|
17
|
+
Then run:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
bundle install
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Complete Setup
|
26
|
+
|
27
|
+
**1. Include the module in your test class:**
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'capybara/presenter'
|
31
|
+
|
32
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
33
|
+
include Capybara::Presenter
|
34
|
+
|
35
|
+
# Disable parallel testing in presenter mode for sequential execution
|
36
|
+
parallelize(workers: 1) if ENV["PRESENTER_MODE"] == "true"
|
37
|
+
|
38
|
+
# Configure browser driver for presenter mode
|
39
|
+
if ENV["PRESENTER_MODE"] == "true"
|
40
|
+
driven_by :selenium, using: :chrome, screen_size: [1920, 1080]
|
41
|
+
else
|
42
|
+
driven_by :selenium, using: :headless_chrome
|
43
|
+
end
|
44
|
+
|
45
|
+
# Add test start notifications and automatic delays
|
46
|
+
setup do
|
47
|
+
if presenter_mode?
|
48
|
+
presenter_test_start_notification(self.class, @NAME)
|
49
|
+
setup_presenter_delays
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
**2. For Minitest users, also add to your individual test classes:**
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class UsersSystemTest < ApplicationSystemTestCase
|
59
|
+
# Test methods automatically get start notifications
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
**3. For RSpec users:**
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
RSpec.configure do |config|
|
67
|
+
config.include Capybara::Presenter, type: :system
|
68
|
+
|
69
|
+
config.before(:each, type: :system) do
|
70
|
+
if presenter_mode?
|
71
|
+
presenter_test_start_notification(self.class, example.description)
|
72
|
+
setup_presenter_delays
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
### Running Tests in Presenter Mode
|
79
|
+
|
80
|
+
```bash
|
81
|
+
# Enable presenter mode
|
82
|
+
PRESENTER_MODE=true bundle exec rails test:system
|
83
|
+
|
84
|
+
# Customize timing
|
85
|
+
PRESENTER_MODE=true PRESENTER_DELAY=1.5 bundle exec rails test:system
|
86
|
+
|
87
|
+
# Disable notifications
|
88
|
+
PRESENTER_MODE=true PRESENTER_NOTIFICATIONS=false bundle exec rails test:system
|
89
|
+
```
|
90
|
+
|
91
|
+
### In Your Tests
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
test "user registration" do
|
95
|
+
visit new_user_registration_path
|
96
|
+
|
97
|
+
fill_in "Email", with: "user@example.com"
|
98
|
+
fill_in "Password", with: "password123"
|
99
|
+
presenter_milestone("Form Complete", "All fields filled")
|
100
|
+
|
101
|
+
click_button "Sign up"
|
102
|
+
presenter_milestone("Success", "User registered successfully")
|
103
|
+
|
104
|
+
assert_text "Welcome!"
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
## API
|
109
|
+
|
110
|
+
- `presenter_mode?` - Check if presenter mode is enabled
|
111
|
+
- `presenter_delay(seconds)` - Add custom delay
|
112
|
+
- `presenter_notification(title, message)` - Show browser notification
|
113
|
+
- `presenter_milestone(title, message)` - Show milestone notification
|
114
|
+
|
115
|
+
## Configuration
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
Capybara::Presenter.configure do |config|
|
119
|
+
config.enabled = ENV['PRESENTER_MODE'] == 'true'
|
120
|
+
config.delay = 2.0
|
121
|
+
config.notifications = true
|
122
|
+
config.notification_position = :center # :top, :center, :bottom
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
## Environment Variables
|
127
|
+
|
128
|
+
- `PRESENTER_MODE=true` - Enable presenter mode
|
129
|
+
- `PRESENTER_DELAY=2.0` - Action delay in seconds
|
130
|
+
- `PRESENTER_TEST_START_DELAY=2.0` - Delay before each test
|
131
|
+
- `PRESENTER_NOTIFICATIONS=false` - Disable notifications
|
132
|
+
|
133
|
+
## Browser Setup
|
134
|
+
|
135
|
+
For notifications to appear, use a non-headless driver:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
if ENV["PRESENTER_MODE"] == "true"
|
139
|
+
driven_by :selenium, using: :chrome
|
140
|
+
else
|
141
|
+
driven_by :selenium, using: :headless_chrome
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
## Examples
|
146
|
+
|
147
|
+
```bash
|
148
|
+
# See the gem in action
|
149
|
+
rake presenter:run
|
150
|
+
|
151
|
+
# View all options
|
152
|
+
rake presenter:help
|
153
|
+
```
|
154
|
+
|
155
|
+
## License
|
156
|
+
|
157
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
# Main gem tests
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
8
|
+
t.libs << 'test'
|
9
|
+
t.libs << 'lib'
|
10
|
+
t.test_files = FileList['test/**/test_*.rb']
|
11
|
+
end
|
12
|
+
|
13
|
+
# Presenter and example tasks
|
14
|
+
# rubocop:disable Metrics/BlockLength
|
15
|
+
namespace :presenter do
|
16
|
+
desc 'Run the sample app system tests in normal mode'
|
17
|
+
task :test do
|
18
|
+
Dir.chdir('examples/sample_app') do
|
19
|
+
sh 'ruby system_test.rb'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Run the sample app system tests in presenter mode'
|
24
|
+
task :run do
|
25
|
+
Dir.chdir('examples/sample_app') do
|
26
|
+
sh 'PRESENTER_MODE=true ruby system_test.rb'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Run presenter with custom delays (DELAY=3.0 START_DELAY=2.0)'
|
31
|
+
task :slow do
|
32
|
+
delay = ENV['DELAY'] || '3.0'
|
33
|
+
start_delay = ENV['START_DELAY'] || '2.0'
|
34
|
+
Dir.chdir('examples/sample_app') do
|
35
|
+
sh "PRESENTER_MODE=true PRESENTER_DELAY=#{delay} PRESENTER_TEST_START_DELAY=#{start_delay} ruby system_test.rb"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Show all presenter options'
|
40
|
+
task :help do
|
41
|
+
puts <<~HELP
|
42
|
+
🎬 Capybara::Presenter - Available Rake Tasks
|
43
|
+
|
44
|
+
Main Tasks:
|
45
|
+
rake test - Run gem unit tests
|
46
|
+
rake presenter:test - Run sample app tests (normal mode)
|
47
|
+
rake presenter:run - Run sample app tests (presenter mode)
|
48
|
+
rake presenter:slow - Run presenter with slower timing
|
49
|
+
|
50
|
+
Custom Presenter Options:
|
51
|
+
rake presenter:slow DELAY=3.0 START_DELAY=2.0
|
52
|
+
#{' '}
|
53
|
+
Environment Variables:
|
54
|
+
PRESENTER_MODE=true - Enable presenter mode
|
55
|
+
PRESENTER_DELAY=2.0 - Action delay in seconds
|
56
|
+
PRESENTER_TEST_START_DELAY=2.0 - Test start delay in seconds
|
57
|
+
PRESENTER_NOTIFICATIONS=false - Disable notifications
|
58
|
+
#{' '}
|
59
|
+
Examples:
|
60
|
+
rake presenter:run # Basic presenter
|
61
|
+
PRESENTER_DELAY=1.0 rake presenter:run # Faster presenter
|
62
|
+
PRESENTER_NOTIFICATIONS=false rake presenter:run # No notifications
|
63
|
+
HELP
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# rubocop:enable Metrics/BlockLength
|
67
|
+
|
68
|
+
task default: :test
|
69
|
+
|
70
|
+
# Add help task at top level
|
71
|
+
desc 'Show all available tasks'
|
72
|
+
task :help do
|
73
|
+
puts <<~HELP
|
74
|
+
🎬 Capybara::Presenter Gem
|
75
|
+
|
76
|
+
Main Commands:
|
77
|
+
rake test - Run gem tests
|
78
|
+
rake presenter:help - Presenter options
|
79
|
+
#{' '}
|
80
|
+
Quick Start:
|
81
|
+
rake presenter:run - See the gem in action!
|
82
|
+
HELP
|
83
|
+
end
|
data/lefthook.yml
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Presenter
|
5
|
+
# Extends Capybara methods with presentation features.
|
6
|
+
# Wraps common Capybara actions to add delays and logging for better presentations.
|
7
|
+
module CapybaraExtensions
|
8
|
+
WRAPPED_METHODS = %i[visit click_button click_link click_on fill_in select choose check uncheck
|
9
|
+
attach_file].freeze
|
10
|
+
|
11
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
12
|
+
def setup_presenter_delays
|
13
|
+
return unless presenter_mode?
|
14
|
+
return if self.class.instance_variable_get(:@presenter_delays_setup)
|
15
|
+
|
16
|
+
self.class.instance_variable_set(:@presenter_delays_setup, true)
|
17
|
+
|
18
|
+
WRAPPED_METHODS.each do |action|
|
19
|
+
next if respond_to?(:"#{action}_without_presenter")
|
20
|
+
|
21
|
+
# Create alias for original method
|
22
|
+
self.class.alias_method :"#{action}_without_presenter", action
|
23
|
+
|
24
|
+
# Override the method with presenter delays
|
25
|
+
self.class.define_method(action) do |*args, **kwargs, &block|
|
26
|
+
if presenter_mode?
|
27
|
+
log_presenter_action(action, args.first)
|
28
|
+
|
29
|
+
# Pre-action delay
|
30
|
+
case action
|
31
|
+
when :click_button, :click_link, :click_on
|
32
|
+
presenter_delay(0.3)
|
33
|
+
when :fill_in, :select, :choose, :check, :uncheck
|
34
|
+
presenter_delay(0.2)
|
35
|
+
when :visit
|
36
|
+
presenter_delay(0.5)
|
37
|
+
when :attach_file
|
38
|
+
presenter_delay(0.3)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Execute the original action
|
43
|
+
result = send(:"#{action}_without_presenter", *args, **kwargs, &block)
|
44
|
+
|
45
|
+
# Post-action delay for actions that trigger page changes
|
46
|
+
presenter_delay(0.8) if presenter_mode? && %i[click_button click_link click_on visit].include?(action)
|
47
|
+
|
48
|
+
result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
57
|
+
def log_presenter_action(action, target)
|
58
|
+
return unless presenter_mode?
|
59
|
+
|
60
|
+
case action
|
61
|
+
when :click_button, :click_link, :click_on
|
62
|
+
puts "🖱️ Clicking: #{target}" if target
|
63
|
+
when :fill_in
|
64
|
+
puts "⌨️ Filling: #{target}" if target
|
65
|
+
when :select, :choose, :check, :uncheck
|
66
|
+
puts "☑️ Selecting: #{target}" if target
|
67
|
+
when :visit
|
68
|
+
puts "🌐 Navigating to: #{target}" if target
|
69
|
+
when :attach_file
|
70
|
+
puts "📎 Attaching file: #{target}" if target
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Presenter
|
5
|
+
# Configuration class for Capybara::Presenter settings.
|
6
|
+
# Manages global settings like delays, notifications, and environment variable parsing.
|
7
|
+
class Configuration
|
8
|
+
attr_reader :enabled, :delay, :notifications, :notification_position, :test_start_delay
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@enabled = ENV['PRESENTER_MODE'] == 'true'
|
12
|
+
@delay = parse_float_env('PRESENTER_DELAY', 2.0)
|
13
|
+
@notifications = ENV['PRESENTER_NOTIFICATIONS'] != 'false'
|
14
|
+
@notification_position = :center
|
15
|
+
@test_start_delay = parse_float_env('PRESENTER_TEST_START_DELAY', 2.0)
|
16
|
+
end
|
17
|
+
|
18
|
+
def enabled=(value)
|
19
|
+
@enabled = !!value
|
20
|
+
end
|
21
|
+
|
22
|
+
def delay=(value)
|
23
|
+
raise ArgumentError, 'delay must be a positive number' unless value.is_a?(Numeric) && value.positive?
|
24
|
+
|
25
|
+
@delay = value.to_f
|
26
|
+
end
|
27
|
+
|
28
|
+
def notifications=(value)
|
29
|
+
@notifications = !!value
|
30
|
+
end
|
31
|
+
|
32
|
+
def notification_position=(value)
|
33
|
+
valid_positions = %i[top center bottom]
|
34
|
+
unless valid_positions.include?(value)
|
35
|
+
raise ArgumentError, "notification_position must be one of: #{valid_positions.join(', ')}"
|
36
|
+
end
|
37
|
+
|
38
|
+
@notification_position = value
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_start_delay=(value)
|
42
|
+
raise ArgumentError, 'test_start_delay must be a positive number' unless value.is_a?(Numeric) && value >= 0
|
43
|
+
|
44
|
+
@test_start_delay = value.to_f
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# rubocop:disable Metrics/MethodLength
|
50
|
+
def parse_float_env(env_var, default)
|
51
|
+
value = ENV.fetch(env_var, nil)
|
52
|
+
return default unless value
|
53
|
+
|
54
|
+
begin
|
55
|
+
parsed = Float(value)
|
56
|
+
if parsed.negative?
|
57
|
+
warn "Warning: #{env_var}=#{value} is negative, using default #{default}"
|
58
|
+
default
|
59
|
+
else
|
60
|
+
parsed
|
61
|
+
end
|
62
|
+
rescue ArgumentError
|
63
|
+
warn "Warning: #{env_var}=#{value} is not a valid number, using default #{default}"
|
64
|
+
default
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# rubocop:enable Metrics/MethodLength
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Presenter
|
5
|
+
# Provides browser notification functionality for presenting test milestones.
|
6
|
+
# Injects JavaScript to display visual notifications during test execution.
|
7
|
+
# rubocop:disable Metrics/ModuleLength
|
8
|
+
module Notifications
|
9
|
+
def presenter_notification(title, message = nil, type: :info, duration: 4000)
|
10
|
+
return unless presenter_mode? && presenter_notifications_enabled?
|
11
|
+
|
12
|
+
show_browser_notification(title, message, type, duration)
|
13
|
+
end
|
14
|
+
|
15
|
+
def presenter_milestone(title, message = nil)
|
16
|
+
return unless presenter_mode?
|
17
|
+
|
18
|
+
presenter_notification("📋 #{title}", message, type: :success, duration: 3000)
|
19
|
+
presenter_delay(1.0)
|
20
|
+
end
|
21
|
+
|
22
|
+
def presenter_test_start_notification(test_class, test_method)
|
23
|
+
return unless presenter_mode? && presenter_notifications_enabled?
|
24
|
+
|
25
|
+
test_name = test_class.name.gsub(/Test$/, '')
|
26
|
+
method_name = test_method.gsub(/^test_/, '').gsub('_', ' ').split.map(&:capitalize).join(' ')
|
27
|
+
|
28
|
+
presenter_notification("🧪 #{test_name}", "Running: #{method_name}", type: :info, duration: 8000)
|
29
|
+
puts "📋 Starting test: #{test_name} - #{method_name}"
|
30
|
+
presenter_delay(Capybara::Presenter.configuration.test_start_delay)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def show_browser_notification(title, message, type, duration)
|
36
|
+
return unless has_browser_access?
|
37
|
+
|
38
|
+
begin
|
39
|
+
page.driver.browser.execute_script(notification_javascript(title, message, type, duration))
|
40
|
+
rescue StandardError => e
|
41
|
+
# Gracefully handle JavaScript errors
|
42
|
+
puts "Presenter notification failed: #{e.message}" if ENV['DEBUG']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def has_browser_access?
|
47
|
+
respond_to?(:page) &&
|
48
|
+
page.respond_to?(:driver) &&
|
49
|
+
page.driver.respond_to?(:browser)
|
50
|
+
end
|
51
|
+
|
52
|
+
# rubocop:disable Metrics/MethodLength
|
53
|
+
def notification_javascript(title, message, _type, duration)
|
54
|
+
# Sanitize inputs to prevent XSS
|
55
|
+
safe_title = sanitize_for_js(title)
|
56
|
+
safe_message = sanitize_for_js(message) if message
|
57
|
+
safe_title_html = sanitize_for_html(title)
|
58
|
+
safe_message_html = sanitize_for_html(message) if message
|
59
|
+
|
60
|
+
position = notification_position_css
|
61
|
+
<<~JS
|
62
|
+
console.log('Presenter notification: #{safe_title}');
|
63
|
+
|
64
|
+
// Remove existing presenter notifications
|
65
|
+
document.querySelectorAll('.presenter-notification').forEach(el => el.remove());
|
66
|
+
|
67
|
+
// Create notification container if it doesn't exist
|
68
|
+
let container = document.getElementById('presenter-notification-container');
|
69
|
+
if (!container) {
|
70
|
+
container = document.createElement('div');
|
71
|
+
container.id = 'presenter-notification-container';
|
72
|
+
container.style.cssText = `
|
73
|
+
position: fixed;
|
74
|
+
#{position}
|
75
|
+
z-index: 10000;
|
76
|
+
pointer-events: none;
|
77
|
+
display: flex;
|
78
|
+
flex-direction: column;
|
79
|
+
align-items: center;
|
80
|
+
gap: 12px;
|
81
|
+
`;
|
82
|
+
document.body.appendChild(container);
|
83
|
+
}
|
84
|
+
|
85
|
+
// Create notification element
|
86
|
+
const notification = document.createElement('div');
|
87
|
+
notification.className = 'presenter-notification';
|
88
|
+
|
89
|
+
notification.style.cssText = `
|
90
|
+
background: rgba(0, 0, 0, 0.5);
|
91
|
+
color: white;
|
92
|
+
padding: 32px 48px;
|
93
|
+
border-radius: 8px;
|
94
|
+
min-width: 700px;
|
95
|
+
max-width: 900px;
|
96
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
97
|
+
font-size: 22px;
|
98
|
+
font-weight: 600;
|
99
|
+
line-height: 1.3;
|
100
|
+
text-align: center;
|
101
|
+
transition: all 0.3s ease-in-out;
|
102
|
+
pointer-events: auto;
|
103
|
+
`;
|
104
|
+
|
105
|
+
notification.innerHTML = `
|
106
|
+
<div style="font-size: 26px; font-weight: 800; margin-bottom: #{message ? '12px' : '0'};">#{safe_title_html}</div>
|
107
|
+
#{safe_message ? "<div style=\"font-size: 20px; font-weight: 500; opacity: 0.9;\">#{safe_message_html}</div>" : ''}
|
108
|
+
`;
|
109
|
+
|
110
|
+
container.appendChild(notification);
|
111
|
+
|
112
|
+
// Auto remove after duration
|
113
|
+
setTimeout(() => {
|
114
|
+
notification.style.opacity = '0';
|
115
|
+
notification.style.transform = 'translateY(-10px)';
|
116
|
+
setTimeout(() => {
|
117
|
+
if (notification.parentNode) {
|
118
|
+
notification.parentNode.removeChild(notification);
|
119
|
+
}
|
120
|
+
}, 300);
|
121
|
+
}, #{duration});
|
122
|
+
|
123
|
+
// Click to dismiss
|
124
|
+
notification.addEventListener('click', () => {
|
125
|
+
notification.style.opacity = '0';
|
126
|
+
notification.style.transform = 'translateY(-10px)';
|
127
|
+
setTimeout(() => {
|
128
|
+
if (notification.parentNode) {
|
129
|
+
notification.parentNode.removeChild(notification);
|
130
|
+
}
|
131
|
+
}, 300);
|
132
|
+
});
|
133
|
+
JS
|
134
|
+
end
|
135
|
+
|
136
|
+
def notification_position_css
|
137
|
+
case Capybara::Presenter.configuration.notification_position
|
138
|
+
when :top
|
139
|
+
'top: 40px; left: 50%; transform: translateX(-50%);'
|
140
|
+
when :center
|
141
|
+
'top: 50%; left: 50%; transform: translate(-50%, -50%);'
|
142
|
+
when :bottom
|
143
|
+
'bottom: 40px; left: 50%; transform: translateX(-50%);'
|
144
|
+
else
|
145
|
+
'top: 40px; left: 50%; transform: translateX(-50%);'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
# rubocop:enable Metrics/MethodLength
|
149
|
+
|
150
|
+
def sanitize_for_js(string)
|
151
|
+
return '' unless string
|
152
|
+
|
153
|
+
string.to_s
|
154
|
+
.gsub('\\', '\\\\') # Escape backslashes first
|
155
|
+
.gsub("'", "\\'") # Escape single quotes
|
156
|
+
.gsub('"', '\\"') # Escape double quotes
|
157
|
+
.gsub("\n", '\\n') # Escape newlines
|
158
|
+
.gsub("\r", '\\r') # Escape carriage returns
|
159
|
+
.gsub("\t", '\\t') # Escape tabs
|
160
|
+
.gsub('</script>', '<\\/script>') # Prevent script injection
|
161
|
+
end
|
162
|
+
|
163
|
+
def sanitize_for_html(string)
|
164
|
+
return '' unless string
|
165
|
+
|
166
|
+
string.to_s
|
167
|
+
.gsub('&', '&') # Escape ampersands first
|
168
|
+
.gsub('<', '<') # Escape less than
|
169
|
+
.gsub('>', '>') # Escape greater than
|
170
|
+
.gsub('"', '"') # Escape double quotes
|
171
|
+
.gsub("'", ''') # Escape single quotes
|
172
|
+
end
|
173
|
+
end
|
174
|
+
# rubocop:enable Metrics/ModuleLength
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'presenter/version'
|
4
|
+
require_relative 'presenter/configuration'
|
5
|
+
require_relative 'presenter/notifications'
|
6
|
+
require_relative 'presenter/capybara_extensions'
|
7
|
+
|
8
|
+
module Capybara
|
9
|
+
# Main module for the Capybara::Presenter gem.
|
10
|
+
# Transforms standard Capybara system tests into presentation-ready demos
|
11
|
+
# with visual notifications and configurable delays.
|
12
|
+
module Presenter
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def configuration
|
17
|
+
@configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure
|
21
|
+
yield(configuration) if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset_configuration!
|
25
|
+
@configuration = Configuration.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.included(base)
|
30
|
+
base.extend(ClassMethods)
|
31
|
+
base.include(InstanceMethods)
|
32
|
+
|
33
|
+
# Show setup guidance when presenter mode is enabled
|
34
|
+
return unless configuration.enabled
|
35
|
+
|
36
|
+
show_setup_guidance
|
37
|
+
end
|
38
|
+
|
39
|
+
# rubocop:disable Metrics/MethodLength
|
40
|
+
def self.show_setup_guidance
|
41
|
+
puts <<~GUIDANCE
|
42
|
+
|
43
|
+
🎬 Capybara::Presenter Setup Guidance:
|
44
|
+
|
45
|
+
For browser notifications to appear:
|
46
|
+
1. Use a non-headless driver (Chrome, Firefox, Safari)
|
47
|
+
2. Ensure browser opens visibly#{' '}
|
48
|
+
3. Disable parallel testing: parallelize(workers: 1)
|
49
|
+
|
50
|
+
Environment variables:
|
51
|
+
- PRESENTER_DELAY=2.0 (action delays)
|
52
|
+
- PRESENTER_TEST_START_DELAY=2.0 (pause before each test)
|
53
|
+
- PRESENTER_NOTIFICATIONS=false (disable notifications)
|
54
|
+
|
55
|
+
💡 This gem works with your existing Capybara driver configuration
|
56
|
+
|
57
|
+
GUIDANCE
|
58
|
+
end
|
59
|
+
# rubocop:enable Metrics/MethodLength
|
60
|
+
|
61
|
+
# Class-level helper methods for checking presenter configuration.
|
62
|
+
module ClassMethods
|
63
|
+
def presenter_mode?
|
64
|
+
Capybara::Presenter.configuration.enabled
|
65
|
+
end
|
66
|
+
|
67
|
+
def presenter_delay_duration
|
68
|
+
Capybara::Presenter.configuration.delay
|
69
|
+
end
|
70
|
+
|
71
|
+
def presenter_notifications_enabled?
|
72
|
+
Capybara::Presenter.configuration.notifications
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Instance-level helper methods for checking presenter configuration.
|
77
|
+
module InstanceMethods
|
78
|
+
include Notifications
|
79
|
+
include CapybaraExtensions
|
80
|
+
|
81
|
+
def presenter_mode?
|
82
|
+
self.class.presenter_mode?
|
83
|
+
end
|
84
|
+
|
85
|
+
def presenter_delay(seconds = nil)
|
86
|
+
return unless presenter_mode?
|
87
|
+
|
88
|
+
sleep(seconds || presenter_delay_duration)
|
89
|
+
end
|
90
|
+
|
91
|
+
def presenter_delay_duration
|
92
|
+
self.class.presenter_delay_duration
|
93
|
+
end
|
94
|
+
|
95
|
+
def presenter_notifications_enabled?
|
96
|
+
self.class.presenter_notifications_enabled?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Capybara
|
2
|
+
module Presenter
|
3
|
+
VERSION: String
|
4
|
+
|
5
|
+
class Configuration
|
6
|
+
attr_reader enabled: bool
|
7
|
+
attr_reader delay: Float
|
8
|
+
attr_reader notifications: bool
|
9
|
+
attr_reader notification_position: Symbol
|
10
|
+
attr_reader notification_style: Symbol
|
11
|
+
attr_reader test_start_delay: Float
|
12
|
+
|
13
|
+
def initialize: () -> void
|
14
|
+
def enabled=: (untyped value) -> bool
|
15
|
+
def delay=: (Numeric value) -> Float
|
16
|
+
def notifications=: (untyped value) -> bool
|
17
|
+
def notification_position=: (Symbol value) -> Symbol
|
18
|
+
def notification_style=: (Symbol value) -> Symbol
|
19
|
+
def test_start_delay=: (Numeric value) -> Float
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def parse_float_env: (String env_var, Float default) -> Float
|
24
|
+
end
|
25
|
+
|
26
|
+
module Notifications
|
27
|
+
def presenter_notification: (String title, ?String? message, ?type: Symbol, ?duration: Integer) -> void
|
28
|
+
def presenter_milestone: (String title, ?String? message) -> void
|
29
|
+
def presenter_test_start_notification: (Class test_class, String test_method) -> void
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def show_browser_notification: (String title, String? message, Symbol type, Integer duration) -> void
|
34
|
+
def has_browser_access?: () -> bool
|
35
|
+
def notification_javascript: (String title, String? message, Symbol type, Integer duration) -> String
|
36
|
+
def notification_position_css: () -> String
|
37
|
+
def sanitize_for_js: (String? string) -> String
|
38
|
+
def sanitize_for_html: (String? string) -> String
|
39
|
+
end
|
40
|
+
|
41
|
+
module Delays
|
42
|
+
def presenter_delay: (?Float? seconds) -> void
|
43
|
+
end
|
44
|
+
|
45
|
+
module CapybaraExtensions
|
46
|
+
def setup_presenter_delays: () -> void
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def log_presenter_action: (Symbol action, untyped target) -> void
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.configuration: () -> Configuration
|
54
|
+
def self.configure: () { (Configuration) -> void } -> void
|
55
|
+
def self.reset_configuration!: () -> Configuration
|
56
|
+
def self.show_setup_guidance: () -> void
|
57
|
+
|
58
|
+
module ClassMethods
|
59
|
+
def presenter_mode?: () -> bool
|
60
|
+
def presenter_delay_duration: () -> Float
|
61
|
+
def presenter_notifications_enabled?: () -> bool
|
62
|
+
end
|
63
|
+
|
64
|
+
module InstanceMethods
|
65
|
+
def presenter_mode?: () -> bool
|
66
|
+
def presenter_delay_duration: () -> Float
|
67
|
+
def presenter_notifications_enabled?: () -> bool
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capybara-presenter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- José Coelho
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-05-27 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: capybara
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: selenium-webdriver
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '4.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '4.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: lefthook
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.11'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.11'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: minitest
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '5.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rake
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '13.0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '13.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rubocop
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '1.75'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.75'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rubocop-minitest
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0.38'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.38'
|
110
|
+
description: Transform Capybara system tests into polished presentations with automatic
|
111
|
+
delays, browser notifications, and visual cues perfect for creating professional
|
112
|
+
test recordings and demonstrations.
|
113
|
+
email:
|
114
|
+
- jose.coelho@palomagroup.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".rubocop.yml"
|
120
|
+
- CHANGELOG.md
|
121
|
+
- CLAUDE.md
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- lefthook.yml
|
126
|
+
- lib/capybara/presenter.rb
|
127
|
+
- lib/capybara/presenter/capybara_extensions.rb
|
128
|
+
- lib/capybara/presenter/configuration.rb
|
129
|
+
- lib/capybara/presenter/notifications.rb
|
130
|
+
- lib/capybara/presenter/version.rb
|
131
|
+
- sig/capybara/demo.rbs
|
132
|
+
homepage: https://github.com/josecoelho/capybara-presenter
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata:
|
136
|
+
allowed_push_host: https://rubygems.org
|
137
|
+
homepage_uri: https://github.com/josecoelho/capybara-presenter
|
138
|
+
source_code_uri: https://github.com/josecoelho/capybara-presenter
|
139
|
+
changelog_uri: https://github.com/josecoelho/capybara-presenter/blob/main/CHANGELOG.md
|
140
|
+
rubygems_mfa_required: 'true'
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: 3.2.0
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubygems_version: 3.6.6
|
156
|
+
specification_version: 4
|
157
|
+
summary: Make Capybara tests presentation-ready with visual notifications and recording-friendly
|
158
|
+
delays
|
159
|
+
test_files: []
|