This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

First commit.

  • Loading branch information...
josevalim committed Oct 29, 2010
0 parents commit 919e6323b0a23776308c214488c376e0f9c39ae3
Showing with 365 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +4 −0 Gemfile
  3. +20 −0 MIT-LICENSE
  4. +13 −0 README.rdoc
  5. +33 −0 Rakefile
  6. +22 −0 hermes.gemspec
  7. +2 −0 lib/hermes.rb
  8. +84 −0 lib/hermes/assertions.rb
  9. +124 −0 lib/hermes/builders.rb
  10. +37 −0 lib/hermes/context.rb
  11. +11 −0 lib/hermes/integration_case.rb
  12. +3 −0 lib/hermes/version.rb
  13. +6 −0 test/test_helper.rb
@@ -0,0 +1,6 @@
.bundle/
log/*.log
pkg/
test/dummy/db/*.sqlite3
test/dummy/log/*.log
test/dummy/tmp/
@@ -0,0 +1,4 @@
source "http://rubygems.org"
gem "activesupport", "3.0.0"
gem "capybara", "0.3.7"
@@ -0,0 +1,20 @@
Copyright 2010 PlataformaTec http://blog.plataformatec.com.br/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
= Hermes
A few testing facilities built on top of Capybara and ActiveSupport::TestCase
== Bugs and Features
If you discover any bugs or add any features, please let us know using GitHub's tracker.
http://github.com/plataformatec/hermes/issues
== License
MIT License. Copyright 2010 Plataforma Tecnologia. http://blog.plataformatec.com.br
@@ -0,0 +1,33 @@
# encoding: UTF-8
require 'rake'
require 'rake/rdoctask'
require 'rake/testtask'
require File.expand_path("../lib/hermes/version", __FILE__)
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
task :default => :test
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'Hermes'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
desc "Build gem"
task :gem do
system "git clean -df"
system "gem build hermes.gemspec"
end
desc "Install gem"
task :install => :gem do
system "gem install *.gem --no-ri --no-rdoc"
end
@@ -0,0 +1,22 @@
# encoding: UTF-8
require File.expand_path("../lib/hermes/version", __FILE__)
Gem::Specification.new do |s|
s.name = "hermes"
s.rubyforge_project = "hermes"
s.version = Hermes::VERSION.dup
s.authors = ["José Valim", "Carlos Antônio da Silva"]
s.email = ["developers@plataformatec.com.br"]
s.description = "A few testing facilities built on top of Capybara and ActiveSupport::TestCase"
s.files = Dir["lib/**/*.rb"] + %w(README.rdoc MIT-LICENSE)
s.test_files = Dir["test/**/*"]
s.extra_rdoc_files = ["README.rdoc"]
s.homepage = "http://github.com/plataformatec/hermes"
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubygems_version = "1.3.6"
s.summary = "A few testing facilities built on top of Capybara and ActiveSupport::TestCase"
end
@@ -0,0 +1,2 @@
module Hermes
end
@@ -0,0 +1,84 @@
module Hermes
# A few assertions built on top of Capybara.
module Assertions
def assert_current_path(expected)
assert_equal expected, current_path
end
def assert_difference_on_click_button(button, *args)
assert_difference *args do
click_button button
end
end
def assert_difference_on_click_link(link, *args)
assert_difference *args do
click_link link
end
end
def assert_content(content)
boolean = has_content?(content)
if boolean
assert_block("assert_content") { true }
else
assert_block("#{body_inner_text}\nExpected to have #{content}"){ false }
end
end
def assert_no_content(content)
boolean = has_content?(content)
if boolean
assert_block("#{body_inner_text}\nDid not expect to have content #{content}"){ false }
else
assert_block("assert_no_content") { true }
end
end
%w(xpath css link select).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def assert_#{method}(*args, &block)
boolean = has_#{method}?(*args, &block)
if boolean
assert_block("assert_#{method}") { true }
else
assert_block("\#{page.body}\nExpected to have #{method} \#{args.inspect}"){ false }
end
end
def assert_no_#{method}(*args, &block)
boolean = has_#{method}?(*args, &block)
if boolean
assert_block("\#{page.body}\nDid not expect to have #{method} \#{args.inspect}"){ false }
else
assert_block("assert_no_#{method}") { true }
end
end
RUBY
end
def assert_mail_deliveries(many=1)
assert_difference("ActionMailer::Base.deliveries.size", many){ yield }
end
def assert_link_to(url, options)
assert_css "a[href='%s']" % url, options
end
def within(scope, prefix=nil)
scope = '#' << ActionController::RecordIdentifier.dom_id(scope, prefix) if scope.is_a?(ActiveRecord::Base)
super(scope)
end
protected
def body_inner_text
Nokogiri::HTML(page.body).inner_text.gsub(/^\s*$/, "").squeeze("\n")
end
# Provide a shortcut to show body inner text. It helps debugging.
def pbit
puts body_inner_text
end
end
end
@@ -0,0 +1,124 @@
# Rails developers have had bad experience with fixtures since a long time
# due to several reasons, including misuse.
#
# This misuse of fixtures is often characterized by a huge ammount of
# fixtures, causing a lot of data to maintain and dependence between tests.
# In my experience working (and rescueing) different applications, 80% of
# these fixtures are used only by 20% of tests.
#
# An example of such tests is a test that assures a given SQL query with
# GROUP BY and ORDER BY conditions returns the correct result set. As expected,
# we need a huge amount of data in this test which we usually don't need in
# order tests.
#
# For such scenarios, factories are a fine solution. They won't cluster
# all your database since they are created for these specific tests and they
# are also easier to maintain.
#
# I believe this was the primary reason for the Rails community to strongly
# adopt factories builders as we saw in the couple two years ago.
#
# However, factories are also misused. It is common to see people creating
# a huge amount of data with factories before each test in their integration
# suite, causing their whole test suite to be slow, while fixtures would
# work great for this purpose.
#
# This is a small attempt to have the best of both worlds.
#
# For the data used in almost all your tests, use fixtures. For all the
# other smaller scenarios, use factories. As both fixtures and factories
# require valid attributes, this code below provides a quick solution
# that allows you to create small, simple factories from the information
# stored in your fixtures.
#
# == Examples
#
# You can define your builder inside the Builders module:
#
# module Hermes::Builders
# build :message do
# { :title => "OMG", :queue => queues(:general) }
# end
# end
#
# It should necessarily return a hash. After defining this builder,
# you can easily create a new message calling +create_message+ or
# +new_message+ in your tests. Both methods accepts an optional
# options parameter that is merged into the given hash.
#
# == Reusing fixtures
#
# The great benefit of builders is that you can reuse your fixtures
# attributes, avoiding duplication. An explicit way of doing it is:
#
# build :message do
# messages(:fixture_one).attributes.merge(
# :title => "Overwritten title"
# )
# end
#
# However, Builders provide an implicit way of doing the same:
#
# build :message, :like => :fixture_one do
# { :title => "Overwritten title" }
# end
#
# == Just Ruby
#
# Since all Builders are defined inside the Builders module, without
# a DSL on top of it, it allows us to use Ruby in case we need to do
# something more complex, like supporting sequences.
#
# module Builders
# @@sequence = 0
#
# def sequence
# @@sequence += 1
# end
# end
#
module Hermes
module Builders
@@builders = ActiveSupport::OrderedHash.new
def self.build(name, options={}, &block)
klass = options[:as] || name.to_s.classify.constantize
builder = if options[:like]
lambda { send(name.to_s.pluralize, options[:like]).attributes.merge(block.call) }
else
block
end
@@builders[name] = [klass, builder]
end
def self.retrieve(scope, name, method, options)
if builder = @@builders[name.to_sym]
klass, block = builder
hash = block.bind(scope).call.merge(options || {})
hash.delete("id")
[klass, hash]
else
raise NoMethodError, "No builder #{name.inspect} for `#{method}'"
end
end
def method_missing(method, *args, &block)
case method.to_s
when /(create|new)_(.*?)(!)?$/
klass, hash = Builders.retrieve(self, $2, method, args.first)
object = klass.new
object.send("attributes=", hash, false)
object.send("save#{$3}") if $1 == "create"
object
when /valid_(.*?)_attributes$/
Builders.retrieve(self, $1, method, args.first)[1]
else
super
end
end
end
end
ActiveSupport::TestCase.send :include, Hermes::Builders
@@ -0,0 +1,37 @@
module Hermes
# Add context support to AS::TestCase.
module Context
def context_list
@@context_list ||= []
end
def context_buffer
@context_buffer ||= []
end
def test(name)
super("#{self.context_buffer.join(" ")} #{name}".strip)
end
def context(name, &block)
klass = Class.new(self)
klass.context_buffer.concat self.context_buffer
klass.context_buffer << name
# Care about Rails tests in nested contexts
# Extracted this from github.com/jm/context
klass.tests($1.constantize) if self < ActiveSupport::TestCase &&
self.name =~ /^(.*(Controller|Helper|Mailer))Test/
# Undefine parent methods so they don't get executed twice
klass.instance_methods.grep(/^test_/).each { |m| klass.send(:undef_method, m) }
klass.class_eval(&block)
self.context_list << klass
Object.const_set("TestContext#{name.camelize.gsub(/\W/,'')}#{klass.object_id.abs}", klass)
klass
end
end
end
ActiveSupport::TestCase.extend Hermes::Context
@@ -0,0 +1,11 @@
require 'hermes/assertions'
class Hermes::IntegrationCase < ActiveSupport::TestCase
include Capybara
include ActionController::RecordIdentifier
include Rails.application.routes.url_helpers
include Hermes::Assertions
teardown { Capybara.reset_sessions! }
class << self; alias :scenario :test; end
end
@@ -0,0 +1,3 @@
module Hermes
VERSION = "0.2.0".freeze
end
@@ -0,0 +1,6 @@
$:.unshift File.expand_path('../../lib', __FILE__)
require 'active_support/test_case'
require 'hermes'
require 'hermes/context'
require 'hermes/integration_case'

0 comments on commit 919e632

Please sign in to comment.