diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..3f25db4
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@
+name: CI
+
+on:
+ push:
+ branches: [master, main]
+ pull_request:
+ branches: [master, main]
+
+jobs:
+ test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ ruby: ['3.1', '3.2', '3.3']
+ include:
+ - os: ubuntu-latest
+ ruby: jruby
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Ruby ${{ matrix.ruby }}
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
+
+ - name: Run tests
+ run: bundle exec rspec
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index bf62658..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-language: ruby
-rvm:
- - 1.9.3
- - jruby
- - 2.0
-before_install:
- - export DISPLAY=:99.0
- - sh -e /etc/init.d/xvfb start
-script:
- - bundle exec rspec
\ No newline at end of file
diff --git a/AI_INTEGRATION_IDEAS.md b/AI_INTEGRATION_IDEAS.md
new file mode 100644
index 0000000..59a3df5
--- /dev/null
+++ b/AI_INTEGRATION_IDEAS.md
@@ -0,0 +1,203 @@
+# AI Integration Ideas for Infinity Test
+
+This document outlines ideas for integrating Infinity Test with AI tools and agents, making Ruby development more efficient and intelligent.
+
+## 1. Claude Code Integration
+
+### Direct Integration
+Infinity Test can work seamlessly with [Claude Code](https://claude.ai/claude-code), Anthropic's CLI tool for AI-assisted development.
+
+**How it works:**
+- Run `infinity_test` in one terminal while using Claude Code in another
+- When Claude Code makes changes to your Ruby files, Infinity Test automatically runs the relevant tests
+- Immediate feedback loop: write code → tests run → see results → iterate
+
+**Example workflow:**
+```bash
+# Terminal 1: Start infinity_test
+infinity_test --mode rails
+
+# Terminal 2: Use Claude Code
+claude "Add a validation to the User model that requires email to be present"
+# Infinity Test automatically runs user_spec.rb when user.rb changes
+```
+
+### AI-Powered Test Generation
+Claude Code can generate tests based on your code changes:
+```bash
+claude "Write RSpec tests for the new validation I just added to User model"
+# Tests are created, infinity_test runs them automatically
+```
+
+## 2. MCP (Model Context Protocol) Server
+
+Create an MCP server that exposes Infinity Test functionality to AI agents:
+
+```ruby
+# Example MCP server endpoints
+class InfinityTestMCPServer
+ # Get current test status
+ def get_test_status
+ { last_run: Time.now, failures: 0, pending: 2 }
+ end
+
+ # Run specific tests
+ def run_tests(files:)
+ InfinityTest.run(files)
+ end
+
+ # Get failed tests
+ def get_failures
+ # Return list of failed test files with error messages
+ end
+end
+```
+
+**Benefits:**
+- AI agents can query test results programmatically
+- Agents can trigger test runs for specific files
+- Agents can understand test failures and suggest fixes
+
+## 3. AI-Assisted Debugging
+
+### Failure Analysis
+When tests fail, integrate with AI to:
+- Analyze the failure message
+- Suggest potential fixes
+- Identify related code that might need changes
+
+```ruby
+# INFINITY_TEST configuration
+InfinityTest.setup do |config|
+ config.after(:all) do |results|
+ if results.failures.any?
+ # Send failures to AI for analysis
+ AIHelper.analyze_failures(results.failures)
+ end
+ end
+end
+```
+
+### Smart Test Selection
+Use AI to predict which tests are most likely to fail based on:
+- Which files were changed
+- Historical failure patterns
+- Code complexity metrics
+
+## 4. Natural Language Test Commands
+
+Future idea: Integrate with AI to support natural language commands:
+
+This is just an idea:
+
+```bash
+# Instead of
+infinity_test --focus spec/models/user_spec.rb
+
+# Support natural language
+infinity_test --ai "run tests for user authentication"
+infinity_test --ai "only run the tests that failed yesterday"
+infinity_test --ai "run slow tests in parallel"
+```
+
+## 5. Continuous Learning
+
+### Pattern Recognition
+- Track which code changes tend to break which tests
+- Learn from your project's test patterns
+- Predict test failures before running
+
+### Smart Prioritization
+- Run tests most likely to fail first
+- Skip tests unlikely to be affected by changes
+- Optimize test order for fastest feedback
+
+## 6. Integration with Popular AI Tools
+
+### GitHub Copilot
+- Infinity Test watches files, Copilot suggests code
+- Immediate test feedback on Copilot suggestions
+
+### Cursor IDE
+- Real-time test results in Cursor's AI panel
+- AI can see test output when suggesting fixes
+
+### Cody (Sourcegraph)
+- Cody understands your test patterns
+- Suggests tests based on code context
+
+## 7. Hooks for AI Agents
+
+Add hooks that AI agents can use:
+
+```ruby
+InfinityTest.setup do |config|
+ # Hook for AI to process before each test run
+ config.before(:all) do
+ AIAgent.notify(:test_starting)
+ end
+
+ # Hook for AI to process test results
+ config.after(:all) do |results|
+ AIAgent.process_results(results)
+ end
+
+ # Custom AI-powered heuristics
+ config.ai_heuristics do |changed_file|
+ AIAgent.predict_tests_to_run(changed_file)
+ end
+end
+```
+
+## 8. Test Quality Analysis
+
+Use AI to analyze test quality:
+- Identify flaky tests
+- Suggest missing test cases
+- Detect duplicate tests
+- Recommend test refactoring
+
+## 9. Documentation Generation
+
+After tests pass, AI can:
+- Generate documentation from test descriptions
+- Update README with usage examples from tests
+- Create API documentation from request specs
+
+## 10. Future Vision: Autonomous Testing
+
+The ultimate goal - AI agents that:
+1. Watch you code
+2. Understand your intent
+3. Write appropriate tests
+4. Run those tests via Infinity Test
+5. Suggest improvements based on results
+6. Iterate until code is solid
+
+---
+
+## Getting Started
+
+To integrate Infinity Test with Claude Code today:
+
+1. Install both tools:
+```bash
+gem install infinity_test
+npm install -g @anthropic/claude-code
+```
+
+2. Start Infinity Test in watch mode:
+```bash
+infinity_test
+```
+
+3. Use Claude Code in another terminal:
+```bash
+claude "Help me write a new feature for my Rails app"
+```
+
+4. Watch the magic happen!
+
+---
+
+*This document is part of the Infinity Test project. Contributions and ideas welcome!*
diff --git a/Gemfile b/Gemfile
index cd8aa9e..154ef5e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,5 @@
source 'https://rubygems.org'
-gemspec
\ No newline at end of file
+gem 'notifiers', github: 'tomas-stefano/notifiers', branch: 'main'
+
+gemspec
diff --git a/History.markdown b/History.markdown
index dff40b8..6d41915 100644
--- a/History.markdown
+++ b/History.markdown
@@ -1,17 +1,83 @@
-development
-===========
-
-- Rewrite the ENTIRE LIBRARY (Separate responsabilities!)
-- The #before_env method in the configuration file was removed.
-- The #before_run and #after_run method in the configuration file, was removed. Use before(:all) and after(:all) instead.
-- Shared Examples to create your own strategy.
-- Update all specs to RSpec 2.
-- Now you can create your own observer (case you want add other gem like watchr / monitor files).
-- Now you can add more patterns to monitor in a simple dsl without ugly nasty code.
-
-- Work Notifications using the notifiers gem.
-- Work RSpec with the new way of auto discover libraries.
-- Work Rubygems. \o/
+v2.0.0
+======
+
+This is a major release with a complete rewrite of the library, modernization of dependencies, and many new features.
+
+Breaking Changes
+----------------
+- Configuration file renamed from `.infinity_test` to `INFINITY_TEST`
+- Removed Bacon test framework support
+- Removed Growl notification support (use notifiers gem instead)
+- The #before_env method in the configuration file was removed
+- The #before_run and #after_run methods were removed. Use before(:all) and after(:all) instead
+
+New Features
+------------
+- **Modern Notifications**: Integration with the notifiers gem supporting:
+ - osascript (macOS built-in)
+ - terminal_notifier (macOS)
+ - notify_send (Linux libnotify)
+ - dunstify (Linux dunst)
+ - auto_discover (automatic detection)
+ - New CLI options: `--notifications` and `--mode` for image themes
+
+- **Callbacks System**: Full callback support with before/after hooks
+ - `before(:all)` - Run before all tests
+ - `after(:all)` - Run after all tests
+ - `before(:each_ruby)` - Run before each Ruby version
+ - `after(:each_ruby)` - Run after each Ruby version
+
+- **Multi-Ruby Support**:
+ - RVM strategy: Run tests across multiple Ruby versions with gemset support
+ - RbEnv strategy: Run tests with RBENV_VERSION environment variable
+ - RubyDefault strategy: Run on current Ruby version
+
+- **Just Watch Mode**: `--just-watch` (-j) option to skip initial test run
+ - Useful for large applications where startup is slow
+ - Only watches for file changes and runs tests on change
+
+- **Focus Mode**: `--focus` (-f) option for running specific tests
+ - `--focus failures` - Run only previously failed tests
+ - `--focus path/to/spec.rb` - Run only specified file
+
+- **Framework Heuristics**:
+ - Rails: Watches models, controllers, helpers, mailers, jobs, lib
+ - Padrino: Similar to Rails with Padrino-specific paths
+ - Rubygems: Watches lib and test/spec directories
+
+- **Auto Discovery Priority**: Smart prioritization when auto-discovering
+ - Strategies: RVM > RbEnv > RubyDefault
+ - Frameworks: Rails > Padrino > Rubygems
+ - Test Frameworks: RSpec > Test::Unit
+
+- **Test Framework Improvements**:
+ - Complete Test::Unit/Minitest implementation with output parsing
+ - RSpec improvements: test_dir=, pending?, and .run? methods
+
+- **Modern File Watching**:
+ - Listen gem (default) - Event-driven, uses native OS notifications
+ - Filewatcher gem - Polling-based, works everywhere including VMs/NFS
+
+- **AI Integration Ideas**: Documentation for integrating with Claude Code and other AI tools
+
+Bug Fixes
+---------
+- Fixed signal handler blocking issue in observer base
+- Fixed ActiveSupport autoload issues
+- Replaced deprecated stub with allow().to receive() in specs
+
+Dependencies
+------------
+- Replaced watchr with listen and filewatcher observers
+- Updated to modern notifiers gem (from GitHub main branch)
+- Removed growl dependency
+
+Internal Changes
+----------------
+- Rewrite of the entire library with separated responsibilities
+- Shared examples for creating custom strategies, frameworks, and observers
+- Updated all specs to modern RSpec syntax
+- Comprehensive test coverage (250+ examples)
v1.0.1
======
diff --git a/.infinity_test b/INFINITY_TEST
similarity index 86%
rename from .infinity_test
rename to INFINITY_TEST
index 42dc0d6..fa0be2b 100644
--- a/.infinity_test
+++ b/INFINITY_TEST
@@ -1,8 +1,7 @@
-InfinityTest.setup do |config|
- config.strategy = :ruby_default
- config.test_framework = :rspec
- config.notifications = :growl
-end
+#InfinityTest.setup do |config|
+# config.strategy = :ruby_default
+# config.test_framework = :rspec
+#end
# infinity_test do
@@ -46,4 +45,4 @@ end
# end
# end
-# end
\ No newline at end of file
+# end
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8a7c5d8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,627 @@
+# Infinity Test
+
+**To Infinity and Beyond!**
+
+Infinity Test is a continuous testing library and a flexible alternative to Autotest and Guard. It watches your files for changes and automatically runs your tests, providing instant feedback with desktop notifications.
+
+Version 2.0.0 brings a complete rewrite with modern dependencies, multi-Ruby support via RVM/RbEnv, and a powerful callbacks system.
+
+## Table of Contents
+
+- [Installation](#installation)
+- [Quick Start](#quick-start)
+- [Command Line Options](#command-line-options)
+- [Configuration File (INFINITY_TEST)](#configuration-file-infinity_test)
+- [Ruby Version Managers](#ruby-version-managers)
+- [Test Frameworks](#test-frameworks)
+- [Application Frameworks](#application-frameworks)
+- [Notifications](#notifications)
+- [Image Themes](#image-themes)
+- [Callbacks](#callbacks)
+- [File Watching](#file-watching)
+- [Advanced Usage](#advanced-usage)
+
+---
+
+## Installation
+
+```bash
+gem install infinity_test
+```
+
+Or add to your Gemfile:
+
+```ruby
+gem 'infinity_test', group: :development
+```
+
+Then run:
+
+```bash
+bundle install
+```
+
+---
+
+## Quick Start
+
+### Step 1: Navigate to your project
+
+```bash
+cd /path/to/your/project
+```
+
+### Step 2: Run Infinity Test
+
+```bash
+infinity_test
+```
+
+That's it! Infinity Test will:
+
+1. Auto-detect your test framework (RSpec or Test::Unit)
+2. Auto-detect your application framework (Rails, Padrino, or Rubygems)
+3. Run all tests immediately
+4. Watch for file changes and re-run relevant tests
+5. Show desktop notifications with test results
+
+### Step 3: Start coding
+
+Edit your files and watch the tests run automatically!
+
+---
+
+## Command Line Options
+
+| Option | Short | Description |
+|--------|-------|-------------|
+| `--ruby strategy` | | Ruby manager strategy: `auto_discover`, `rvm`, `rbenv`, `ruby_default` |
+| `--rubies=versions` | | Ruby versions to test against (comma-separated) |
+| `--test library` | | Test framework: `auto_discover`, `rspec`, `test_unit` |
+| `--framework library` | | Application framework: `auto_discover`, `rails`, `rubygems`, `padrino` |
+| `--options=options` | | Additional options to pass to test command |
+| `--notifications library` | | Notification system: `auto_discover`, `osascript`, `terminal_notifier`, `notify_send`, `dunstify` |
+| `--mode theme` | | Image theme for notifications (see [Image Themes](#image-themes)) |
+| `--no-infinity-and-beyond` | `-n` | Run tests once and exit (CI mode) |
+| `--just-watch` | `-j` | Skip initial test run, only watch for changes |
+| `--focus [FILE]` | `-f` | Focus on specific tests or `failures` for last failed tests |
+| `--no-verbose` | | Don't print commands before executing |
+| `--no-bundler` | | Bypass Bundler support |
+| `--help` | | Show help message |
+
+### Examples
+
+**Run with RSpec only:**
+```bash
+infinity_test --test rspec
+```
+
+**Run tests on multiple Ruby versions with RVM:**
+```bash
+infinity_test --ruby rvm --rubies=3.0.0,3.1.0,3.2.0
+```
+
+**Run tests once and exit (for CI):**
+```bash
+infinity_test -n
+```
+
+**Skip initial test run, just watch:**
+```bash
+infinity_test --just-watch
+```
+
+**Focus on failed tests:**
+```bash
+infinity_test --focus failures
+```
+
+**Focus on a specific file:**
+```bash
+infinity_test --focus spec/models/user_spec.rb
+```
+
+**Use a specific notification theme:**
+```bash
+infinity_test --mode mario_bros
+```
+
+**Pass additional options to the test runner:**
+```bash
+infinity_test --options="-Ilib -Itest"
+```
+
+---
+
+## Configuration File (INFINITY_TEST)
+
+You can persist your settings in an `INFINITY_TEST` file. Infinity Test looks for configuration in two locations:
+
+1. **Project file**: `./INFINITY_TEST` (higher priority)
+2. **Global file**: `~/INFINITY_TEST` (lower priority)
+
+### Basic Configuration
+
+```ruby
+InfinityTest.setup do |config|
+ config.test_framework = :rspec
+ config.framework = :rails
+ config.notifications = :terminal_notifier
+ config.mode = :mario_bros
+ config.verbose = true
+end
+```
+
+### All Configuration Options
+
+```ruby
+InfinityTest.setup do |config|
+ # Ruby Version Manager
+ # Options: :auto_discover, :rvm, :rbenv, :ruby_default
+ config.strategy = :auto_discover
+
+ # Ruby versions to test against (requires RVM or RbEnv)
+ config.rubies = ['3.0.0', '3.1.0', '3.2.0']
+
+ # Gemset to use with RVM
+ config.gemset = 'my_project'
+
+ # Test framework
+ # Options: :auto_discover, :rspec, :test_unit
+ config.test_framework = :rspec
+
+ # Application framework
+ # Options: :auto_discover, :rails, :padrino, :rubygems
+ config.framework = :rails
+
+ # File observer
+ # Options: :listen (event-driven), :filewatcher (polling)
+ config.observer = :listen
+
+ # Notification system
+ # Options: :auto_discover, :osascript, :terminal_notifier, :notify_send, :dunstify
+ config.notifications = :auto_discover
+
+ # Image theme for notifications
+ config.mode = :simpson
+
+ # Or use custom images
+ config.success_image = '/path/to/success.png'
+ config.failure_image = '/path/to/failure.png'
+ config.pending_image = '/path/to/pending.png'
+
+ # Bundler support (auto-detected if Gemfile exists)
+ config.bundler = true
+
+ # Print commands before executing
+ config.verbose = true
+
+ # File extension to watch
+ config.extension = :rb
+
+ # Skip initial test run
+ config.just_watch = false
+
+ # Run tests and exit (CI mode)
+ config.infinity_and_beyond = true
+
+ # Focus mode: nil, :failures, or file path
+ config.focus = nil
+
+ # Ignore specific test files
+ config.ignore_test_files = [
+ 'spec/slow/performance_spec.rb'
+ ]
+
+ # Ignore test folders
+ config.ignore_test_folders = [
+ 'spec/integration'
+ ]
+end
+```
+
+### Configuration with Callbacks
+
+```ruby
+InfinityTest.setup do |config|
+ config.test_framework = :rspec
+ config.mode = :faces
+end
+
+# Clear terminal before running tests
+InfinityTest::Core::Base.before(:all) do
+ InfinityTest::Core::Base.clear_terminal
+end
+
+# Run after all tests complete
+InfinityTest::Core::Base.after(:all) do
+ system('say "Tests finished"')
+end
+
+# Run before each Ruby version (when testing multiple rubies)
+InfinityTest::Core::Base.before(:each_ruby) do |environment|
+ puts "Testing with Ruby #{environment[:ruby_version]}"
+end
+
+# Run after each Ruby version
+InfinityTest::Core::Base.after(:each_ruby) do |environment|
+ puts "Finished testing Ruby #{environment[:ruby_version]}"
+end
+```
+
+---
+
+## Ruby Version Managers
+
+Infinity Test supports testing across multiple Ruby versions using RVM or RbEnv.
+
+### Auto Discovery (Default)
+
+```ruby
+config.strategy = :auto_discover
+```
+
+Priority order: RVM > RbEnv > RubyDefault
+
+### RVM Strategy
+
+Test against multiple Ruby versions with optional gemset support:
+
+```ruby
+InfinityTest.setup do |config|
+ config.strategy = :rvm
+ config.rubies = ['3.0.0', '3.1.0', 'jruby-9.4.0.0']
+ config.gemset = 'infinity_test' # Optional
+end
+```
+
+**Command line:**
+```bash
+infinity_test --ruby rvm --rubies=3.0.0,3.1.0,jruby-9.4.0.0
+```
+
+### RbEnv Strategy
+
+Test against multiple Ruby versions using rbenv:
+
+```ruby
+InfinityTest.setup do |config|
+ config.strategy = :rbenv
+ config.rubies = ['3.0.0', '3.1.0', '3.2.0']
+end
+```
+
+**Command line:**
+```bash
+infinity_test --ruby rbenv --rubies=3.0.0,3.1.0
+```
+
+### Ruby Default Strategy
+
+Use the current Ruby version only (no version manager):
+
+```ruby
+config.strategy = :ruby_default
+```
+
+---
+
+## Test Frameworks
+
+### RSpec
+
+Auto-detected when `spec/` directory exists with `spec_helper.rb` or `*_spec.rb` files.
+
+```ruby
+config.test_framework = :rspec
+```
+
+### Test::Unit / Minitest
+
+Auto-detected when `test/` directory exists with `test_helper.rb` or `*_test.rb` files.
+
+```ruby
+config.test_framework = :test_unit
+```
+
+---
+
+## Application Frameworks
+
+### Rails
+
+Auto-detected when `config/environment.rb` exists.
+
+**Watched directories:**
+- `app/models` → runs corresponding model specs/tests
+- `app/controllers` → runs corresponding controller specs/tests
+- `app/helpers` → runs corresponding helper specs/tests
+- `app/mailers` → runs corresponding mailer specs/tests
+- `app/jobs` → runs corresponding job specs/tests
+- `lib/` → runs corresponding lib specs/tests
+- `spec/` or `test/` → runs the changed spec/test file
+- `spec_helper.rb` or `test_helper.rb` → runs all tests
+
+### Padrino
+
+Auto-detected when `config/boot.rb` exists with Padrino reference.
+
+Similar heuristics to Rails with Padrino-specific paths.
+
+### Rubygems (Default)
+
+Used for gem development and simple Ruby projects.
+
+**Watched directories:**
+- `lib/` → runs corresponding specs/tests
+- `spec/` or `test/` → runs the changed spec/test file
+
+---
+
+## Notifications
+
+Desktop notifications show test results with themed images.
+
+### Notification Systems
+
+| System | Platform | Description |
+|--------|----------|-------------|
+| `auto_discover` | All | Automatically detect available notifier |
+| `terminal_notifier` | macOS | Modern macOS notifications |
+| `osascript` | macOS | Built-in AppleScript notifications |
+| `notify_send` | Linux | libnotify notifications |
+| `dunstify` | Linux | Dunst notification daemon |
+
+**Configuration:**
+```ruby
+config.notifications = :terminal_notifier
+```
+
+**Command line:**
+```bash
+infinity_test --notifications terminal_notifier
+```
+
+---
+
+## Image Themes
+
+Infinity Test includes fun image themes for notifications. Each theme has three images: `success`, `failure`, and `pending`.
+
+### Available Themes
+
+| Theme | Description | Location |
+|-------|-------------|----------|
+| `simpson` | The Simpsons characters (default) | `images/simpson/` |
+| `faces` | Expressive face icons | `images/faces/` |
+| `fuuu` | Rage comic faces | `images/fuuu/` |
+| `hands` | Hand gesture icons | `images/hands/` |
+| `mario_bros` | Super Mario characters | `images/mario_bros/` |
+| `rails` | Ruby on Rails themed | `images/rails/` |
+| `rubies` | Ruby gemstone themed | `images/rubies/` |
+| `street_fighter` | Street Fighter characters | `images/street_fighter/` |
+| `toy_story` | Toy Story characters | `images/toy_story/` |
+
+### Using a Theme
+
+**Configuration file:**
+```ruby
+config.mode = :mario_bros
+```
+
+**Command line:**
+```bash
+infinity_test --mode mario_bros
+```
+
+### Custom Images
+
+Use your own images by setting the image paths directly:
+
+```ruby
+InfinityTest.setup do |config|
+ config.success_image = '/path/to/my_success.png'
+ config.failure_image = '/path/to/my_failure.png'
+ config.pending_image = '/path/to/my_pending.png'
+end
+```
+
+Or point to a custom directory containing `success.*`, `failure.*`, and `pending.*` images:
+
+```ruby
+config.mode = '/path/to/my/images/directory'
+```
+
+---
+
+## Callbacks
+
+Callbacks let you run custom code before or after tests.
+
+### Available Callbacks
+
+| Callback | Scope | Description |
+|----------|-------|-------------|
+| `before(:all)` | All | Runs once before all tests |
+| `after(:all)` | All | Runs once after all tests |
+| `before(:each_ruby)` | Per Ruby | Runs before testing each Ruby version |
+| `after(:each_ruby)` | Per Ruby | Runs after testing each Ruby version |
+
+### Examples
+
+**Clear terminal before tests:**
+```ruby
+InfinityTest::Core::Base.before(:all) do
+ InfinityTest::Core::Base.clear_terminal
+end
+```
+
+**Play a sound after tests:**
+```ruby
+InfinityTest::Core::Base.after(:all) do
+ system('afplay /path/to/sound.mp3')
+end
+```
+
+**Log Ruby version being tested:**
+```ruby
+InfinityTest::Core::Base.before(:each_ruby) do |env|
+ File.write('test.log', "Testing: #{env[:ruby_version]}\n", mode: 'a')
+end
+```
+
+**Compile extensions before each Ruby:**
+```ruby
+InfinityTest::Core::Base.before(:each_ruby) do |env|
+ system('rake compile')
+end
+```
+
+---
+
+## File Watching
+
+### Listen (Default)
+
+Event-driven file watching using native OS notifications. Fast and efficient.
+
+```ruby
+config.observer = :listen
+```
+
+### Filewatcher
+
+Polling-based file watching. Works everywhere including VMs and NFS mounts.
+
+```ruby
+config.observer = :filewatcher
+```
+
+---
+
+## Advanced Usage
+
+### Continuous Integration Mode
+
+Run tests once and exit with proper exit code:
+
+```bash
+infinity_test -n
+```
+
+Or in configuration:
+```ruby
+config.infinity_and_beyond = false
+```
+
+### Just Watch Mode
+
+Skip the initial test run and only watch for changes. Useful for large projects:
+
+```bash
+infinity_test --just-watch
+```
+
+### Focus Mode
+
+Run only specific tests:
+
+```bash
+# Run only previously failed tests
+infinity_test --focus failures
+
+# Run only a specific file
+infinity_test --focus spec/models/user_spec.rb
+```
+
+### Ignoring Files
+
+```ruby
+InfinityTest.setup do |config|
+ # Ignore specific test files
+ config.ignore_test_files = [
+ 'spec/slow/large_integration_spec.rb',
+ 'spec/external/api_spec.rb'
+ ]
+
+ # Ignore entire directories
+ config.ignore_test_folders = [
+ 'spec/integration',
+ 'spec/system'
+ ]
+end
+```
+
+### Watching Non-Ruby Files
+
+```ruby
+# Watch Python files
+config.extension = :py
+
+# Watch JavaScript files
+config.extension = :js
+```
+
+### Sample Complete Configuration
+
+```ruby
+# INFINITY_TEST
+
+InfinityTest.setup do |config|
+ # Multi-Ruby testing with RVM
+ config.strategy = :rvm
+ config.rubies = ['3.1.0', '3.2.0', '3.3.0']
+ config.gemset = 'myapp'
+
+ # Test framework and app framework
+ config.test_framework = :rspec
+ config.framework = :rails
+
+ # Notifications with Mario theme
+ config.notifications = :terminal_notifier
+ config.mode = :mario_bros
+
+ # Performance settings
+ config.observer = :listen
+ config.just_watch = false
+ config.verbose = true
+
+ # Ignore slow tests during development
+ config.ignore_test_folders = ['spec/system']
+end
+
+# Clear screen before each run
+InfinityTest::Core::Base.before(:all) do
+ InfinityTest::Core::Base.clear_terminal
+end
+
+# Notify via system sound when done
+InfinityTest::Core::Base.after(:all) do
+ system('afplay /System/Library/Sounds/Glass.aiff')
+end
+```
+
+---
+
+## Contributing
+
+1. Fork the repository
+2. Create your feature branch (`git checkout -b feature/amazing-feature`)
+3. Run the tests (`infinity_test`)
+4. Commit your changes (`git commit -am 'Add amazing feature'`)
+5. Push to the branch (`git push origin feature/amazing-feature`)
+6. Create a Pull Request
+
+---
+
+## License
+
+MIT License - see [LICENSE.txt](LICENSE.txt) for details.
+
+---
+
+## Author
+
+Tomas D'Stefano
+
+**Happy Testing!**
diff --git a/Readme.markdown b/Readme.markdown
deleted file mode 100644
index 886b830..0000000
--- a/Readme.markdown
+++ /dev/null
@@ -1,21 +0,0 @@
-# Infinity Test
-
-Infinity Test is a continuous testing library and a flexible alternative to
-Autotest, using the awesome Watchr library with RSpec, Test::Unit, Bacon and
-with RVM functionality, giving the possibility to test with all Ruby versions
-that you have in your RVM configuration.
-
-## To Infinity and Beyond!
-
-
-
-## Install
-
- gem install infinity_test
-
-## Preparing the 2.0.0.rc1
-
-**After LONG YEARS without developing this gem, in 2015 I'm working to release the 2.0.0 soon. Coming soon!! \o/**
diff --git a/TODO.markdown b/TODO.markdown
index 8def40f..233788f 100644
--- a/TODO.markdown
+++ b/TODO.markdown
@@ -1,35 +1,29 @@
-## Road to 2.0
-
-* Test::Unit/Minitest.
-* RubyDefault strategy.
-* Make callbacks work in the new structure.
-* Work with test pattern on Configuration overwriting the Test framework instance.
-* Be possible to rewrite the rules in more nicer way, using the Hike gem to find files and paths.
-* Work with gemsets.
-* Rails.
-* Padrino.
-* Rvm.
-* Test with spork and zeus and other gems spork like (Experimental).
-
-### 2.0.0 Bugs
-
-* Improve auto discover feature priorization subclasses for #run? method.
-* Observer process signal must work in Ruby 2.0.0.
-
-### Flexibility
-
-* Ignore test files and test folders when run command and change files/dir.
-* Verbose way of InfinityTest.
-* Add specific options to the command.
-* Create a infinity test generator with thor!
-* Don't run integration tests. Ignore them when changed.
-
-### 2.0.2
-
-* RbEnv (experimented feature).
-* Bacon.
-* Focus feature(fails, pass one file, run entire suite) with --focus (experimented feature)!
-* Cucumber
-*
-
-Maybe we could create a infinity-test-contrib repositories with other heuristics platforms
+## Todo
+
+Infinity test is a gem that watch files changes and run tests.
+
+That also can run with different ruby versions all test suite or only that file change (using rvm or
+rbenv), ruby default just run the test on the current ruby version.
+
+## What Needs to Be Done
+
+* ~~Make notifications work with Notifiers gem (remove growl and use the autodiscover from notifiers gem).~~ DONE
+* ~~Change .infinity_test to INFINITY_TEST~~ DONE
+* ~~Make Test::Unit to work (the infinity test to check test folder).~~ DONE
+* ~~Make RSpec work~~ DONE
+* ~~Finish RubyDefault strategy~~ DONE
+* ~~Finish RVM (running different versions - the user need to specify)~~ DONE
+* ~~Finish RbEnv (running different versions - the user need to specify)~~ DONE
+* Make callbacks work in the new structure (loading the infinity test file).
+* ~~Finish Gem autodiscover and its changes heuristics.~~ DONE
+* ~~Finish Rails autodiscover and its changes heuristics.~~ DONE
+* ~~Padrino autodiscover and its changes heuristics.~~ DONE
+* ~~Improve auto discover feature priorization subclasses for #run? method.~~ DONE
+* ~~Focus feature(fails, pass one file, run entire suite) with --focus (experimented feature)!~~ DONE
+* ~~Add post-run hooks to be added to the INFINITY_TEST file that run other things (coverage, code
+analysis, etc - see ideas)~~ DONE (via callbacks system)
+
+* ~~Give some ideas (write to a md file the ideas) about how to integrate the infinity test with AI tools/AI agents or
+even Claude code ... so ruby developers can see~~ DONE (see AI_INTEGRATION_IDEAS.md)
+
+* ~~Update HISTORY with all changes since last version.~~ DONE
diff --git a/infinity_test.gemspec b/infinity_test.gemspec
index 92ec8d2..2af84ae 100644
--- a/infinity_test.gemspec
+++ b/infinity_test.gemspec
@@ -18,14 +18,15 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
- spec.add_dependency 'activesupport', '>= 3.2.0'
- spec.add_dependency 'watchr'
- spec.add_dependency 'hike', '~> 1.2'
- spec.add_dependency 'notifiers', '>= 1.2.2'
+ spec.add_dependency 'activesupport'
+ spec.add_dependency 'listen'
+ spec.add_dependency 'filewatcher'
+ spec.add_dependency 'hike'
+ spec.add_dependency 'notifiers'
- spec.add_development_dependency 'bundler', '~> 1.3'
+ spec.add_development_dependency 'bundler'
spec.add_development_dependency 'rake'
- spec.add_development_dependency 'rspec', '>= 2.14'
+ spec.add_development_dependency 'rspec'
spec.add_development_dependency 'simplecov'
spec.add_development_dependency 'pry'
diff --git a/lib/infinity_test.rb b/lib/infinity_test.rb
index 99b581e..d8b7e4f 100644
--- a/lib/infinity_test.rb
+++ b/lib/infinity_test.rb
@@ -1,4 +1,5 @@
require 'optparse'
+require 'active_support'
require 'active_support/core_ext'
require 'active_support/deprecation'
require 'hike'
@@ -8,6 +9,7 @@ module InfinityTest
module Core
autoload :AutoDiscover, 'infinity_test/core/auto_discover'
autoload :Base, 'infinity_test/core/base'
+ autoload :Callback, 'infinity_test/core/callback'
autoload :CommandBuilder, 'infinity_test/core/command_builder'
autoload :CommandRunner, 'infinity_test/core/command_runner'
autoload :ConfigurationMerge, 'infinity_test/core/configuration_merge'
@@ -33,8 +35,8 @@ module Framework
module Observer
autoload :Base, 'infinity_test/observer/base'
- autoload :Watchr, 'infinity_test/observer/watchr'
- autoload :EventMachine, 'infinity_test/observer/event_machine'
+ autoload :Listen, 'infinity_test/observer/listen'
+ autoload :Filewatcher, 'infinity_test/observer/filewatcher'
autoload :SharedExample, 'infinity_test/observer/shared_example'
end
@@ -65,4 +67,3 @@ def self.setup(&block)
require 'infinity_test/framework/rubygems'
require 'infinity_test/test_framework/test_unit'
require 'infinity_test/test_framework/rspec'
-require 'infinity_test/test_framework/bacon'
diff --git a/lib/infinity_test/core/auto_discover.rb b/lib/infinity_test/core/auto_discover.rb
index ed1be10..63e396b 100644
--- a/lib/infinity_test/core/auto_discover.rb
+++ b/lib/infinity_test/core/auto_discover.rb
@@ -3,6 +3,14 @@ module Core
class AutoDiscover
attr_reader :base
+ # Priority order for auto discovery (higher priority first)
+ # More specific libraries should be checked before generic ones
+ PRIORITY = {
+ strategy: [:rvm, :rbenv, :ruby_default],
+ framework: [:rails, :padrino, :rubygems],
+ test_framework: [:rspec, :test_unit]
+ }.freeze
+
def initialize(base)
@base = base
end
@@ -29,7 +37,16 @@ def discover_test_framework
def auto_discover(library_type)
library_base_class = "InfinityTest::#{library_type.to_s.camelize}::Base".constantize
- library = library_base_class.subclasses.find { |subclass| subclass.run? }
+ subclasses = library_base_class.subclasses
+ priority_order = PRIORITY[library_type] || []
+
+ # Sort subclasses by priority (known priorities first, unknown last)
+ sorted_subclasses = subclasses.sort_by do |subclass|
+ name = subclass.name.demodulize.underscore.to_sym
+ priority_order.index(name) || priority_order.length
+ end
+
+ library = sorted_subclasses.find { |subclass| subclass.run? }
if library.present?
library.name.demodulize.underscore.to_sym
@@ -39,7 +56,7 @@ def auto_discover(library_type)
The InfinityTest::Core::AutoDiscover doesn't discover nothing to run.
Are you using a #{library_type} that Infinity test knows?
- Infinity Test #{library_type.to_s.pluralize}: #{library_base_class.subclasses.each { |klass| klass }}
+ Infinity Test #{library_type.to_s.pluralize}: #{library_base_class.subclasses.map(&:name).join(', ')}
}
raise Exception, message
diff --git a/lib/infinity_test/core/base.rb b/lib/infinity_test/core/base.rb
index 7ccf6c0..8719a5d 100644
--- a/lib/infinity_test/core/base.rb
+++ b/lib/infinity_test/core/base.rb
@@ -23,14 +23,13 @@ class Base
cattr_accessor :specific_options
self.specific_options = ''
- # Test Framework to use Rspec, Bacon, Test::Unit or AutoDiscover(defaults)
+ # Test Framework to use RSpec, Test::Unit or AutoDiscover(defaults)
# ==== Options
# * :rspec
- # * :bacon
# * :test_unit (Test unit here apply to this two libs: test/unit and minitest)
- # * :auto_discover(defaults)
+ # * :auto_discover (default)
#
- # This will load a exactly a class constantize by name.
+ # This will load a class constantized by name.
#
cattr_accessor :test_framework
self.test_framework = :auto_discover
@@ -47,13 +46,14 @@ class Base
cattr_accessor :framework
self.framework = :auto_discover
- # Framework to observer watch the dirs.
+ # Framework to observe and watch the dirs for file changes.
#
# ==== Options
- # * watchr
+ # * :listen (default) - Event-driven, uses native OS notifications
+ # * :filewatcher - Polling-based, works everywhere including VMs/NFS
#
cattr_accessor :observer
- self.observer = :watchr
+ self.observer = :listen
# Ignore test files.
#
@@ -80,11 +80,11 @@ class Base
# Set the notification framework to use with Infinity Test.
#
# ==== Options
- # * :growl
- # * :lib_notify
- # * :auto_discover(defaults)
- #
- # This will load a exactly a class constantize by name.
+ # * :auto_discover (default) - Automatically detect available notifier
+ # * :terminal_notifier - macOS terminal-notifier
+ # * :osascript - macOS built-in notifications
+ # * :notify_send - Linux/BSD libnotify
+ # * :dunstify - Linux/BSD dunst
#
cattr_writer :notifications
self.notifications = :auto_discover
@@ -140,6 +140,22 @@ class Base
cattr_accessor :infinity_and_beyond
self.infinity_and_beyond = true
+ # Skip running tests on startup, only watch for file changes.
+ # Useful for large applications where you want to start watching quickly.
+ #
+ cattr_accessor :just_watch
+ self.just_watch = false
+
+ # Focus mode for running specific tests.
+ #
+ # ==== Options
+ # * nil - Run all tests (default)
+ # * :failures - Run only previously failed tests
+ # * String - Run only the specified file/pattern
+ #
+ cattr_accessor :focus
+ self.focus = nil
+
# The extension files that Infinity Test will search.
# You can observe python, erlang, etc files.
#
@@ -202,8 +218,10 @@ def self.verbose?
# # ...
# end
#
- def self.before(scope, &block)
- # setting_callback(Callbacks::BeforeCallback, scope, &block)
+ def self.before(scope = :all, &block)
+ callback = Callback.new(:before, scope, &block)
+ callbacks.push(callback)
+ callback
end
# Callback method to handle after all run and for each ruby too!
@@ -222,8 +240,32 @@ def self.before(scope, &block)
# # ...
# end
#
- def self.after(scope, &block)
- # setting_callback(Callbacks::AfterCallback, scope, &block)
+ def self.after(scope = :all, &block)
+ callback = Callback.new(:after, scope, &block)
+ callbacks.push(callback)
+ callback
+ end
+
+ # Run all before callbacks for the given scope
+ #
+ def self.run_before_callbacks(scope = :all, environment = nil)
+ callbacks.select { |c| c.before? && c.scope == scope }.each do |callback|
+ callback.call(environment)
+ end
+ end
+
+ # Run all after callbacks for the given scope
+ #
+ def self.run_after_callbacks(scope = :all, environment = nil)
+ callbacks.select { |c| c.after? && c.scope == scope }.each do |callback|
+ callback.call(environment)
+ end
+ end
+
+ # Clear all registered callbacks
+ #
+ def self.clear_callbacks!
+ self.callbacks = []
end
# Clear the terminal (Useful in the before callback)
@@ -244,10 +286,10 @@ def self.notifications(notification_name = nil, &block)
.notifications is DEPRECATED.
Use this instead:
InfinityTest.setup do |config|
- config.notifications = :growl
+ config.notifications = :dunstify
end
MESSAGE
- ActiveSupport::Deprecation.warn(message)
+ ActiveSupport::Deprecation.new.warn(message)
self.notifications = notification_name
self.instance_eval(&block) if block_given?
end
@@ -270,7 +312,7 @@ def self.show_images(options)
config.mode = 'infinity_test_dir_that_contain_images'
end
MESSAGE
- ActiveSupport::Deprecation.warn(message)
+ ActiveSupport::Deprecation.new.warn(message)
self.success_image = options[:success_image] || options[:sucess_image] if options[:success_image].present? || options[:sucess_image].present? # for fail typo in earlier versions.
self.pending_image = options[:pending_image] if options[:pending_image].present?
self.failure_image = options[:failure_image] if options[:failure_image].present?
@@ -298,7 +340,7 @@ def self.use(options)
config.gemset = :some_gemset
end
MESSAGE
- ActiveSupport::Deprecation.warn(message)
+ ActiveSupport::Deprecation.new.warn(message)
self.rubies = options[:rubies] if options[:rubies].present?
self.specific_options = options[:specific_options] if options[:specific_options].present?
self.test_framework = options[:test_framework] if options[:test_framework].present?
@@ -311,7 +353,7 @@ def self.use(options)
#
def self.clear(option)
message = '.clear(:terminal) is DEPRECATED. Please use .clear_terminal instead.'
- ActiveSupport::Deprecation.warn(message)
+ ActiveSupport::Deprecation.new.warn(message)
clear_terminal
end
@@ -322,14 +364,6 @@ def self.heuristics(&block)
def self.replace_patterns(&block)
# There is a spec pending.
end
-
- private
-
- # def self.setting_callback(callback_class, scope, &block)
- # callback_instance = callback_class.new(scope, &block)
- # self.callbacks.push(callback_instance)
- # callback_instance
- # end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/core/callback.rb b/lib/infinity_test/core/callback.rb
new file mode 100644
index 0000000..a0a4faa
--- /dev/null
+++ b/lib/infinity_test/core/callback.rb
@@ -0,0 +1,59 @@
+module InfinityTest
+ module Core
+ class Callback
+ attr_reader :scope, :block, :type
+
+ VALID_SCOPES = [:all, :each_ruby].freeze
+
+ # Create a new callback
+ #
+ # @param type [Symbol] :before or :after
+ # @param scope [Symbol] :all or :each_ruby (defaults to :all)
+ # @param block [Proc] The block to execute
+ #
+ def initialize(type, scope = :all, &block)
+ @type = type
+ @scope = scope || :all
+ @block = block
+
+ validate_scope!
+ end
+
+ # Execute the callback block
+ #
+ # @param environment [Hash] Optional environment info passed to the block
+ #
+ def call(environment = nil)
+ if block.arity > 0
+ block.call(environment)
+ else
+ block.call
+ end
+ end
+
+ def before?
+ type == :before
+ end
+
+ def after?
+ type == :after
+ end
+
+ def all?
+ scope == :all
+ end
+
+ def each_ruby?
+ scope == :each_ruby
+ end
+
+ private
+
+ def validate_scope!
+ unless VALID_SCOPES.include?(scope)
+ raise ArgumentError, "Invalid callback scope: #{scope}. Valid scopes are: #{VALID_SCOPES.join(', ')}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/infinity_test/core/configuration_merge.rb b/lib/infinity_test/core/configuration_merge.rb
index 1217224..6ca247b 100644
--- a/lib/infinity_test/core/configuration_merge.rb
+++ b/lib/infinity_test/core/configuration_merge.rb
@@ -5,6 +5,7 @@ class ConfigurationMerge
delegate :strategy, :rubies, :test_framework, :framework, :to => :options
delegate :specific_options, :infinity_and_beyond, :verbose, :bundler, :to => :options
+ delegate :notifications, :mode, :just_watch, :focus, :to => :options
def initialize(base, options)
@base = base
@@ -25,6 +26,10 @@ def merge!
@base.infinity_and_beyond = infinity_and_beyond unless infinity_and_beyond.nil?
@base.verbose = verbose unless verbose.nil?
@base.bundler = bundler unless bundler.nil?
+ @base.notifications = notifications if notifications.present?
+ @base.mode = mode if mode.present?
+ @base.just_watch = just_watch unless just_watch.nil?
+ @base.focus = focus if focus.present?
@base
end
end
diff --git a/lib/infinity_test/core/continuous_test_server.rb b/lib/infinity_test/core/continuous_test_server.rb
index a6676da..5388c3c 100644
--- a/lib/infinity_test/core/continuous_test_server.rb
+++ b/lib/infinity_test/core/continuous_test_server.rb
@@ -3,25 +3,53 @@ module Core
class ContinuousTestServer
attr_reader :base
delegate :binary, :test_files, to: :test_framework
- delegate :infinity_and_beyond, :notifications, :extension, to: :base
+ delegate :infinity_and_beyond, :notifications, :extension, :just_watch, :focus, to: :base
def initialize(base)
@base = base
end
def start
- run_strategy
+ run_strategy unless just_watch
start_observer
end
# Run strategy based on the choosed ruby strategy.
#
def run_strategy
- # PENDING: run_before_callbacks
+ Base.run_before_callbacks(:all)
+ apply_focus if focus.present?
notify(strategy.run)
- # PENDING: run_after_callbacks
+ Base.run_after_callbacks(:all)
+ ensure
+ clear_focus
+ end
+
+ # Apply focus filter to test files
+ #
+ def apply_focus
+ case focus
+ when :failures
+ test_framework.test_files = last_failed_files if last_failed_files.present?
+ when String
+ test_framework.test_files = focus if File.exist?(focus)
+ end
+ end
+
+ # Clear focus after running tests
+ #
+ def clear_focus
+ test_framework.test_files = nil
+ end
+
+ # Track last failed test files (stored in .infinity_test_failures)
+ #
+ def last_failed_files
+ failures_file = '.infinity_test_failures'
+ return nil unless File.exist?(failures_file)
+ File.read(failures_file).split("\n").select { |f| File.exist?(f) }.join(' ')
end
# Re run strategy changed the changed files.
diff --git a/lib/infinity_test/core/load_configuration.rb b/lib/infinity_test/core/load_configuration.rb
index 8dd0fd9..e4c838c 100644
--- a/lib/infinity_test/core/load_configuration.rb
+++ b/lib/infinity_test/core/load_configuration.rb
@@ -4,28 +4,28 @@ class LoadConfiguration
attr_accessor :global_file, :project_file
def initialize
- @global_file = File.expand_path('~/.infinity_test')
- @project_file = './.infinity_test'
+ @global_file = File.expand_path('~/INFINITY_TEST')
+ @project_file = './INFINITY_TEST'
@old_configuration = InfinityTest::OldDSL::Configuration
end
# Load the Configuration file
#
- # Command line options can be persisted in a .infinity_test file in a project.
- # You can also store a .infinity_test file in your home directory (~/.infinity_test)
+ # Command line options can be persisted in an INFINITY_TEST file in a project.
+ # You can also store an INFINITY_TEST file in your home directory (~/INFINITY_TEST)
#
# Precedence is:
# command line
- # ./.infinity_test
- # ~/.infinity_test
+ # ./INFINITY_TEST
+ # ~/INFINITY_TEST
#
# Example:
#
- # ~/.infinity_test -> infinity_test { notifications :growl }
+ # ~/INFINITY_TEST -> infinity_test { notifications :osascript }
#
- # ./.infinity_test -> infinity_test { notifications :lib_notify } # High Priority
+ # ./INFINITY_TEST -> infinity_test { notifications :terminal_notifier } # High Priority
#
- # After the load the Notifications Framework will be Lib Notify
+ # After the load the Notifications Framework will be Terminal Notifier
#
def load!
load_global_file!
diff --git a/lib/infinity_test/core/notifier.rb b/lib/infinity_test/core/notifier.rb
index 4705496..921d6c2 100644
--- a/lib/infinity_test/core/notifier.rb
+++ b/lib/infinity_test/core/notifier.rb
@@ -56,4 +56,4 @@ def images_dir
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/core/options.rb b/lib/infinity_test/core/options.rb
index 3ca1f6d..54b4932 100644
--- a/lib/infinity_test/core/options.rb
+++ b/lib/infinity_test/core/options.rb
@@ -3,6 +3,7 @@ module Core
class Options
attr_accessor :arguments, :options_parser, :strategy, :bundler, :verbose
attr_accessor :rubies, :specific_options, :test_framework, :framework, :infinity_and_beyond
+ attr_accessor :notifications, :mode, :just_watch, :focus
def initialize(*arguments)
@arguments = arguments.flatten.clone
@@ -17,7 +18,11 @@ def new_options_parser
options_to_added_in_the_command
test_framework_to_be_run
app_framework
+ notifications_library
+ image_mode
infinity_and_beyond_option
+ just_watch_option
+ focus_option
verbose_mode
skip_bundler
).each do |option_to_parse|
@@ -53,7 +58,7 @@ def options_to_added_in_the_command(option)
end
def test_framework_to_be_run(option)
- option.on('--test library', 'Test Framework to be run. Ex.: auto_discover, rspec, test_unit, bacon.') do |library|
+ option.on('--test library', 'Test Framework to be run. Ex.: auto_discover, rspec, test_unit.') do |library|
@test_framework = library.to_sym
end
end
@@ -64,12 +69,36 @@ def app_framework(option)
end
end
+ def notifications_library(option)
+ option.on('--notifications library', 'Notification library to use. Ex.: auto_discover, osascript, terminal_notifier, notify_send, dunstify.') do |library|
+ @notifications = library.to_sym
+ end
+ end
+
+ def image_mode(option)
+ option.on('--mode theme', 'Image theme for notifications. Ex.: simpson, faces, fuuu, hands, mario_bros, rails, rubies, street_fighter, toy_story.') do |theme|
+ @mode = theme.to_sym
+ end
+ end
+
def infinity_and_beyond_option(option)
option.on('-n', '--no-infinity-and-beyond', 'Run tests and exit. Useful in a Continuous Integration environment.') do
@infinity_and_beyond = false
end
end
+ def just_watch_option(option)
+ option.on('-j', '--just-watch', 'Skip initial test run and only watch for file changes. Useful for large applications.') do
+ @just_watch = true
+ end
+ end
+
+ def focus_option(option)
+ option.on('-f', '--focus [FILE]', 'Focus on specific tests. Use "failures" for failed tests, or provide a file path.') do |file|
+ @focus = file == 'failures' ? :failures : file
+ end
+ end
+
def verbose_mode(option)
option.on('--no-verbose', "Don't print commands before executing them") do
@verbose = false
diff --git a/lib/infinity_test/framework/padrino.rb b/lib/infinity_test/framework/padrino.rb
index 2e1dc2b..aa55dc0 100644
--- a/lib/infinity_test/framework/padrino.rb
+++ b/lib/infinity_test/framework/padrino.rb
@@ -1,7 +1,24 @@
module InfinityTest
module Framework
class Padrino < Base
+ delegate :test_dir, :test_helper_file, to: :test_framework
+
+ # Add Heuristics to the observer run on pattern changes!
+ #
+ # ==== Heuristics
+ # * Watch app folder (models, controllers, helpers, mailers) and run corresponding tests/specs
+ # * Watch lib dir and run corresponding tests
+ # * Watch test/spec dir and run the changed file
+ # * Watch test/spec helper and run all
+ #
def heuristics
+ watch_dir('app/models') { |file| run_test(file) }
+ watch_dir('app/controllers') { |file| run_test(file) }
+ watch_dir('app/helpers') { |file| run_test(file) }
+ watch_dir('app/mailers') { |file| run_test(file) }
+ watch_dir(:lib) { |file| run_test(file) }
+ watch_dir(test_dir) { |file| run_file(file) }
+ watch(test_helper_file) { run_all }
end
# ==== Returns
diff --git a/lib/infinity_test/framework/rails.rb b/lib/infinity_test/framework/rails.rb
index 4d191e0..2671462 100644
--- a/lib/infinity_test/framework/rails.rb
+++ b/lib/infinity_test/framework/rails.rb
@@ -1,10 +1,30 @@
module InfinityTest
module Framework
class Rails < Base
+ delegate :test_dir, :test_helper_file, to: :test_framework
+
+ # Add Heuristics to the observer run on pattern changes!
+ #
+ # ==== Heuristics
+ # * Watch app/models and run corresponding tests/specs
+ # * Watch app/controllers and run corresponding tests/specs
+ # * Watch app/helpers and run corresponding tests/specs
+ # * Watch app/mailers and run corresponding tests/specs
+ # * Watch app/jobs and run corresponding tests/specs
+ # * Watch lib dir and run corresponding tests
+ # * Watch test/spec dir and run the changed file
+ # * Watch test/spec helper and run all
+ # * Watch config/routes.rb and run routing specs
+ #
def heuristics
- # watch_dir('app') { |file| RunTest(file, :dir => :spec) }
- # watch(:Gemfile) { |file| BundleInstall and RunAll }
- # watch(test_framework.test_helper_file) { |file| RunAll }
+ watch_dir('app/models') { |file| run_test(file) }
+ watch_dir('app/controllers') { |file| run_test(file) }
+ watch_dir('app/helpers') { |file| run_test(file) }
+ watch_dir('app/mailers') { |file| run_test(file) }
+ watch_dir('app/jobs') { |file| run_test(file) }
+ watch_dir(:lib) { |file| run_test(file) }
+ watch_dir(test_dir) { |file| run_file(file) }
+ watch(test_helper_file) { run_all }
end
def self.run?
diff --git a/lib/infinity_test/framework/shared_example.rb b/lib/infinity_test/framework/shared_example.rb
index 4d4f648..4e7aa7d 100644
--- a/lib/infinity_test/framework/shared_example.rb
+++ b/lib/infinity_test/framework/shared_example.rb
@@ -14,34 +14,34 @@ module SharedExample
# end
#
shared_examples_for 'an infinity test framework' do
- it 'should respond to #heuristics' do
+ it 'responds to #heuristics' do
expect(subject).to respond_to(:heuristics)
end
- it 'should respond to #heuristics' do
+ it 'responds to #heuristics!' do
expect(subject).to respond_to(:heuristics!)
end
- it 'should respond to .run?' do
+ it 'responds to .run?' do
expect(subject.class).to respond_to(:run?)
end
- it 'should respond to #base' do
+ it 'responds to #base' do
expect(subject).to respond_to(:base)
end
- it 'should respond to #test_framework' do
+ it 'responds to #test_framework' do
expect(subject).to respond_to(:test_framework)
end
- it 'should respond to #strategy' do
+ it 'responds to #strategy' do
expect(subject).to respond_to(:strategy)
end
- it 'should respond to #observer' do
+ it 'responds to #observer' do
expect(subject).to respond_to(:observer)
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/observer/base.rb b/lib/infinity_test/observer/base.rb
index 8c3e1c8..8fc88d6 100644
--- a/lib/infinity_test/observer/base.rb
+++ b/lib/infinity_test/observer/base.rb
@@ -26,17 +26,15 @@ def start
def signal
Signal.trap('INT') do
- if @interrupt
+ if @interrupt_at && (Time.now - @interrupt_at) < 2
puts " To Infinity and Beyond!"
exit
else
puts " Are you sure? :S ... Interrupt a second time to quit!"
- @interrupt = true
- Kernel.sleep 1.5
- @interrupt = false
+ @interrupt_at = Time.now
end
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/observer/filewatcher.rb b/lib/infinity_test/observer/filewatcher.rb
new file mode 100644
index 0000000..1638342
--- /dev/null
+++ b/lib/infinity_test/observer/filewatcher.rb
@@ -0,0 +1,72 @@
+require 'filewatcher'
+
+module InfinityTest
+ module Observer
+ class Filewatcher < Base
+ attr_reader :observer, :watch_paths, :patterns
+
+ def initialize(continuous_test_server)
+ super
+ @watch_paths = []
+ @patterns = {}
+ end
+
+ # Watch a file or pattern for changes.
+ #
+ # ==== Examples
+ #
+ # watch('lib/(.*)\.rb') { |file| puts [file.name, file.path, file.match_data] }
+ # watch('test/test_helper.rb') { run_all() }
+ #
+ def watch(pattern_or_file, &block)
+ pattern = Regexp.new(pattern_or_file.to_s)
+ @patterns[pattern] = block
+ end
+
+ # Watch a directory for changes.
+ #
+ # ==== Examples
+ #
+ # watch_dir(:lib) { |file| run_test(file) }
+ # watch_dir(:test) { |file| run_file(file) }
+ #
+ # watch_dir(:test, :py) { |file| puts [file.name, file.path, file.match_data] }
+ # watch_dir(:test, :js) { |file| puts [file.name, file.path, file.match_data] }
+ #
+ def watch_dir(dir_name, extension = :rb, &block)
+ watch("^#{dir_name}/*/(.*).#{extension}", &block)
+ path = "#{dir_name}/**/*.#{extension}"
+ @watch_paths << path unless @watch_paths.include?(path)
+ end
+
+ # Start the continuous test server.
+ #
+ def start
+ paths = @watch_paths.empty? ? ['**/*.rb'] : @watch_paths
+
+ @observer = ::Filewatcher.new(paths)
+
+ @observer.watch do |changes|
+ changes.each do |file_path, _event|
+ relative_path = file_path.sub("#{Dir.pwd}/", '')
+ handle_file_change(relative_path)
+ end
+ end
+ rescue Interrupt
+ @observer.stop if @observer
+ end
+
+ private
+
+ def handle_file_change(file_path)
+ @patterns.each do |pattern, block|
+ if match_data = pattern.match(file_path)
+ changed_file = InfinityTest::Core::ChangedFile.new(match_data)
+ block.call(changed_file)
+ break
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/infinity_test/observer/listen.rb b/lib/infinity_test/observer/listen.rb
new file mode 100644
index 0000000..89f0e82
--- /dev/null
+++ b/lib/infinity_test/observer/listen.rb
@@ -0,0 +1,74 @@
+require 'listen'
+
+module InfinityTest
+ module Observer
+ class Listen < Base
+ attr_reader :observer, :directories, :patterns
+
+ def initialize(continuous_test_server)
+ super
+ @directories = []
+ @patterns = {}
+ end
+
+ # Watch a file or pattern for changes.
+ #
+ # ==== Examples
+ #
+ # watch('lib/(.*)\.rb') { |file| puts [file.name, file.path, file.match_data] }
+ # watch('test/test_helper.rb') { run_all() }
+ #
+ def watch(pattern_or_file, &block)
+ pattern = Regexp.new(pattern_or_file.to_s)
+ @patterns[pattern] = block
+ end
+
+ # Watch a directory for changes.
+ #
+ # ==== Examples
+ #
+ # watch_dir(:lib) { |file| run_test(file) }
+ # watch_dir(:test) { |file| run_file(file) }
+ #
+ # watch_dir(:test, :py) { |file| puts [file.name, file.path, file.match_data] }
+ # watch_dir(:test, :js) { |file| puts [file.name, file.path, file.match_data] }
+ #
+ def watch_dir(dir_name, extension = :rb, &block)
+ watch("^#{dir_name}/*/(.*).#{extension}", &block)
+ @directories << dir_name.to_s unless @directories.include?(dir_name.to_s)
+ end
+
+ # Start the continuous test server.
+ #
+ def start
+ dirs = @directories.empty? ? ['.'] : @directories
+ dirs = dirs.select { |d| File.directory?(d) }
+ dirs = ['.'] if dirs.empty?
+
+ @observer = ::Listen.to(*dirs) do |modified, added, _removed|
+ (modified + added).each do |file|
+ relative_path = file.sub("#{Dir.pwd}/", '')
+ handle_file_change(relative_path)
+ end
+ end
+
+ @observer.start
+ sleep
+ rescue Interrupt
+ @observer.stop if @observer
+ end
+
+ private
+
+ def handle_file_change(file_path)
+ @patterns.each do |pattern, block|
+ if match_data = pattern.match(file_path)
+ changed_file = InfinityTest::Core::ChangedFile.new(match_data)
+ block.call(changed_file)
+ break
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/infinity_test/observer/shared_example.rb b/lib/infinity_test/observer/shared_example.rb
index 35900ef..159270f 100644
--- a/lib/infinity_test/observer/shared_example.rb
+++ b/lib/infinity_test/observer/shared_example.rb
@@ -14,22 +14,22 @@ module SharedExample
# end
#
shared_examples_for 'an infinity test observer' do
- it 'should respond to #observer' do
+ it 'responds to #observer' do
expect(subject).to respond_to(:observer)
end
- it 'should respond to #watch_dir' do
+ it 'responds to #watch_dir' do
expect(subject).to respond_to(:watch_dir)
end
- it 'should respond to #watch' do
+ it 'responds to #watch' do
expect(subject).to respond_to(:watch)
end
- it 'should respond to #start' do
+ it 'responds to #start' do
expect(subject).to respond_to(:start)
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/observer/watchr.rb b/lib/infinity_test/observer/watchr.rb
deleted file mode 100644
index 760ce41..0000000
--- a/lib/infinity_test/observer/watchr.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'watchr'
-
-module InfinityTest
- module Observer
- class Watchr < Base
- attr_reader :observer
-
- def initialize(continuous_test_server)
- super
- @observer = ::Watchr::Script.new
- end
-
- # ==== Examples
- #
- # watch('lib/(.*)\.rb') { |file| puts [file.name, file.path, file.match_data] }
- # watch('test/test_helper.rb') { run_all() }
- #
- def watch(pattern_or_file, &block)
- @observer.watch(pattern_or_file.to_s) do |match_data|
- block.call(InfinityTest::Core::ChangedFile.new(match_data))
- end
- end
-
- # ==== Examples
- #
- # watch_dir(:lib) { |file| RunTest(file) }
- # watch_dir(:test) { |file| RunFile(file) }
- #
- # watch_dir(:test, :py) { |file| puts [file.name, file.path, file.match_data] }
- # watch_dir(:test, :js) { |file| puts [file.name, file.path, file.match_data] }
- #
- def watch_dir(dir_name, extension = :rb, &block)
- watch("^#{dir_name}/*/(.*).#{extension}", &block)
- end
-
- # Start the continuous test server.
- #
- def start
- @handler = ::Watchr.handler.new
- @controller = ::Watchr::Controller.new(@observer, @handler)
- @controller.run
- end
- end
- end
-end
\ No newline at end of file
diff --git a/lib/infinity_test/strategy/rbenv.rb b/lib/infinity_test/strategy/rbenv.rb
index 9ce5ff5..4f03af0 100644
--- a/lib/infinity_test/strategy/rbenv.rb
+++ b/lib/infinity_test/strategy/rbenv.rb
@@ -1,15 +1,31 @@
module InfinityTest
module Strategy
class Rbenv < Base
+ attr_reader :continuous_test_server
+ delegate :binary, :test_files, to: :continuous_test_server
+
+ # Build and run commands for each ruby version specified.
+ # Uses rbenv's RBENV_VERSION environment variable to run tests in different Ruby environments.
+ #
+ # ==== Returns
+ # String: The command string for all ruby versions joined with &&
+ #
def run!
+ rubies = Core::Base.rubies
+
+ commands = rubies.map do |ruby_version|
+ "RBENV_VERSION=#{ruby_version} #{command_builder.ruby.option(:S).add(binary).add(test_files).to_s}"
+ end
+
+ commands.join(' && ')
end
# ==== Returns
- # TrueClass: If the user had the rbenv installed.
- # FalseClass: If the user don't had the rbenv installed.
+ # TrueClass: If the user has rbenv installed AND has specified rubies to test against.
+ # FalseClass: If rbenv is not installed OR no rubies are specified.
#
def self.run?
- File.exist?(File.expand_path('~/.rbenv'))
+ Core::Base.rubies.present? && File.exist?(File.expand_path('~/.rbenv'))
end
end
end
diff --git a/lib/infinity_test/strategy/rvm.rb b/lib/infinity_test/strategy/rvm.rb
index f65f95f..d19fb81 100644
--- a/lib/infinity_test/strategy/rvm.rb
+++ b/lib/infinity_test/strategy/rvm.rb
@@ -1,18 +1,33 @@
module InfinityTest
module Strategy
class Rvm < Base
+ attr_reader :continuous_test_server
+ delegate :binary, :test_files, to: :continuous_test_server
+
+ # Build and run commands for each ruby version specified.
+ # Uses RVM's `rvm do` syntax to run tests in different Ruby environments.
+ #
+ # ==== Returns
+ # String: The command string for the first ruby version (others run sequentially)
+ #
def run!
- base.rubies.each do |ruby_version|
- command_builder.rvm.do.ruby_version.ruby.option(:S).add(test_framework.binary).add(test_framework.test_dir)
+ rubies = Core::Base.rubies
+ gemset = Core::Base.gemset
+
+ commands = rubies.map do |ruby_version|
+ ruby_with_gemset = gemset.present? ? "#{ruby_version}@#{gemset}" : ruby_version
+ command_builder.rvm.add(ruby_with_gemset).do.ruby.option(:S).add(binary).add(test_files).to_s
end
+
+ commands.join(' && ')
end
# ==== Returns
- # TrueClass: If the user had the rvm installed.
- # FalseClass: If the user don't had the rvm installed.
+ # TrueClass: If the user has RVM installed AND has specified rubies to test against.
+ # FalseClass: If RVM is not installed OR no rubies are specified.
#
def self.run?
- installed_users_home? or installed_system_wide?
+ Core::Base.rubies.present? && (installed_users_home? || installed_system_wide?)
end
# ==== Returns
diff --git a/lib/infinity_test/strategy/shared_example.rb b/lib/infinity_test/strategy/shared_example.rb
index 48131ab..0974965 100644
--- a/lib/infinity_test/strategy/shared_example.rb
+++ b/lib/infinity_test/strategy/shared_example.rb
@@ -15,18 +15,18 @@ module SharedExample
# end
#
shared_examples_for 'a infinity test strategy' do
- it 'should respond to #run!' do
+ it 'responds to #run!' do
expect(subject).to respond_to(:run)
end
- it 'should respond to .run?' do
+ it 'responds to .run?' do
expect(subject.class).to respond_to(:run?)
end
- it 'should have Strategy::Base as superclass' do
+ it 'has Strategy::Base as superclass' do
expect(subject.class.superclass).to be InfinityTest::Strategy::Base
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/test_framework/bacon.rb b/lib/infinity_test/test_framework/bacon.rb
deleted file mode 100644
index d8c46d6..0000000
--- a/lib/infinity_test/test_framework/bacon.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module InfinityTest
- module TestFramework
- class Bacon < Base
- end
- end
-end
\ No newline at end of file
diff --git a/lib/infinity_test/test_framework/rspec.rb b/lib/infinity_test/test_framework/rspec.rb
index 798d4bf..c1eb6f0 100644
--- a/lib/infinity_test/test_framework/rspec.rb
+++ b/lib/infinity_test/test_framework/rspec.rb
@@ -10,7 +10,11 @@ def test_helper_file
end
def test_dir
- 'spec'
+ @test_dir ||= 'spec'
+ end
+
+ def test_dir=(dir)
+ @test_dir = dir
end
def test_files
@@ -28,6 +32,17 @@ def success?
def failure?
@failures > 0
end
+
+ def pending?
+ @pending > 0
+ end
+
+ def self.run?
+ File.exist?('spec') && (
+ File.exist?('spec/spec_helper.rb') ||
+ Dir['spec/**/*_spec.rb'].any?
+ )
+ end
end
end
end
\ No newline at end of file
diff --git a/lib/infinity_test/test_framework/shared_example.rb b/lib/infinity_test/test_framework/shared_example.rb
index 1f4849b..547a518 100644
--- a/lib/infinity_test/test_framework/shared_example.rb
+++ b/lib/infinity_test/test_framework/shared_example.rb
@@ -15,42 +15,42 @@ module SharedExample
# end
#
shared_examples_for 'a infinity test test framework' do
- it 'should respond to #test_message=' do
+ it 'responds to #test_message=' do
expect(subject).to respond_to(:test_message=)
end
- it 'should respond to #test_message' do
+ it 'responds to #test_message' do
expect(subject).to respond_to(:test_message)
end
- it 'should respond to #succeed?' do
+ it 'responds to #succeed?' do
expect(subject).to respond_to(:success?)
end
- it 'should respond to #failure?' do
+ it 'responds to #failure?' do
expect(subject).to respond_to(:failure?)
end
- it 'should respond to #pending?' do
+ it 'responds to #pending?' do
expect(subject).to respond_to(:pending?)
end
- it 'should respond to #test_helper_file' do
+ it 'responds to #test_helper_file' do
expect(subject).to respond_to(:test_helper_file)
end
- it 'should respond to #test_dir' do
+ it 'responds to #test_dir' do
expect(subject).to respond_to(:test_dir)
end
- it 'should respond to #test_dir=' do
+ it 'responds to #test_dir=' do
expect(subject).to respond_to(:test_dir=)
end
- it 'should respond to #binary' do
+ it 'responds to #binary' do
expect(subject).to respond_to(:binary)
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/infinity_test/test_framework/test_unit.rb b/lib/infinity_test/test_framework/test_unit.rb
index f2246ad..b3e6e1c 100644
--- a/lib/infinity_test/test_framework/test_unit.rb
+++ b/lib/infinity_test/test_framework/test_unit.rb
@@ -2,15 +2,55 @@ module InfinityTest
module TestFramework
class TestUnit < Base
def test_dir
- 'test'
+ @test_dir ||= 'test'
+ end
+
+ def test_dir=(dir)
+ @test_dir = dir
end
def test_helper_file
File.join(test_dir, 'test_helper.rb')
end
+ def test_files
+ @test_files || test_dir
+ end
+
def binary
- ''
+ 'ruby'
+ end
+
+ # Patterns for parsing minitest/test-unit output
+ # Example: "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips"
+ def patterns
+ {
+ :runs => /(\d+) runs,/,
+ :assertions => /(\d+) assertions,/,
+ :failures => /(\d+) failures,/,
+ :errors => /(\d+) errors,/,
+ :skips => /(\d+) skips/
+ }
+ end
+
+ def success?
+ @failures.zero? && @errors.zero? && @skips.zero?
+ end
+
+ def failure?
+ @failures > 0 || @errors > 0
+ end
+
+ def pending?
+ @skips > 0
+ end
+
+ def self.run?
+ File.exist?('test') && (
+ File.exist?('test/test_helper.rb') ||
+ Dir['test/**/*_test.rb'].any? ||
+ Dir['test/**/test_*.rb'].any?
+ )
end
end
end
diff --git a/spec/infinity_test/core/auto_discover_spec.rb b/spec/infinity_test/core/auto_discover_spec.rb
index 054921a..5fb657d 100644
--- a/spec/infinity_test/core/auto_discover_spec.rb
+++ b/spec/infinity_test/core/auto_discover_spec.rb
@@ -7,7 +7,7 @@ module InfinityTest
let(:auto_discover) { AutoDiscover.new(base) }
describe '#discover_libraries' do
- it 'discover strategy, framework and test framework' do
+ it 'discovers strategy, framework and test framework' do
expect(auto_discover).to receive(:discover_strategy)
expect(auto_discover).to receive(:discover_framework)
expect(auto_discover).to receive(:discover_test_framework)
@@ -19,11 +19,11 @@ module InfinityTest
context 'when strategy is auto discover' do
before do
base.strategy = :auto_discover
- Strategy::RubyDefault.stub(:run?).and_return(false)
+ allow(Strategy::RubyDefault).to receive(:run?).and_return(false)
expect(Strategy::Rvm).to receive(:run?).and_return(true)
end
- it 'change the base strategy' do
+ it 'changes the base strategy' do
auto_discover.discover_strategy
expect(base.strategy).to be :rvm
end
@@ -32,7 +32,7 @@ module InfinityTest
context 'when strategy is different from auto discover' do
before { base.strategy = :ruby_default }
- it 'change anything' do
+ it 'does not change anything' do
auto_discover.discover_strategy
expect(base.strategy).to be :ruby_default
end
@@ -41,9 +41,9 @@ module InfinityTest
context 'when do not find any strategy' do
before do
base.strategy = :auto_discover
- Strategy::RubyDefault.stub(:run?).and_return(false)
+ allow(Strategy::RubyDefault).to receive(:run?).and_return(false)
expect(Strategy::Rvm).to receive(:run?).and_return(false)
- Strategy::Rbenv.stub(:run?).and_return(false)
+ allow(Strategy::Rbenv).to receive(:run?).and_return(false)
end
it 'raises exception' do
@@ -56,11 +56,11 @@ module InfinityTest
context 'when framework is auto discover' do
before do
base.framework = :auto_discover
- Framework::Rubygems.stub(:run?).and_return(false)
+ allow(Framework::Rubygems).to receive(:run?).and_return(false)
expect(Framework::Rails).to receive(:run?).and_return(true)
end
- it 'change the base framework' do
+ it 'changes the base framework' do
auto_discover.discover_framework
expect(base.framework).to be :rails
end
@@ -69,7 +69,7 @@ module InfinityTest
context 'when framework is different from auto discover' do
before { base.framework = :padrino }
- it 'change anything' do
+ it 'does not change anything' do
auto_discover.discover_framework
expect(base.framework).to be :padrino
end
@@ -80,25 +80,70 @@ module InfinityTest
context 'when framework is auto discover' do
before do
base.test_framework = :auto_discover
- TestFramework::TestUnit.stub(:run?).and_return(false)
- TestFramework::Rspec.stub(:run?).and_return(false)
- expect(TestFramework::Bacon).to receive(:run?).and_return(true)
+ allow(TestFramework::TestUnit).to receive(:run?).and_return(false)
+ expect(TestFramework::Rspec).to receive(:run?).and_return(true)
end
- it 'change the base framework' do
+ it 'changes the base framework' do
auto_discover.discover_test_framework
- expect(base.test_framework).to be :bacon
+ expect(base.test_framework).to be :rspec
end
end
context 'when framework is different from auto discover' do
before { base.test_framework = :test_unit }
- it 'change anything' do
+ it 'does not change anything' do
auto_discover.discover_test_framework
expect(base.test_framework).to be :test_unit
end
end
end
+
+ describe 'PRIORITY' do
+ it 'defines priority order for strategies' do
+ expect(AutoDiscover::PRIORITY[:strategy]).to eq [:rvm, :rbenv, :ruby_default]
+ end
+
+ it 'defines priority order for frameworks' do
+ expect(AutoDiscover::PRIORITY[:framework]).to eq [:rails, :padrino, :rubygems]
+ end
+
+ it 'defines priority order for test frameworks' do
+ expect(AutoDiscover::PRIORITY[:test_framework]).to eq [:rspec, :test_unit]
+ end
+ end
+
+ describe 'priority ordering' do
+ context 'when multiple strategies match' do
+ before do
+ base.strategy = :auto_discover
+ # Both RVM and RubyDefault would match, but RVM has higher priority
+ allow(Strategy::Rvm).to receive(:run?).and_return(true)
+ allow(Strategy::Rbenv).to receive(:run?).and_return(false)
+ allow(Strategy::RubyDefault).to receive(:run?).and_return(true)
+ end
+
+ it 'selects the higher priority strategy (RVM over RubyDefault)' do
+ auto_discover.discover_strategy
+ expect(base.strategy).to be :rvm
+ end
+ end
+
+ context 'when multiple frameworks match' do
+ before do
+ base.framework = :auto_discover
+ # Both Rails and Rubygems would match, but Rails has higher priority
+ allow(Framework::Rails).to receive(:run?).and_return(true)
+ allow(Framework::Padrino).to receive(:run?).and_return(false)
+ allow(Framework::Rubygems).to receive(:run?).and_return(true)
+ end
+
+ it 'selects the higher priority framework (Rails over Rubygems)' do
+ auto_discover.discover_framework
+ expect(base.framework).to be :rails
+ end
+ end
+ end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/core/base_spec.rb b/spec/infinity_test/core/base_spec.rb
index f652f3d..c1bc197 100644
--- a/spec/infinity_test/core/base_spec.rb
+++ b/spec/infinity_test/core/base_spec.rb
@@ -1,10 +1,9 @@
require "spec_helper"
-require 'active_support/core_ext/kernel'
module InfinityTest
describe Base do
describe ".setup" do
- it "should yield self" do
+ it "yields self" do
Base.setup do |config|
expect(config).to be InfinityTest::Base
end
@@ -12,70 +11,108 @@ module InfinityTest
end
describe ".using_bundler?" do
- it "should return the same bundler accessor" do
+ it "returns the same bundler accessor" do
expect(Base.using_bundler?).to equal Base.bundler
end
end
describe "#verbose?" do
- it "should return the same verbose accessor" do
+ it "returns the same verbose accessor" do
expect(Base.verbose?).to equal Base.verbose
end
end
describe ".observer" do
- it "should have watchr as default observer" do
- expect(Base.observer).to equal :watchr
+ it "has listen as default observer" do
+ expect(Base.observer).to equal :listen
end
end
describe ".ignore_test_files" do
- it "should not have test files to ignore as default" do
+ it "does not have test files to ignore as default" do
expect(Base.ignore_test_files).to eql []
end
end
describe "#ignore_test_folders" do
- it "should not ignore test folders as default" do
+ it "does not ignore test folders as default" do
expect(Base.ignore_test_folders).to eql []
end
end
describe ".before" do
- before { pending }
- let(:proc) { Proc.new { 'To Infinity and beyond!' } }
+ after { Base.clear_callbacks! }
- it "should create before callback instance and push to the callback accessor" do
- expect(BeforeCallback).to receive(:new).with(:all, &proc).once.and_return(:foo)
- before_callback = Base.before(:all, &proc)
- expect(before_callback).to be :foo
- expect(Base.callbacks).to be_include before_callback
+ it "creates before callback instance and pushes to the callback accessor" do
+ before_callback = Base.before(:all) { 'To Infinity and beyond!' }
+ expect(before_callback).to be_a Core::Callback
+ expect(before_callback.type).to eq :before
+ expect(before_callback.scope).to eq :all
+ expect(Base.callbacks).to include before_callback
+ end
+
+ it "defaults scope to :all" do
+ before_callback = Base.before { 'test' }
+ expect(before_callback.scope).to eq :all
end
end
describe ".after" do
- before { pending }
- let(:proc) { Proc.new {}}
+ after { Base.clear_callbacks! }
+
+ it "creates after callback instance and pushes to the callback accessor" do
+ after_callback = Base.after(:each_ruby) { 'done' }
+ expect(after_callback).to be_a Core::Callback
+ expect(after_callback.type).to eq :after
+ expect(after_callback.scope).to eq :each_ruby
+ expect(Base.callbacks).to include after_callback
+ end
+ end
+
+ describe ".run_before_callbacks" do
+ after { Base.clear_callbacks! }
+
+ it "runs all before callbacks for the given scope" do
+ results = []
+ Base.before(:all) { results << 'first' }
+ Base.before(:all) { results << 'second' }
+ Base.before(:each_ruby) { results << 'each_ruby' }
+
+ Base.run_before_callbacks(:all)
+ expect(results).to eq ['first', 'second']
+ end
+ end
+
+ describe ".run_after_callbacks" do
+ after { Base.clear_callbacks! }
+
+ it "runs all after callbacks for the given scope" do
+ results = []
+ Base.after(:all) { results << 'first' }
+ Base.after(:all) { results << 'second' }
+
+ Base.run_after_callbacks(:all)
+ expect(results).to eq ['first', 'second']
+ end
+ end
- it "should create before callback instance and push to the callback accessor" do
- expect(AfterCallback).to receive(:new).with(:each, &proc).once.and_return(:foo)
- after_callback = Base.after(:each, &proc)
- expect(after_callback).to be :foo
- expect(Base.callbacks).to be_include after_callback
+ describe ".just_watch" do
+ it "defaults to false" do
+ expect(Base.just_watch).to eq false
end
end
describe ".notifications" do
- it "should set the notification class accessor" do
+ it "sets the notification class accessor" do
silence_stream(STDOUT) do
- Base.notifications(:growl)
- expect(Base.notifications).to be :growl
+ Base.notifications(:osascript)
+ expect(Base.notifications).to be :osascript
end
end
- it "should set the images" do
+ it "sets the images" do
silence_stream(STDOUT) do
- Base.notifications(:growl) do
+ Base.notifications(:osascript) do
show_images :success_image => 'foo', :failure_image => 'bar', :pending_image => 'baz'
end
end
@@ -87,9 +124,9 @@ module InfinityTest
Base.pending_image = nil
end
- it "should set the mode" do
+ it "sets the mode" do
silence_stream(STDOUT) do
- Base.notifications(:growl) do
+ Base.notifications(:osascript) do
show_images :mode => :mortal_kombat
end
end
@@ -116,42 +153,42 @@ module InfinityTest
Base.gemset = @gemset
end
- it "should set the rubies" do
+ it "sets the rubies" do
silence_stream(STDOUT) do
Base.use :rubies => %w(foo bar)
end
expect(Base.rubies).to eql %w(foo bar)
end
- it "should set the specific options" do
+ it "sets the specific options" do
silence_stream(STDOUT) do
Base.use :specific_options => '-J -Ilib -Itest'
end
expect(Base.specific_options).to eql '-J -Ilib -Itest'
end
- it "should set the test framework" do
+ it "sets the test framework" do
silence_stream(STDOUT) do
Base.use :test_framework => :rspec
end
expect(Base.test_framework).to be :rspec
end
- it "should set the app framework" do
+ it "sets the app framework" do
silence_stream(STDOUT) do
Base.use :app_framework => :rails
end
expect(Base.framework).to be :rails
end
- it "should set the verbose mode" do
+ it "sets the verbose mode" do
silence_stream(STDOUT) do
Base.use :verbose => false
end
- expect(Base.verbose).to equal false # I choose to don't use should be_false
+ expect(Base.verbose).to equal false
end
- it "should set the gemset" do
+ it "sets the gemset" do
silence_stream(STDOUT) do
Base.use :gemset => 'infinity_test'
end
@@ -160,14 +197,14 @@ module InfinityTest
end
describe ".heuristics" do
- it "should need to see." do
- pending 'Need to see what to do with the patterns to watch'
+ it "accepts a block" do
+ expect { Base.heuristics {} }.to_not raise_exception
end
end
describe ".replace_patterns" do
- it "should need to see" do
- pending 'Need to see how improve this method.'
+ it "accepts a block" do
+ expect { Base.replace_patterns {} }.to_not raise_exception
end
end
@@ -175,7 +212,7 @@ module InfinityTest
let(:options) { Object.new }
let(:configuration_merge) { Object.new }
- it "should call merge on the configuration merge object" do
+ it "calls merge on the configuration merge object" do
expect(ConfigurationMerge).to receive(:new).with(Core::Base, options).and_return(configuration_merge)
expect(configuration_merge).to receive(:merge!)
Core::Base.merge!(options)
@@ -183,7 +220,7 @@ module InfinityTest
end
describe ".clear" do
- it "should call clear_terminal method" do
+ it "calls clear_terminal method" do
silence_stream(STDOUT) do
expect(Base).to receive(:clear_terminal).and_return(true)
Base.clear(:terminal)
@@ -192,7 +229,7 @@ module InfinityTest
end
describe ".clear_terminal" do
- it "should call system clear" do
+ it "calls system clear" do
silence_stream(STDOUT) do
expect(Base).to receive(:system).with('clear').and_return(true)
Base.clear_terminal
@@ -200,4 +237,4 @@ module InfinityTest
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/core/callback_spec.rb b/spec/infinity_test/core/callback_spec.rb
new file mode 100644
index 0000000..47bed18
--- /dev/null
+++ b/spec/infinity_test/core/callback_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+module InfinityTest
+ module Core
+ describe Callback do
+ describe '#initialize' do
+ it 'creates a before callback with default scope' do
+ callback = Callback.new(:before) { 'test' }
+ expect(callback.type).to eq :before
+ expect(callback.scope).to eq :all
+ end
+
+ it 'creates an after callback with custom scope' do
+ callback = Callback.new(:after, :each_ruby) { 'test' }
+ expect(callback.type).to eq :after
+ expect(callback.scope).to eq :each_ruby
+ end
+
+ it 'raises error for invalid scope' do
+ expect { Callback.new(:before, :invalid) { 'test' } }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe '#call' do
+ it 'executes the block without arguments' do
+ result = nil
+ callback = Callback.new(:before) { result = 'executed' }
+ callback.call
+ expect(result).to eq 'executed'
+ end
+
+ it 'executes the block with environment argument' do
+ result = nil
+ callback = Callback.new(:before, :each_ruby) { |env| result = env[:ruby_version] }
+ callback.call(ruby_version: '3.0.0')
+ expect(result).to eq '3.0.0'
+ end
+ end
+
+ describe '#before?' do
+ it 'returns true for before callbacks' do
+ callback = Callback.new(:before) { 'test' }
+ expect(callback).to be_before
+ end
+
+ it 'returns false for after callbacks' do
+ callback = Callback.new(:after) { 'test' }
+ expect(callback).not_to be_before
+ end
+ end
+
+ describe '#after?' do
+ it 'returns true for after callbacks' do
+ callback = Callback.new(:after) { 'test' }
+ expect(callback).to be_after
+ end
+
+ it 'returns false for before callbacks' do
+ callback = Callback.new(:before) { 'test' }
+ expect(callback).not_to be_after
+ end
+ end
+
+ describe '#all?' do
+ it 'returns true for all scope' do
+ callback = Callback.new(:before, :all) { 'test' }
+ expect(callback).to be_all
+ end
+
+ it 'returns false for each_ruby scope' do
+ callback = Callback.new(:before, :each_ruby) { 'test' }
+ expect(callback).not_to be_all
+ end
+ end
+
+ describe '#each_ruby?' do
+ it 'returns true for each_ruby scope' do
+ callback = Callback.new(:before, :each_ruby) { 'test' }
+ expect(callback).to be_each_ruby
+ end
+
+ it 'returns false for all scope' do
+ callback = Callback.new(:before, :all) { 'test' }
+ expect(callback).not_to be_each_ruby
+ end
+ end
+ end
+ end
+end
diff --git a/spec/infinity_test/core/command_builder_spec.rb b/spec/infinity_test/core/command_builder_spec.rb
index 5139a22..4c0538a 100644
--- a/spec/infinity_test/core/command_builder_spec.rb
+++ b/spec/infinity_test/core/command_builder_spec.rb
@@ -4,35 +4,35 @@ module InfinityTest
module Core
describe CommandBuilder do
describe "#add" do
- it "should add the argument in the command" do
+ it "adds the argument in the command" do
expect(subject.ruby.option(:S).add(:rspec).add(:spec)).to eq 'ruby -S rspec spec'
end
end
describe "#option" do
- it "should put options with one dash and be possible to add more keywords" do
+ it "puts options with one dash and allows adding more keywords" do
expect(subject.bundle.exec.ruby.option(:S).rspec).to eq 'bundle exec ruby -S rspec'
end
end
describe "#opt" do
- it "should put double dash options and be possible to add keywords" do
+ it "puts double dash options and allows adding keywords" do
expect(subject.ruby.opt(:copyright)).to eq 'ruby --copyright'
end
end
describe '#method_missing' do
- it "should put space after every keyword" do
+ it "puts space after every keyword" do
expect(subject.bundle.exec.ruby.some_file).to eq 'bundle exec ruby some_file'
end
end
describe "#respond_to?" do
- it "should respond to enything because missing methods will build the command" do
- expect(subject.respond_to?(:foo)).to be_true
- expect(subject.respond_to?(:bar)).to be_true
+ it "responds to anything because missing methods will build the command" do
+ expect(subject.respond_to?(:foo)).to be true
+ expect(subject.respond_to?(:bar)).to be true
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/core/configuration_merge_spec.rb b/spec/infinity_test/core/configuration_merge_spec.rb
index 5af7e98..2762797 100644
--- a/spec/infinity_test/core/configuration_merge_spec.rb
+++ b/spec/infinity_test/core/configuration_merge_spec.rb
@@ -21,99 +21,99 @@ module Core
subject { ConfigurationMerge.new(base, options) }
describe "#merge!" do
- it "should keep the strategy when options strategy is blank" do
+ it "keeps the strategy when options strategy is blank" do
subject.merge!
expect(base.strategy).to be :auto_discover
end
- it "should merge the strategies" do
+ it "merges the strategies" do
expect(options).to receive(:strategy).twice.and_return(:rbenv)
subject.merge!
expect(base.strategy).to be :rbenv
end
- it "should keep the rubies when options rubies is nil" do
+ it "keeps the rubies when options rubies is nil" do
base.rubies = %w(jruby ree)
expect(options).to receive(:rubies).and_return(nil)
subject.merge!
expect(base.rubies).to eq %w(jruby ree)
end
- it "should overwrite the rubies when options rubies is empty" do
+ it "overwrites the rubies when options rubies is empty" do
base.rubies = %w(jruby ree)
expect(options).to receive(:rubies).twice.and_return([])
subject.merge!
expect(base.rubies).to be_blank
end
- it "should merge the rubies" do
+ it "merges the rubies" do
expect(options).to receive(:rubies).twice.and_return(%w(ree jruby))
subject.merge!
expect(base.rubies).to eql %w(ree jruby)
end
- it "should keep the test framework when options test framework is blank" do
+ it "keeps the test framework when options test framework is blank" do
subject.merge!
expect(base.test_framework).to be :auto_discover
end
- it "should merge the test library" do
+ it "merges the test library" do
expect(options).to receive(:test_framework).twice.and_return(:rspec)
subject.merge!
expect(base.test_framework).to be :rspec
end
- it "should keep the framework when options framework is blank" do
+ it "keeps the framework when options framework is blank" do
subject.merge!
expect(base.framework).to be :auto_discover
end
- it "should merge the framework" do
+ it "merges the framework" do
expect(options).to receive(:framework).twice.and_return(:rails)
subject.merge!
expect(base.framework).to be :rails
end
- it "should keep the specific option when options specific is blank" do
+ it "keeps the specific option when options specific is blank" do
subject.merge!
expect(base.specific_options).to eql ''
end
- it "should merge the specific options" do
+ it "merges the specific options" do
expect(options).to receive(:specific_options).twice.and_return('-J -Ilib -Itest')
subject.merge!
expect(base.specific_options).to eql '-J -Ilib -Itest'
end
- it "should merge with the infinity test and beyond" do
+ it "merges with the infinity test and beyond" do
expect(options).to receive(:infinity_and_beyond).twice.and_return(false)
subject.merge!
expect(base.infinity_and_beyond).to be false
end
- it "should keep the base default if option infinity test and beyond is nil" do
+ it "keeps the base default if option infinity test and beyond is nil" do
expect(options).to receive(:infinity_and_beyond).and_return(nil)
subject.merge!
expect(base.infinity_and_beyond).to be true
end
- it "should keep the verbose mode when verbose mode is blank" do
+ it "keeps the verbose mode when verbose mode is blank" do
subject.merge!
- expect(base.verbose).to be_true
+ expect(base.verbose).to be true
end
- it "should merge the verbose mode" do
+ it "merges the verbose mode" do
expect(options).to receive(:verbose).twice.and_return(false)
subject.merge!
expect(base.verbose).to be false
end
- it "should keep the bundler default when bundler is blank" do
+ it "keeps the bundler default when bundler is blank" do
subject.merge!
expect(base.bundler).to be true
end
- it "should merge the verbose mode" do
+ it "merges the bundler mode" do
expect(options).to receive(:bundler).twice.and_return(false)
subject.merge!
expect(base.bundler).to be false
@@ -121,4 +121,4 @@ module Core
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/core/continuous_test_server_spec.rb b/spec/infinity_test/core/continuous_test_server_spec.rb
index dee5a51..607c2d6 100644
--- a/spec/infinity_test/core/continuous_test_server_spec.rb
+++ b/spec/infinity_test/core/continuous_test_server_spec.rb
@@ -7,7 +7,7 @@ module Core
let(:continuous_test_server) { ContinuousTestServer.new(base) }
describe '#start!' do
- it 'run strategy, start observer' do
+ it 'runs strategy and starts observer' do
expect(continuous_test_server).to receive(:run_strategy)
expect(continuous_test_server).to receive(:start_observer)
continuous_test_server.start
@@ -18,11 +18,11 @@ module Core
context 'when base configuration as infinity and beyond' do
before do
expect(base).to receive(:infinity_and_beyond).and_return(true)
- base.stub(:framework).and_return(:rails)
- base.stub(:observer).and_return(:watchr)
+ allow(base).to receive(:framework).and_return(:rails)
+ allow(base).to receive(:observer).and_return(:listen)
end
- it 'add framework heuristics and start the observer' do
+ it 'adds framework heuristics and starts the observer' do
expect(continuous_test_server.framework).to receive(:heuristics!)
expect(continuous_test_server.observer).to receive(:start!)
continuous_test_server.start_observer
@@ -32,10 +32,10 @@ module Core
context 'when base configuration is not infinity and beyond' do
before do
expect(base).to receive(:infinity_and_beyond).and_return(false)
- base.stub(:framework).and_return(:rails)
+ allow(base).to receive(:framework).and_return(:rails)
end
- it 'do not start the observer' do
+ it 'does not start the observer' do
expect(continuous_test_server.framework).to_not receive(:heuristics!)
expect(continuous_test_server.observer).to_not receive(:start!)
continuous_test_server.start_observer
@@ -49,14 +49,14 @@ module Core
context 'when have notification library' do
let(:test_framework) { double }
- let(:base) { double(notifications: :growl) }
+ let(:base) { double(notifications: :auto_discover) }
before do
expect(continuous_test_server).to receive(:test_framework).exactly(:twice).and_return(test_framework)
expect(test_framework).to receive(:test_message=).with(test_message)
end
- it 'instantiante a notifier passing the strategy result and the continuous server' do
+ it 'instantiates a notifier passing the strategy result and the continuous server' do
expect(Core::Notifier).to receive(:new).and_return(double(notify: true))
continuous_test_server.notify(test_message)
end
@@ -65,7 +65,7 @@ module Core
context 'when do not have notification library' do
let(:base) { double(notifications: nil) }
- it 'instantiante a notifier passing the strategy result and the continuous server' do
+ it 'does not instantiate a notifier' do
expect(Core::Notifier).to_not receive(:new)
continuous_test_server.notify(test_message)
end
@@ -79,7 +79,7 @@ module Core
expect(continuous_test_server).to receive(:test_framework).twice.and_return(test_framework)
end
- it 'run strategy agains and ensure the test files is nil after' do
+ it 'runs strategy again and ensures the test files is nil after' do
expect(test_framework).to receive(:test_files=).twice
expect(continuous_test_server).to receive(:run_strategy)
continuous_test_server.rerun_strategy('base_test.py')
@@ -113,4 +113,4 @@ module Core
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/core/load_configuration_spec.rb b/spec/infinity_test/core/load_configuration_spec.rb
index 4ecdfb8..dc7a5d1 100644
--- a/spec/infinity_test/core/load_configuration_spec.rb
+++ b/spec/infinity_test/core/load_configuration_spec.rb
@@ -3,7 +3,7 @@
module InfinityTest
describe LoadConfiguration do
describe "#load!" do
- it "should load global file and project file" do
+ it "loads global file and project file" do
expect(subject).to receive(:load_global_file!).and_return(true)
expect(subject).to receive(:load_project_file!).and_return(true)
subject.load!
@@ -11,7 +11,7 @@ module InfinityTest
end
describe "#load_global_file!" do
- it "should load the global file" do
+ it "loads the global file" do
subject.global_file = 'foo'
expect(subject).to receive(:load_file).with('foo').and_return(true)
subject.load_global_file!
@@ -19,7 +19,7 @@ module InfinityTest
end
describe "#load_project_file!" do
- it "should load the project file" do
+ it "loads the project file" do
subject.project_file = 'bar'
expect(subject).to receive(:load_file).with('bar').and_return(true)
subject.load_project_file!
@@ -27,17 +27,17 @@ module InfinityTest
end
describe "#load_file" do
- it "should load if file exist" do
+ it "loads if file exists" do
expect(File).to receive(:exist?).with('bar').and_return(true)
expect(subject).to receive(:load).with('bar').and_return(true)
- expect(subject.load_file('bar')).to be_true
+ expect(subject.load_file('bar')).to be true
end
- it "should not load if file dont exist" do
+ it "does not load if file does not exist" do
expect(File).to receive(:exist?).with('baz').and_return(false)
expect(subject).to_not receive(:load)
subject.load_file('baz')
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/core/notifier_spec.rb b/spec/infinity_test/core/notifier_spec.rb
index c1e2fe0..2097185 100644
--- a/spec/infinity_test/core/notifier_spec.rb
+++ b/spec/infinity_test/core/notifier_spec.rb
@@ -5,7 +5,7 @@ module Core
describe Notifier do
let(:test_framework) { double }
- subject(:notifier) { Notifier.new(test_framework: test_framework, library: :growl) }
+ subject(:notifier) { Notifier.new(test_framework: test_framework, library: :auto_discover) }
describe '#image' do
before do
@@ -47,7 +47,7 @@ module Core
describe '#library' do
it 'returns the primitive value' do
- expect(notifier.library).to be :growl
+ expect(notifier.library).to be :auto_discover
end
end
@@ -126,6 +126,26 @@ module Core
end
end
end
+
+ describe '#notify' do
+ let(:notification_builder) { double }
+
+ before do
+ expect(Core::Base).to receive(:mode).and_return(:simpson)
+ expect(test_framework).to receive(:success?).and_return(true)
+ expect(test_framework).to receive(:test_message).and_return('5 examples, 0 failures')
+ end
+
+ it 'sends a notification with title, message and image' do
+ expect(notifier).to receive(:auto_discover).and_return(notification_builder)
+ expect(notification_builder).to receive(:title).with(RUBY_VERSION).and_return(notification_builder)
+ expect(notification_builder).to receive(:message).with('5 examples, 0 failures').and_return(notification_builder)
+ expect(notification_builder).to receive(:image).and_return(notification_builder)
+ expect(notification_builder).to receive(:notify)
+
+ notifier.notify
+ end
+ end
end
end
end
\ No newline at end of file
diff --git a/spec/infinity_test/core/options_spec.rb b/spec/infinity_test/core/options_spec.rb
index e6dad18..630ee67 100644
--- a/spec/infinity_test/core/options_spec.rb
+++ b/spec/infinity_test/core/options_spec.rb
@@ -4,101 +4,165 @@ module InfinityTest
describe Options do
describe "#parse!" do
describe "#strategy" do
- it "should parse the --ruby options with rvm" do
+ it "parses the --ruby options with rvm" do
expect(parse('--ruby', 'rvm').strategy).to be :rvm
end
- it "should parse the --ruby options with rbenv" do
+ it "parses the --ruby options with rbenv" do
expect(parse('--ruby', 'rbenv').strategy).to be :rbenv
end
end
describe "#rubies" do
- it "should pass the ruby versions" do
+ it "passes the ruby versions" do
expect(parse('--rubies=ree,jruby').rubies).to eql %w(ree jruby)
end
- it "should have empty rubies when pass rubies= without versions" do
+ it "has empty rubies when pass rubies= without versions" do
expect(parse('--rubies=').rubies).to eql []
end
- it "should be nil when don't pass the rubies option" do
+ it "is nil when rubies option is not passed" do
expect(parse.rubies).to be_nil
end
end
describe "#infinity_and_beyond" do
- it "should return false when setting --no-infinity-and-beyond" do
+ it "returns false when setting --no-infinity-and-beyond" do
expect(parse('--no-infinity-and-beyond').infinity_and_beyond).to equal false
expect(parse('-n').infinity_and_beyond).to equal false
end
- it "should return nil when not setting the --no-infinity-and-beyond" do
+ it "returns nil when not setting the --no-infinity-and-beyond" do
expect(parse.infinity_and_beyond).to be_nil
end
end
describe "#specific_options" do
- it "should parse the options" do
+ it "parses the options" do
expect(parse('--options=-J-Ilib-Itest').specific_options).to eql '-J -Ilib -Itest'
end
end
describe "#test_framework" do
- it "should parse the test framework as rspec" do
+ it "parses the test framework as rspec" do
expect(parse('--test', 'rspec').test_framework).to be :rspec
end
- it "should parse the test framework as bacon" do
- expect(parse('--test', 'bacon').test_framework).to be :bacon
- end
-
- it "should parse the test framework as test_unit" do
+ it "parses the test framework as test_unit" do
expect(parse('--test', 'test_unit').test_framework).to be :test_unit
end
- it "should parse the test framework as other" do
+ it "parses the test framework as other" do
expect(parse('--test', 'other').test_framework).to be :other
end
end
describe "#framework" do
- it "should parse the app framework as rails" do
+ it "parses the app framework as rails" do
expect(parse('--framework', 'rails').framework).to be :rails
end
- it "should parse the app framework as rubygems" do
+ it "parses the app framework as rubygems" do
expect(parse('--framework', 'rubygems').framework).to be :rubygems
end
- it "should parse the app framework as other" do
+ it "parses the app framework as other" do
expect(parse('--framework', 'other').framework).to be :other
end
end
describe "#verbose?" do
- it "should return nil when dont pass nothing" do
+ it "returns nil when nothing is passed" do
expect(parse.verbose?).to be_nil
end
- it "should not be verbose whe pass the option --no-verbose" do
+ it "is not verbose when passing the option --no-verbose" do
expect(parse('--no-verbose')).not_to be_verbose
end
end
describe "#bundler?" do
- it "should not use bundler when passing this option" do
+ it "does not use bundler when passing this option" do
expect(parse('--no-bundler')).not_to be_bundler
end
- it "should return nil when dont pass nothing" do
+ it "returns nil when nothing is passed" do
expect(parse.bundler?).to be_nil
end
end
+
+ describe "#notifications" do
+ it "parses the notifications library as auto_discover" do
+ expect(parse('--notifications', 'auto_discover').notifications).to be :auto_discover
+ end
+
+ it "parses the notifications library as osascript" do
+ expect(parse('--notifications', 'osascript').notifications).to be :osascript
+ end
+
+ it "parses the notifications library as terminal_notifier" do
+ expect(parse('--notifications', 'terminal_notifier').notifications).to be :terminal_notifier
+ end
+
+ it "returns nil when nothing is passed" do
+ expect(parse.notifications).to be_nil
+ end
+ end
+
+ describe "#mode" do
+ it "parses the image mode as simpson" do
+ expect(parse('--mode', 'simpson').mode).to be :simpson
+ end
+
+ it "parses the image mode as faces" do
+ expect(parse('--mode', 'faces').mode).to be :faces
+ end
+
+ it "parses the image mode as rails" do
+ expect(parse('--mode', 'rails').mode).to be :rails
+ end
+
+ it "returns nil when nothing is passed" do
+ expect(parse.mode).to be_nil
+ end
+ end
+
+ describe "#just_watch" do
+ it "returns true when setting --just-watch" do
+ expect(parse('--just-watch').just_watch).to eq true
+ end
+
+ it "returns true when setting -j" do
+ expect(parse('-j').just_watch).to eq true
+ end
+
+ it "returns nil when nothing is passed" do
+ expect(parse.just_watch).to be_nil
+ end
+ end
+
+ describe "#focus" do
+ it "parses --focus with file path" do
+ expect(parse('--focus', 'spec/models/user_spec.rb').focus).to eq 'spec/models/user_spec.rb'
+ end
+
+ it "parses -f with file path" do
+ expect(parse('-f', 'spec/models/user_spec.rb').focus).to eq 'spec/models/user_spec.rb'
+ end
+
+ it "parses --focus failures as symbol" do
+ expect(parse('--focus', 'failures').focus).to eq :failures
+ end
+
+ it "returns nil when nothing is passed" do
+ expect(parse.focus).to be_nil
+ end
+ end
end
def parse(*args)
InfinityTest::Options.new(args.flatten).parse!
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/framework/padrino_spec.rb b/spec/infinity_test/framework/padrino_spec.rb
index 45ccdf0..12afd87 100644
--- a/spec/infinity_test/framework/padrino_spec.rb
+++ b/spec/infinity_test/framework/padrino_spec.rb
@@ -3,25 +3,34 @@
module InfinityTest
module Framework
describe Padrino do
- subject { Padrino.new(Core::Base) }
+ let(:observer) { double('Observer') }
+ let(:test_framework) { double('TestFramework') }
+ let(:continuous_test_server) { double('ContinuousTestServer', observer: observer, test_framework: test_framework) }
+ subject { Padrino.new(continuous_test_server) }
+
describe "#heuristics" do
- before { pending }
- it "should add heuristics" do
+ it "adds heuristics" do
+ # 6 watch_dir calls: models, controllers, helpers, mailers, lib, test_dir
+ expect(observer).to receive(:watch_dir).exactly(6)
+ # 1 watch call for test_helper_file
+ expect(observer).to receive(:watch)
+ expect(test_framework).to receive(:test_helper_file)
+ expect(test_framework).to receive(:test_dir)
expect { subject.heuristics }.to_not raise_exception
end
end
describe ".run?" do
- it "should return true if find the config/apps.rb" do
+ it "returns true if config/apps.rb exists" do
expect(File).to receive(:exist?).with(File.expand_path('./config/apps.rb')).and_return(true)
expect(Padrino).to be_run
end
- it "should return false if don't find the config/apps.rb" do
+ it "returns false if config/apps.rb does not exist" do
expect(File).to receive(:exist?).with(File.expand_path('./config/apps.rb')).and_return(false)
expect(Padrino).not_to be_run
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/framework/rails_spec.rb b/spec/infinity_test/framework/rails_spec.rb
index 1b72af5..4312afb 100644
--- a/spec/infinity_test/framework/rails_spec.rb
+++ b/spec/infinity_test/framework/rails_spec.rb
@@ -3,27 +3,34 @@
module InfinityTest
module Framework
describe Rails do
- subject { Rails.new(Core::Base) }
+ let(:observer) { double('Observer') }
+ let(:test_framework) { double('TestFramework') }
+ let(:continuous_test_server) { double('ContinuousTestServer', observer: observer, test_framework: test_framework) }
+ subject { Rails.new(continuous_test_server) }
describe "#heuristics" do
- before { pending }
-
- it "should add heuristics" do
+ it "adds heuristics" do
+ # 6 watch_dir calls: models, controllers, helpers, mailers, jobs, lib, test_dir
+ expect(observer).to receive(:watch_dir).exactly(7)
+ # 1 watch call for test_helper_file
+ expect(observer).to receive(:watch)
+ expect(test_framework).to receive(:test_helper_file)
+ expect(test_framework).to receive(:test_dir)
expect { subject.heuristics }.to_not raise_exception
end
end
describe ".run?" do
- it "should return true if exist the config/enviroment.rb file" do
+ it "returns true if config/environment.rb exists" do
expect(File).to receive(:exist?).with(File.expand_path('./config/environment.rb')).and_return(true)
expect(Rails).to be_run
end
- it "should return false if don't exist the config/enviroment.rb file" do
+ it "returns false if config/environment.rb does not exist" do
expect(File).to receive(:exist?).with(File.expand_path('./config/environment.rb')).and_return(false)
expect(Rails).not_to be_run
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/framework/rubygems_spec.rb b/spec/infinity_test/framework/rubygems_spec.rb
index dde3e14..32476be 100644
--- a/spec/infinity_test/framework/rubygems_spec.rb
+++ b/spec/infinity_test/framework/rubygems_spec.rb
@@ -9,7 +9,7 @@ module Framework
subject { Rubygems.new(continuous_test_server) }
describe "#heuristics" do
- it "should add heuristics" do
+ it "adds heuristics" do
expect(observer).to receive(:watch_dir).exactly(2)
expect(observer).to receive(:watch)
expect(test_framework).to receive(:test_helper_file)
@@ -19,12 +19,12 @@ module Framework
end
describe ".run?" do
- it "should return true if have a .gemspec in the user current dir" do
+ it "returns true if there is a .gemspec in the user current dir" do
expect(Dir).to receive(:[]).with('*.gemspec').and_return(['infinity_test.gemspec'])
expect(Rubygems).to be_run
end
- it "should return false if don't have a .gemspec in the user current dir" do
+ it "returns false if there is no .gemspec in the user current dir" do
expect(Dir).to receive(:[]).with('*.gemspec').and_return([])
expect(Rubygems).not_to be_run
end
diff --git a/spec/infinity_test/observer/base_spec.rb b/spec/infinity_test/observer/base_spec.rb
new file mode 100644
index 0000000..476d92c
--- /dev/null
+++ b/spec/infinity_test/observer/base_spec.rb
@@ -0,0 +1,78 @@
+require 'spec_helper'
+
+module InfinityTest
+ module Observer
+ describe Base do
+ let(:continuous_server) { double }
+ subject { Base.new(continuous_server) }
+
+ describe "#initialize" do
+ it "sets the continuous_test_server" do
+ expect(subject.continuous_test_server).to eq(continuous_server)
+ end
+ end
+
+ describe "#watch" do
+ it "raises NotImplementedError" do
+ expect { subject.watch('pattern') {} }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe "#watch_dir" do
+ it "raises NotImplementedError" do
+ expect { subject.watch_dir(:lib, :rb) {} }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe "#start" do
+ it "raises NotImplementedError" do
+ expect { subject.start }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe "#start!" do
+ it "calls signal and start" do
+ expect(subject).to receive(:signal)
+ expect(subject).to receive(:start)
+ subject.start!
+ end
+ end
+
+ describe "#signal" do
+ it "traps INT signal" do
+ expect(Signal).to receive(:trap).with('INT')
+ subject.signal
+ end
+
+ context "interrupt handling logic" do
+ let(:handler) do
+ handler_block = nil
+ allow(Signal).to receive(:trap) { |_, &block| handler_block = block }
+ subject.signal
+ handler_block
+ end
+
+ it "shows warning on first interrupt" do
+ expect(subject).to receive(:puts).with(" Are you sure? :S ... Interrupt a second time to quit!")
+ handler.call
+ expect(subject.instance_variable_get(:@interrupt_at)).to be_within(1).of(Time.now)
+ end
+
+ it "exits on second interrupt within 2 seconds" do
+ subject.instance_variable_set(:@interrupt_at, Time.now)
+ expect(subject).to receive(:puts).with(" To Infinity and Beyond!")
+ expect(subject).to receive(:exit)
+ handler.call
+ end
+
+ it "resets timer if second interrupt is after 2 seconds" do
+ subject.instance_variable_set(:@interrupt_at, Time.now - 3)
+ expect(subject).to receive(:puts).with(" Are you sure? :S ... Interrupt a second time to quit!")
+ handler.call
+ expect(subject.instance_variable_get(:@interrupt_at)).to be_within(1).of(Time.now)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/infinity_test/observer/filewatcher_spec.rb b/spec/infinity_test/observer/filewatcher_spec.rb
new file mode 100644
index 0000000..39e73e9
--- /dev/null
+++ b/spec/infinity_test/observer/filewatcher_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+module InfinityTest
+ module Observer
+ describe Filewatcher do
+ let(:continuous_server) { double }
+ subject { Filewatcher.new(continuous_server) }
+
+ it_should_behave_like 'an infinity test observer'
+
+ describe "#patterns" do
+ it "starts as an empty hash" do
+ expect(subject.patterns).to eq({})
+ end
+ end
+
+ describe "#watch_paths" do
+ it "starts as an empty array" do
+ expect(subject.watch_paths).to eq([])
+ end
+ end
+
+ describe "#watch" do
+ it "adds a pattern with its block to patterns" do
+ block = proc { |file| file }
+ subject.watch('lib/(.*)\.rb', &block)
+ expect(subject.patterns.keys.first).to be_a(Regexp)
+ expect(subject.patterns.values.first).to eq(block)
+ end
+ end
+
+ describe "#watch_dir" do
+ it "adds a glob pattern to watch_paths" do
+ subject.watch_dir(:spec)
+ expect(subject.watch_paths).to include('spec/**/*.rb')
+ end
+
+ it "adds a pattern to patterns" do
+ subject.watch_dir(:spec)
+ expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).rb')
+ end
+
+ it "accepts a custom extension" do
+ subject.watch_dir(:spec, :py)
+ expect(subject.watch_paths).to include('spec/**/*.py')
+ expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).py')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/infinity_test/observer/listen_spec.rb b/spec/infinity_test/observer/listen_spec.rb
new file mode 100644
index 0000000..441fa72
--- /dev/null
+++ b/spec/infinity_test/observer/listen_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+module InfinityTest
+ module Observer
+ describe Listen do
+ let(:continuous_server) { double }
+ subject { Listen.new(continuous_server) }
+
+ it_should_behave_like 'an infinity test observer'
+
+ describe "#patterns" do
+ it "starts as an empty hash" do
+ expect(subject.patterns).to eq({})
+ end
+ end
+
+ describe "#directories" do
+ it "starts as an empty array" do
+ expect(subject.directories).to eq([])
+ end
+ end
+
+ describe "#watch" do
+ it "adds a pattern with its block to patterns" do
+ block = proc { |file| file }
+ subject.watch('lib/(.*)\.rb', &block)
+ expect(subject.patterns.keys.first).to be_a(Regexp)
+ expect(subject.patterns.values.first).to eq(block)
+ end
+ end
+
+ describe "#watch_dir" do
+ it "adds a pattern to patterns" do
+ subject.watch_dir(:spec)
+ expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).rb')
+ end
+
+ it "adds the directory to directories" do
+ subject.watch_dir(:spec)
+ expect(subject.directories).to include('spec')
+ end
+
+ it "accepts a custom extension" do
+ subject.watch_dir(:spec, :py)
+ expect(subject.patterns.keys.first.source).to eq('^spec/*/(.*).py')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/infinity_test/observer/watchr_spec.rb b/spec/infinity_test/observer/watchr_spec.rb
deleted file mode 100644
index 3b977a4..0000000
--- a/spec/infinity_test/observer/watchr_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'spec_helper'
-
-module InfinityTest
- module Observer
- describe Watchr do
- let(:continuous_server) { double }
- subject { Watchr.new(continuous_server)}
- it_should_behave_like 'an infinity test observer'
-
- describe "#observer" do
- it "should be instance of watchr script" do
- expect(subject.observer).to be_instance_of(::Watchr::Script)
- end
- end
-
- describe "#watch" do
- it "should pass the args to the observer" do
- expect(subject.observer).to receive(:watch).with('lib')
- subject.watch(:lib)
- end
- end
-
- describe "#watch_dir" do
- it "should pass the pattern to the observer" do
- expect(subject.observer).to receive(:watch).with("^spec/*/(.*).rb")
- subject.watch_dir(:spec)
- end
-
- it "should pass the pattern and the extension to the observer" do
- expect(subject.observer).to receive(:watch).with("^spec/*/(.*).py")
- subject.watch_dir(:spec, :py)
- end
- end
-
- describe "#start" do
- it "should initialize an watchr controller passing the #observer" do
- handler = double
- controller = controller
- expect(::Watchr.handler).to receive(:new).and_return(handler)
- expect(::Watchr::Controller).to receive(:new).with(subject.observer, handler).and_return(controller)
- expect(controller).to receive(:run).and_return(:running)
- expect(subject.start).to equal :running
- end
- end
- end
- end
-end
diff --git a/spec/infinity_test/strategy/rbenv_spec.rb b/spec/infinity_test/strategy/rbenv_spec.rb
index b6d0f54..d1b850d 100644
--- a/spec/infinity_test/strategy/rbenv_spec.rb
+++ b/spec/infinity_test/strategy/rbenv_spec.rb
@@ -3,19 +3,49 @@
module InfinityTest
module Strategy
describe Rbenv do
- subject { Rbenv.new(Core::Base) }
+ let(:base) { BaseFixture.new(test_framework: :rspec) }
+ let(:continuous_test_server) { Core::ContinuousTestServer.new(base) }
+ subject { Rbenv.new(continuous_test_server) }
+
it_should_behave_like 'a infinity test strategy'
describe ".run?" do
let(:rbenv_dir) { File.expand_path('~/.rbenv') }
- it "should return true if the user had the rbenv installed" do
- expect(File).to receive(:exist?).with(rbenv_dir).and_return(true)
- Rbenv.run?
+
+ context "when rubies are specified" do
+ before do
+ allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0'])
+ end
+
+ it "returns true if the user has rbenv installed" do
+ expect(File).to receive(:exist?).with(rbenv_dir).and_return(true)
+ expect(Rbenv).to be_run
+ end
+
+ it "returns false if the user does not have rbenv installed" do
+ expect(File).to receive(:exist?).with(rbenv_dir).and_return(false)
+ expect(Rbenv).not_to be_run
+ end
+ end
+
+ context "when no rubies are specified" do
+ before do
+ allow(Core::Base).to receive(:rubies).and_return([])
+ end
+
+ it "returns false even if rbenv is installed" do
+ expect(Rbenv).not_to be_run
+ end
+ end
+ end
+
+ describe '#run!' do
+ before do
+ allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0'])
end
- it "should return false if the user don't had the rbenv installed" do
- expect(File).to receive(:exist?).with(rbenv_dir).and_return(false)
- Rbenv.run?
+ it 'returns the command for multiple ruby versions' do
+ expect(subject.run!).to eq 'RBENV_VERSION=2.7.0 ruby -S rspec spec && RBENV_VERSION=3.0.0 ruby -S rspec spec'
end
end
end
diff --git a/spec/infinity_test/strategy/ruby_default_spec.rb b/spec/infinity_test/strategy/ruby_default_spec.rb
index da287e7..f134e72 100644
--- a/spec/infinity_test/strategy/ruby_default_spec.rb
+++ b/spec/infinity_test/strategy/ruby_default_spec.rb
@@ -9,13 +9,13 @@ module Strategy
it_should_behave_like 'a infinity test strategy'
describe ".run?" do
- it "should return true when don't pass any ruby versions to run tests" do
- Core::Base.stub(:rubies).and_return([])
+ it "returns true when no ruby versions are passed to run tests" do
+ allow(Core::Base).to receive(:rubies).and_return([])
expect(RubyDefault).to be_run
end
- it "should return false when pass some ruby version to run tests" do
- Core::Base.stub(:rubies).and_return(['ree', 'jruby'])
+ it "returns false when some ruby version is passed to run tests" do
+ allow(Core::Base).to receive(:rubies).and_return(['ree', 'jruby'])
expect(RubyDefault).not_to be_run
end
end
@@ -29,4 +29,4 @@ module Strategy
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/strategy/rvm_spec.rb b/spec/infinity_test/strategy/rvm_spec.rb
index 9b27e13..3000cce 100644
--- a/spec/infinity_test/strategy/rvm_spec.rb
+++ b/spec/infinity_test/strategy/rvm_spec.rb
@@ -3,27 +3,67 @@
module InfinityTest
module Strategy
describe Rvm do
- subject { Rvm.new(Core::Base) }
+ let(:base) { BaseFixture.new(test_framework: :rspec) }
+ let(:continuous_test_server) { Core::ContinuousTestServer.new(base) }
+ subject { Rvm.new(continuous_test_server) }
+
it_should_behave_like 'a infinity test strategy'
describe ".run?" do
- it "should return true if the user had the RVM installed in users home" do
- expect(Rvm).to receive(:installed_users_home?).and_return(true)
- expect(Rvm).to be_run
+ context "when rubies are specified" do
+ before do
+ allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0'])
+ end
+
+ it "returns true if the user has RVM installed in users home" do
+ expect(Rvm).to receive(:installed_users_home?).and_return(true)
+ expect(Rvm).to be_run
+ end
+
+ it "returns true if the user has RVM installed system wide" do
+ expect(Rvm).to receive(:installed_users_home?).and_return(false)
+ expect(Rvm).to receive(:installed_system_wide?).and_return(true)
+ expect(Rvm).to be_run
+ end
+
+ it "returns false if the user does not have RVM installed" do
+ expect(Rvm).to receive(:installed_users_home?).and_return(false)
+ expect(Rvm).to receive(:installed_system_wide?).and_return(false)
+ expect(Rvm).not_to be_run
+ end
end
- it "should return true if the user had the RVM installed in system wid" do
- expect(Rvm).to receive(:installed_users_home?).and_return(false)
- expect(Rvm).to receive(:installed_system_wide?).and_return(true)
- expect(Rvm).to be_run
+ context "when no rubies are specified" do
+ before do
+ allow(Core::Base).to receive(:rubies).and_return([])
+ end
+
+ it "returns false even if RVM is installed" do
+ expect(Rvm).not_to be_run
+ end
end
+ end
+
+ describe '#run!' do
+ before do
+ allow(Core::Base).to receive(:rubies).and_return(['2.7.0', '3.0.0'])
+ allow(Core::Base).to receive(:gemset).and_return(nil)
+ end
+
+ it 'returns the command for multiple ruby versions' do
+ expect(subject.run!).to eq 'rvm 2.7.0 do ruby -S rspec spec && rvm 3.0.0 do ruby -S rspec spec'
+ end
+
+ context 'with gemset' do
+ before do
+ allow(Core::Base).to receive(:gemset).and_return('infinity_test')
+ end
- it "should return false if the user don't had the RVM installed in users home" do
- expect(Rvm).to receive(:installed_users_home?).and_return(false)
- expect(Rvm).to receive(:installed_system_wide?).and_return(false)
- expect(Rvm).not_to be_run
+ it 'includes gemset in the command' do
+ expect(subject.run!).to eq 'rvm 2.7.0@infinity_test do ruby -S rspec spec && rvm 3.0.0@infinity_test do ruby -S rspec spec'
+ end
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/test_framework/bacon_spec.rb b/spec/infinity_test/test_framework/bacon_spec.rb
deleted file mode 100644
index 1be05d2..0000000
--- a/spec/infinity_test/test_framework/bacon_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'spec_helper'
-
-module InfinityTest
- module TestFramework
- describe Bacon do
- it_should_behave_like 'a infinity test test framework'
- end
- end
-end
\ No newline at end of file
diff --git a/spec/infinity_test/test_framework/rspec_spec.rb b/spec/infinity_test/test_framework/rspec_spec.rb
index dd5ba9f..875f041 100644
--- a/spec/infinity_test/test_framework/rspec_spec.rb
+++ b/spec/infinity_test/test_framework/rspec_spec.rb
@@ -6,7 +6,7 @@ module TestFramework
it_should_behave_like 'a infinity test test framework'
describe "#test_dir" do
- it "should return spec as test dir" do
+ it "returns spec as test dir" do
expect(subject.test_dir).to eq 'spec'
end
end
@@ -27,13 +27,13 @@ module TestFramework
end
describe "#test_helper_file" do
- it "should be the spec helper" do
+ it "is the spec helper" do
expect(subject.test_helper_file).to eq 'spec/spec_helper.rb'
end
end
describe "#binary" do
- it "should return rspec as binary" do
+ it "returns rspec as binary" do
expect(subject.binary).to eq 'rspec'
end
end
@@ -116,4 +116,4 @@ module TestFramework
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/infinity_test/test_framework/test_unit_spec.rb b/spec/infinity_test/test_framework/test_unit_spec.rb
index b255b25..c4a73b2 100644
--- a/spec/infinity_test/test_framework/test_unit_spec.rb
+++ b/spec/infinity_test/test_framework/test_unit_spec.rb
@@ -6,20 +6,188 @@ module TestFramework
it_should_behave_like 'a infinity test test framework'
describe "#test_dir" do
- it "should return spec as test dir" do
+ it "returns test as test dir" do
expect(subject.test_dir).to eq 'test'
end
+
+ it "can be set to a custom directory" do
+ subject.test_dir = 'custom_test'
+ expect(subject.test_dir).to eq 'custom_test'
+ end
+ end
+
+ describe '#test_files' do
+ context 'when is empty' do
+ it 'returns the test dir' do
+ expect(subject.test_files).to eq 'test'
+ end
+ end
+
+ context 'when assign the test files' do
+ it 'returns the assigned value' do
+ subject.test_files = 'test/models/user_test.rb'
+ expect(subject.test_files).to eq 'test/models/user_test.rb'
+ end
+ end
end
describe "#test_helper_file" do
- it "should be the spec helper" do
+ it "is the test helper" do
expect(subject.test_helper_file).to eq 'test/test_helper.rb'
end
end
describe '#binary' do
- it 'should see the binary with the strategy instance'
+ it 'returns ruby as binary' do
+ expect(subject.binary).to eq 'ruby'
+ end
+ end
+
+ describe '#test_message' do
+ subject(:test_unit) { TestUnit.new }
+
+ it 'returns the final test results' do
+ test_unit.test_message = "Finished in 0.001s, 1000.0 runs/s.\n10 runs, 15 assertions, 0 failures, 0 errors, 0 skips\n"
+ expect(test_unit.test_message).to eq '10 runs, 15 assertions, 0 failures, 0 errors, 0 skips'
+ end
+ end
+
+ describe '#success?' do
+ subject(:test_unit) { TestUnit.new }
+
+ context 'when test message has ZERO failures, ZERO errors, and ZERO skips' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips"
+ end
+
+ it 'returns true' do
+ expect(test_unit).to be_success
+ end
+ end
+
+ context 'when test message has ONE failure' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 1 failures, 0 errors, 0 skips"
+ end
+
+ it 'returns false' do
+ expect(test_unit).to_not be_success
+ end
+ end
+
+ context 'when test message has ONE error' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 1 errors, 0 skips"
+ end
+
+ it 'returns false' do
+ expect(test_unit).to_not be_success
+ end
+ end
+
+ context 'when test message has ONE skip' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 1 skips"
+ end
+
+ it 'returns false' do
+ expect(test_unit).to_not be_success
+ end
+ end
+ end
+
+ describe '#failure?' do
+ subject(:test_unit) { TestUnit.new }
+
+ context 'when test message has ONE failure' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 1 failures, 0 errors, 0 skips"
+ end
+
+ it 'returns true' do
+ expect(test_unit).to be_failure
+ end
+ end
+
+ context 'when test message has ONE error' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 1 errors, 0 skips"
+ end
+
+ it 'returns true' do
+ expect(test_unit).to be_failure
+ end
+ end
+
+ context 'when test message has ZERO failures and ZERO errors' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips"
+ end
+
+ it 'returns false' do
+ expect(test_unit).to_not be_failure
+ end
+ end
+ end
+
+ describe '#pending?' do
+ subject(:test_unit) { TestUnit.new }
+
+ context 'when test message has ONE skip' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 1 skips"
+ end
+
+ it 'returns true' do
+ expect(test_unit).to be_pending
+ end
+ end
+
+ context 'when test message has ZERO skips' do
+ before do
+ test_unit.test_message = "10 runs, 15 assertions, 0 failures, 0 errors, 0 skips"
+ end
+
+ it 'returns false' do
+ expect(test_unit).to_not be_pending
+ end
+ end
+ end
+
+ describe '.run?' do
+ context 'when test directory exists with test files' do
+ before do
+ allow(File).to receive(:exist?).with('test').and_return(true)
+ allow(File).to receive(:exist?).with('test/test_helper.rb').and_return(true)
+ end
+
+ it 'returns true' do
+ expect(TestUnit.run?).to be true
+ end
+ end
+
+ context 'when test directory exists with *_test.rb files' do
+ before do
+ allow(File).to receive(:exist?).with('test').and_return(true)
+ allow(File).to receive(:exist?).with('test/test_helper.rb').and_return(false)
+ allow(Dir).to receive(:[]).with('test/**/*_test.rb').and_return(['test/user_test.rb'])
+ end
+
+ it 'returns true' do
+ expect(TestUnit.run?).to be true
+ end
+ end
+
+ context 'when test directory does not exist' do
+ before do
+ allow(File).to receive(:exist?).with('test').and_return(false)
+ end
+
+ it 'returns false' do
+ expect(TestUnit.run?).to be false
+ end
+ end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index eb6a0fd..4616ddd 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -34,3 +34,14 @@ def initialize(options={})
@test_framework = options[:test_framework]
end
end
+
+# Replacement for deprecated silence_stream from ActiveSupport
+def silence_stream(stream)
+ old_stream = stream.dup
+ stream.reopen(File::NULL)
+ stream.sync = true
+ yield
+ensure
+ stream.reopen(old_stream)
+ old_stream.close
+end