aslakhellesoy-cucumber 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +73 -0
- data/README.txt +78 -0
- data/Rakefile +4 -0
- data/bin/cucumber +3 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +15 -0
- data/examples/Rakefile +7 -0
- data/examples/pure_ruby/addition.rb +16 -0
- data/examples/pure_ruby/steps/addition_steps.rb +43 -0
- data/examples/simple/addition.story +12 -0
- data/examples/simple/division.story +17 -0
- data/examples/simple/steps/addition_steps.rb +43 -0
- data/examples/simple_norwegian/steg/matte_steg.rb.rb +31 -0
- data/examples/simple_norwegian/summering.story +10 -0
- data/examples/web/run_stories.story +10 -0
- data/examples/web/steps/stories_steps.rb +39 -0
- data/gem_tasks/deployment.rake +34 -0
- data/gem_tasks/environment.rake +7 -0
- data/gem_tasks/fix_cr_lf.rake +10 -0
- data/gem_tasks/rspec.rake +21 -0
- data/gem_tasks/treetop.rake +33 -0
- data/gem_tasks/website.rake +17 -0
- data/lib/cucumber.rb +17 -0
- data/lib/cucumber/cli.rb +118 -0
- data/lib/cucumber/core_ext/proc.rb +35 -0
- data/lib/cucumber/core_ext/string.rb +17 -0
- data/lib/cucumber/executor.rb +85 -0
- data/lib/cucumber/formatters.rb +1 -0
- data/lib/cucumber/formatters/ansicolor.rb +89 -0
- data/lib/cucumber/formatters/html_formatter.rb +271 -0
- data/lib/cucumber/formatters/pretty_formatter.rb +66 -0
- data/lib/cucumber/formatters/progress_formatter.rb +41 -0
- data/lib/cucumber/parser/languages.yml +35 -0
- data/lib/cucumber/parser/nodes.rb +88 -0
- data/lib/cucumber/parser/story_parser.rb +13 -0
- data/lib/cucumber/parser/story_parser.treetop.erb +41 -0
- data/lib/cucumber/parser/story_parser_en.rb +554 -0
- data/lib/cucumber/parser/story_parser_fr.rb +554 -0
- data/lib/cucumber/parser/story_parser_no.rb +554 -0
- data/lib/cucumber/parser/story_parser_pt.rb +554 -0
- data/lib/cucumber/parser/top_down_visitor.rb +26 -0
- data/lib/cucumber/rails/world.rb +69 -0
- data/lib/cucumber/rake/task.rb +74 -0
- data/lib/cucumber/ruby_tree.rb +14 -0
- data/lib/cucumber/ruby_tree/nodes.rb +68 -0
- data/lib/cucumber/step_methods.rb +39 -0
- data/lib/cucumber/step_mother.rb +38 -0
- data/lib/cucumber/tree.rb +127 -0
- data/lib/cucumber/version.rb +9 -0
- data/script/console +10 -0
- data/script/console.cmd +1 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/txt2html +74 -0
- data/script/txt2html.cmd +1 -0
- data/setup.rb +1585 -0
- data/spec/cucumber/core_ext/string_spec.rb +20 -0
- data/spec/cucumber/executor_spec.rb +55 -0
- data/spec/cucumber/formatters/ansicolor_spec.rb +18 -0
- data/spec/cucumber/formatters/html_formatter_spec.rb +59 -0
- data/spec/cucumber/formatters/stories.html +274 -0
- data/spec/cucumber/sell_cucumbers.story +9 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +7 -0
- data/website/index.html +11 -0
- data/website/index.txt +39 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.html.erb +48 -0
- metadata +157 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
Story: Run Stories
|
2
|
+
As an information seeker
|
3
|
+
I want to find more information
|
4
|
+
So that I can learn more
|
5
|
+
|
6
|
+
Scenario: Find what I'm looking for
|
7
|
+
Given I am on the search page
|
8
|
+
And I have entered "rspec"
|
9
|
+
When I search
|
10
|
+
Then I should see a link to "RSpec-1.1.3: Overview":http://rspec.info/
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec'
|
2
|
+
|
3
|
+
case PLATFORM
|
4
|
+
when /darwin/
|
5
|
+
require 'safariwatir'
|
6
|
+
Browser = Watir::Safari
|
7
|
+
when /win32/
|
8
|
+
require 'watir'
|
9
|
+
Browser = Watir::IE
|
10
|
+
when /java/
|
11
|
+
require 'celerity'
|
12
|
+
Browser = Celerity::Browser
|
13
|
+
else
|
14
|
+
raise "Can't do web stories on #{PLATFORM}"
|
15
|
+
end
|
16
|
+
|
17
|
+
Before do
|
18
|
+
@b = Browser.new
|
19
|
+
end
|
20
|
+
|
21
|
+
After do
|
22
|
+
@b.close
|
23
|
+
end
|
24
|
+
|
25
|
+
Given 'I am on the search page' do
|
26
|
+
@b.goto 'http://www.google.com/'
|
27
|
+
end
|
28
|
+
|
29
|
+
Given /I have entered "(.*)"/ do |query|
|
30
|
+
@b.text_field(:name, 'q').set(query)
|
31
|
+
end
|
32
|
+
|
33
|
+
When 'I search' do
|
34
|
+
@b.button(:name, 'btnG').click
|
35
|
+
end
|
36
|
+
|
37
|
+
Then /I should see a link to "(.*)":(.*)/ do |text, url|
|
38
|
+
@b.link(:url, url).text.should == text
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
desc 'Release the website and new gem version'
|
2
|
+
task :deploy => [:check_version, :website, :release] do
|
3
|
+
puts "Remember to create SVN tag:"
|
4
|
+
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
5
|
+
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
6
|
+
puts "Suggested comment:"
|
7
|
+
puts "Tagging release #{CHANGES}"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
|
11
|
+
task :local_deploy => [:website_generate, :install_gem]
|
12
|
+
|
13
|
+
task :check_version do
|
14
|
+
unless ENV['VERSION']
|
15
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
unless ENV['VERSION'] == VERS
|
19
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
|
25
|
+
task :install_gem_no_doc => [:clean, :package] do
|
26
|
+
sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
|
27
|
+
end
|
28
|
+
|
29
|
+
namespace :manifest do
|
30
|
+
desc 'Recreate Manifest.txt to include ALL files'
|
31
|
+
task :refresh do
|
32
|
+
`rake check_manifest | patch -p0 > Manifest.txt`
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class StoryCompiler
|
2
|
+
def compile
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
tt = PLATFORM =~ /win32/ ? 'tt.bat' : 'tt'
|
6
|
+
template = ERB.new(IO.read(File.dirname(__FILE__) + '/../lib/cucumber/parser/story_parser.treetop.erb'))
|
7
|
+
langs = YAML.load_file(File.dirname(__FILE__) + '/../lib/cucumber/parser/languages.yml')
|
8
|
+
langs.each do |lang, words|
|
9
|
+
grammar_file = File.dirname(__FILE__) + "/../lib/cucumber/parser/story_parser_#{lang}.treetop"
|
10
|
+
|
11
|
+
STDOUT.write("Generating and compiling grammar for #{lang}...")
|
12
|
+
grammar = template.result(binding)
|
13
|
+
|
14
|
+
# http://groups.google.com/group/treetop-dev/browse_thread/thread/c8a8ced33da07f73
|
15
|
+
# http://github.com/vic/treetop/commit/5cec030a1c363e06783f98e4b45ce6ab121996ab
|
16
|
+
grammar = "module Cucumber\nmodule Parser\n#{grammar}\nend\nend"
|
17
|
+
|
18
|
+
File.open(grammar_file, "wb") do |io|
|
19
|
+
io.write(grammar)
|
20
|
+
end
|
21
|
+
system "#{tt} #{grammar_file}"
|
22
|
+
FileUtils.rm(grammar_file)
|
23
|
+
STDOUT.puts("Done!")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
namespace :treetop do
|
29
|
+
desc 'Compile the grammar'
|
30
|
+
task :compile do
|
31
|
+
StoryCompiler.new.compile
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
desc 'Generate website files'
|
2
|
+
task :website_generate => :ruby_env do
|
3
|
+
(Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt|
|
4
|
+
sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
desc 'Upload website files to rubyforge'
|
9
|
+
task :website_upload do
|
10
|
+
host = "#{rubyforge_username}@rubyforge.org"
|
11
|
+
remote_dir = "/var/www/gforge-projects/#{PATH}/"
|
12
|
+
local_dir = 'website'
|
13
|
+
sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate and upload website files'
|
17
|
+
task :website => [:website_generate, :website_upload, :publish_docs]
|
data/lib/cucumber.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'treetop/runtime'
|
5
|
+
require 'treetop/ruby_extensions'
|
6
|
+
require 'cucumber/version'
|
7
|
+
require 'cucumber/step_methods'
|
8
|
+
require 'cucumber/ruby_tree'
|
9
|
+
require 'cucumber/executor'
|
10
|
+
require 'cucumber/step_mother'
|
11
|
+
require 'cucumber/formatters'
|
12
|
+
require 'cucumber/parser/story_parser'
|
13
|
+
require 'cucumber/cli'
|
14
|
+
|
15
|
+
module Cucumber
|
16
|
+
|
17
|
+
end
|
data/lib/cucumber/cli.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'cucumber'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
class CLI
|
6
|
+
class << self
|
7
|
+
attr_writer :step_mother, :stories
|
8
|
+
|
9
|
+
def execute
|
10
|
+
@execute_called = true
|
11
|
+
parse(ARGV).execute!(@step_mother, @stories)
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute_called?
|
15
|
+
@execute_called
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(args)
|
19
|
+
cli = new(args)
|
20
|
+
cli.parse_options!
|
21
|
+
cli
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(args)
|
26
|
+
@args = args.dup
|
27
|
+
@args.extend(OptionParser::Arguable)
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_options!
|
31
|
+
@options = { :require => nil, :lang => 'en', :format => 'pretty', :dry_run => false }
|
32
|
+
@args.options do |opts|
|
33
|
+
opts.banner = "Usage: cucumber [options] FILES|DIRS"
|
34
|
+
opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR", "Require files before executing the stories.",
|
35
|
+
"If this option is not specified, all *.rb files that",
|
36
|
+
"are siblings or below the stories will be autorequired") do |v|
|
37
|
+
@options[:require] ||= []
|
38
|
+
@options[:require] << v
|
39
|
+
end
|
40
|
+
opts.on("-l LINE", "--line LANG", "Only execute the scenario at the given line") do |v|
|
41
|
+
@options[:line] = v
|
42
|
+
end
|
43
|
+
opts.on("-a LANG", "--language LANG", "Specify language for stories (Default: #{@options[:lang]})") do |v|
|
44
|
+
@options[:lang] = v
|
45
|
+
end
|
46
|
+
opts.on("-f FORMAT", "--format FORMAT", "How to format stories (Default: #{@options[:format]})") do |v|
|
47
|
+
@options[:format] = v
|
48
|
+
end
|
49
|
+
opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.") do
|
50
|
+
@options[:dry_run] = true
|
51
|
+
end
|
52
|
+
end.parse!
|
53
|
+
# Whatever is left after option parsing
|
54
|
+
@files = @args.map do |path|
|
55
|
+
path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
|
56
|
+
File.directory?(path) ? Dir["#{path}/**/*.story"] : path
|
57
|
+
end.flatten
|
58
|
+
end
|
59
|
+
|
60
|
+
def execute!(step_mother, stories)
|
61
|
+
$executor = Executor.new(formatter, step_mother)
|
62
|
+
require_files
|
63
|
+
load_plain_text_stories(stories)
|
64
|
+
$executor.line = @options[:line].to_i if @options[:line]
|
65
|
+
$executor.visit_stories(stories)
|
66
|
+
exit 1 if $executor.failed
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Requires files - typically step files and ruby story files.
|
72
|
+
def require_files
|
73
|
+
require "cucumber/parser/story_parser_#{@options[:lang]}"
|
74
|
+
requires = @options[:require] || @args.map{|f| File.directory?(f) ? f : File.dirname(f)}.uniq
|
75
|
+
libs = requires.map do |path|
|
76
|
+
path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
|
77
|
+
File.directory?(path) ? Dir["#{path}/**/*.rb"] : path
|
78
|
+
end.flatten
|
79
|
+
libs.each do |lib|
|
80
|
+
begin
|
81
|
+
require lib
|
82
|
+
rescue LoadError => e
|
83
|
+
e.message << "\nFailed to load #{lib}"
|
84
|
+
raise e
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def load_plain_text_stories(stories)
|
90
|
+
parser = Parser::StoryParser.new
|
91
|
+
@files.each do |f|
|
92
|
+
stories << Parser::StoryNode.parse(f, parser)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def formatter
|
97
|
+
klass = {
|
98
|
+
'progress' => Formatters::ProgressFormatter,
|
99
|
+
'html' => Formatters::HtmlFormatter,
|
100
|
+
'pretty' => Formatters::PrettyFormatter,
|
101
|
+
}[@options[:format]]
|
102
|
+
klass.new(STDOUT)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Hook the toplevel StepMother to the CLI
|
109
|
+
# TODO: Hook in a RubyStories object on toplevel for pure ruby stories
|
110
|
+
extend Cucumber::StepMethods
|
111
|
+
Cucumber::CLI.step_mother = step_mother
|
112
|
+
|
113
|
+
extend(Cucumber::RubyTree)
|
114
|
+
Cucumber::CLI.stories = stories
|
115
|
+
|
116
|
+
at_exit do
|
117
|
+
Cucumber::CLI.execute unless Cucumber::CLI.execute_called?
|
118
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module CoreExt
|
3
|
+
# Proc extension that allows a proc to be called in the context of any object.
|
4
|
+
# Also makes it possible to tack a name onto a Proc.
|
5
|
+
module CallIn
|
6
|
+
attr_accessor :name
|
7
|
+
|
8
|
+
def call_in(obj, *args, &proc)
|
9
|
+
obj.extend(mod)
|
10
|
+
# raise ArgCountError.new("The #{name} block takes #{arity2} arguments, but there are #{args.length} matched variables") if args.length != arity2
|
11
|
+
obj.__send__(meth, *args, &proc)
|
12
|
+
end
|
13
|
+
|
14
|
+
def arity2
|
15
|
+
arity == -1 ? 0 : arity
|
16
|
+
end
|
17
|
+
|
18
|
+
def backtrace_line
|
19
|
+
inspect.match(/[\d\w]+@(.*)>/)[1] + ":in `#{name}'"
|
20
|
+
end
|
21
|
+
|
22
|
+
def meth
|
23
|
+
@meth ||= "__cucumber_#{object_id}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def mod
|
27
|
+
p = self
|
28
|
+
m = meth
|
29
|
+
@mod ||= Module.new do
|
30
|
+
define_method(m, &p)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class String
|
2
|
+
def gzub(pattern, format=nil, &proc)
|
3
|
+
s = dup
|
4
|
+
md = match(pattern)
|
5
|
+
pos = 0
|
6
|
+
md.captures.each_with_index do |m, n|
|
7
|
+
replacement = if block_given?
|
8
|
+
proc.call(m)
|
9
|
+
else
|
10
|
+
format % m
|
11
|
+
end
|
12
|
+
s[md.offset(n+1)[0] + pos, m.length] = replacement
|
13
|
+
pos += replacement.length - m.length
|
14
|
+
end
|
15
|
+
s
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'cucumber/core_ext/proc'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
class Pending < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class ArgCountError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class Executor
|
11
|
+
attr_reader :failed
|
12
|
+
|
13
|
+
def line=(line)
|
14
|
+
@line = line
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(formatter, step_mother)
|
18
|
+
@formatter = formatter
|
19
|
+
@world_proc = lambda{ Object.new }
|
20
|
+
@before_procs = []
|
21
|
+
@after_procs = []
|
22
|
+
@step_mother = step_mother
|
23
|
+
end
|
24
|
+
|
25
|
+
def register_world_proc(&proc)
|
26
|
+
@world_proc = proc
|
27
|
+
end
|
28
|
+
|
29
|
+
def register_before_proc(&proc)
|
30
|
+
proc.extend(CoreExt::CallIn)
|
31
|
+
@before_procs << proc
|
32
|
+
end
|
33
|
+
|
34
|
+
def register_after_proc(&proc)
|
35
|
+
proc.extend(CoreExt::CallIn)
|
36
|
+
@after_procs << proc
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_stories(stories)
|
40
|
+
raise "Line number can only be specified when there is 1 story. There were #{stories.length}." if @line && stories.length != 1
|
41
|
+
@step_mother.visit_stories(stories)
|
42
|
+
@formatter.visit_stories(stories) if @formatter.respond_to?(:visit_stories)
|
43
|
+
stories.accept(self)
|
44
|
+
@formatter.dump
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_story(story)
|
48
|
+
story.accept(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
def visit_header(header)
|
52
|
+
@formatter.header_executing(header) if @formatter.respond_to?(:header_executing)
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_narrative(narrative)
|
56
|
+
@formatter.narrative_executing(narrative) if @formatter.respond_to?(:narrative_executing)
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_scenario(scenario)
|
60
|
+
if @line.nil? || scenario.at_line?(@line)
|
61
|
+
@error = nil
|
62
|
+
@world = @world_proc.call
|
63
|
+
@formatter.scenario_executing(scenario) if @formatter.respond_to?(:scenario_executing)
|
64
|
+
@before_procs.each{|p| p.call_in(@world, *[])}
|
65
|
+
scenario.accept(self)
|
66
|
+
@after_procs.each{|p| p.call_in(@world, *[])}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_step(step)
|
71
|
+
if @error.nil?
|
72
|
+
begin
|
73
|
+
step.execute_in(@world)
|
74
|
+
rescue Pending => ignore
|
75
|
+
rescue => e
|
76
|
+
@failed = true
|
77
|
+
@error = e
|
78
|
+
end
|
79
|
+
@formatter.step_executed(step)
|
80
|
+
else
|
81
|
+
@formatter.step_skipped(step)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|