scrab 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.
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
6
+ *.swo
7
+ logs
@@ -0,0 +1,3 @@
1
+ :username: YOUR_USERNAME
2
+ :password: YOUR_PASSWORD
3
+ :project: THE_NAME_OF_YOUR_PROJECT
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in scrab.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^spec/.+_spec\.rb$})
11
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
12
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
13
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
14
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
15
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
16
+ watch('spec/spec_helper.rb') { "spec" }
17
+ watch('config/routes.rb') { "spec/routing" }
18
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
19
+ # Capybara request specs
20
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
21
+ end
22
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "scrab"
4
+ require 'optparse'
5
+
6
+ options = {}
7
+
8
+ opt_parser = OptionParser.new do |o|
9
+ o.banner = "Usage: scrab <command> [options*]"
10
+ o.separator ""
11
+
12
+ o.separator "Available commands:"
13
+ o.separator " push"
14
+ o.separator ""
15
+
16
+ o.separator "Global options:"
17
+
18
+ o.on("-h", "--help", "Show this message") do
19
+ puts opt_parser
20
+ exit
21
+ end
22
+ end
23
+
24
+ opt_parser.parse!
25
+
26
+ command_name = ARGV.shift
27
+
28
+ begin
29
+ command = Scrab::CommandFactory.get(command_name, STDOUT)
30
+ command.run(options, *ARGV)
31
+ rescue Scrab::StandardError => e
32
+ puts e.message
33
+ puts opt_parser
34
+ end
@@ -0,0 +1,84 @@
1
+ require "rubygems"
2
+ require "gherkin"
3
+
4
+ class CustomFormatter
5
+ def uri(uri)
6
+ @uri = uri
7
+ end
8
+
9
+ def feature(feature)
10
+ @feature = feature
11
+ end
12
+
13
+ def background(background)
14
+ @background = background
15
+ end
16
+
17
+ def step(step)
18
+ @step = step
19
+ end
20
+
21
+ def scenario(scenario)
22
+ @scenario = scenario
23
+ end
24
+
25
+ def scenario_outline(outline)
26
+ @outline = outline
27
+ end
28
+
29
+ def examples(examples)
30
+ @examples = examples
31
+ end
32
+
33
+ def eof
34
+ @feature
35
+ end
36
+ end
37
+
38
+ fmt = CustomFormatter.new
39
+ prs = Gherkin::Parser::Parser.new(fmt)
40
+ p prs.parse(File.read(ARGV[0]), nil, 0)
41
+
42
+ #require "cucumber/formatter/io"
43
+ #
44
+ #
45
+ #class CustomFormatter
46
+ # include Cucumber::Formatter::Io
47
+ #
48
+ # def initialize(step_mother, path_or_io, options)
49
+ # @step_mother, @io, @options = step_mother, ensure_io(path_or_io, "fuubar"), options
50
+ # end
51
+ #
52
+ # def after_features(features)
53
+ # puts "==== AFTER FEATURES"
54
+ # p features
55
+ # end
56
+ #
57
+ # def before_features(features)
58
+ # puts "==== BEFORE FEATURES"
59
+ # p features
60
+ # end
61
+ #
62
+ # def before_background(background)
63
+ # end
64
+ #
65
+ # def after_background(background)
66
+ # end
67
+ #
68
+ # def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
69
+ # puts "==== STEP RESULT"
70
+ # p [keyword, step_match, multiline_arg, status, exception, source_indent, background]
71
+ # end
72
+ #
73
+ # def before_examples(examples)
74
+ # end
75
+ #
76
+ # def after_examples(examples)
77
+ # end
78
+ #
79
+ # def before_table_row(table_row)
80
+ # end
81
+ #
82
+ # def after_table_row(table_row)
83
+ # end
84
+ #end
@@ -0,0 +1,38 @@
1
+ require "bundler"
2
+ Bundler.setup(:default)
3
+
4
+ require "rally_rest_api"
5
+ require "gherkin"
6
+ require "scrab/version"
7
+ require "scrab/command_set"
8
+ require "scrab/rally"
9
+ require "scrab/story"
10
+ require "scrab/scenario"
11
+ require "scrab/step"
12
+ require "scrab/feature_parser"
13
+
14
+ module Scrab
15
+ class StandardError < ::StandardError; end
16
+
17
+ LibPath = Pathname.new(File.dirname(__FILE__) + "/scrab")
18
+ CommandsPath = LibPath.join('commands')
19
+ Commands = Module.new
20
+ CommandFactory = CommandSet.new(Commands, CommandsPath)
21
+
22
+ def self.rally_config_path
23
+ "#{ENV['HOME']}/.rally"
24
+ end
25
+
26
+ def self.rally_config
27
+ raise "Please configure rally at #{rally_config_path}, check .rally.sample for an example" unless File.exist?(rally_config_path)
28
+ @rally_config ||= YAML.load(File.read(rally_config_path))
29
+ end
30
+
31
+ def self.rally
32
+ @rally ||= Rally.new(rally_config.merge(:logger => logger))
33
+ end
34
+
35
+ def self.logger
36
+ @logger ||= Logger.new(File.dirname(__FILE__) + "/../logs/rally.log")
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ class Scrab::CommandSet
2
+ class CommandNotFoundError < StandardError
3
+ def initialize(command_name)
4
+ super "Command not found: #{command_name}"
5
+ end
6
+ end
7
+
8
+ def initialize(namespace, commands_path)
9
+ @namespace = namespace
10
+ @commands_path = Pathname.new(commands_path)
11
+ end
12
+
13
+ def get(command_name, out)
14
+ begin
15
+ Kernel.load(@commands_path.join("#{command_name}.rb").to_s)
16
+ rescue LoadError => e
17
+ raise CommandNotFoundError.new(command_name)
18
+ end
19
+
20
+ const = @namespace
21
+
22
+ command_name.split("/").each do |command_part|
23
+ const_name = command_part.capitalize.gsub(/_(.)/) { $1.upcase }
24
+ const = const.const_get(const_name)
25
+ end
26
+
27
+ const.new(out)
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ class Scrab::Commands::Push
2
+ def initialize(out, parser = nil)
3
+ @out = out
4
+ @parser = parser || Scrab::FeatureParser.new(Scrab::Story)
5
+ end
6
+
7
+ def run(options, *features)
8
+ determine_feature_paths(features).each do |feature|
9
+ begin
10
+ story = @parser.parse(feature)
11
+
12
+ @out.print "Pushing '[#{story.id}] #{story.name}' to Rally... "
13
+
14
+ if Scrab.rally.update(story)
15
+ @out.puts "OK."
16
+ else
17
+ @out.puts "FAILED."
18
+ end
19
+ rescue Scrab::FeatureParser::ParsingError => e
20
+ @out.puts "Failed to parse '#{feature}':\n\t#{e.message}"
21
+ end
22
+ end
23
+ end
24
+
25
+ def determine_feature_paths(features)
26
+ features = ['features/'] if features.empty?
27
+
28
+ features.map do |feature_path|
29
+ if File.directory?(feature_path)
30
+ `find #{feature_path} -type f -name '*.feature'`.split(/\n/)
31
+ else
32
+ feature_path
33
+ end
34
+ end.flatten
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ class Scrab::FeatureParser
2
+ class ParsingError < StandardError
3
+ def initialize(line, desc)
4
+ super "Couldn't parse '#{line}': #{desc}"
5
+ end
6
+ end
7
+
8
+ class GherkinFormatter
9
+ def initialize(story)
10
+ @story = story
11
+ end
12
+
13
+ def feature(feature)
14
+ if feature.name.strip =~ /^\[(US\d+)\]\s+(.*)$/
15
+ @story.id = $1
16
+ @story.name = $2
17
+ else
18
+ raise ParsingError.new(feature.name, "expect feature title to be in '[US0000] Story name' format")
19
+ end
20
+
21
+ @story.narrative = feature.description
22
+ end
23
+
24
+ def background(background)
25
+ end
26
+
27
+ def scenario(scenario)
28
+ @scenario = Scrab::Scenario.new
29
+ @scenario.name = scenario.name
30
+ @scenario.description = scenario.description
31
+ @story.scenarios << @scenario
32
+ end
33
+
34
+ def step(step)
35
+ @step = Scrab::Step.new
36
+ @step.keyword = step.keyword
37
+ @step.name = step.name
38
+ @scenario.steps << @step
39
+ end
40
+
41
+ def scenario_outline(outline)
42
+ end
43
+
44
+ def examples(examples)
45
+ end
46
+
47
+ def uri(uri)
48
+ # no-op
49
+ end
50
+
51
+ def eof
52
+ # no-op
53
+ end
54
+ end
55
+
56
+ # TODO: Pass story instance to parse instead of the class here
57
+ def initialize(story_klass)
58
+ @story_klass = story_klass
59
+ end
60
+
61
+ def parse(feature_path)
62
+ story = @story_klass.new
63
+ parser = Gherkin::Parser::Parser.new(GherkinFormatter.new(story))
64
+ parser.parse(File.read(feature_path), nil, 0)
65
+ story
66
+ end
67
+ end
@@ -0,0 +1,60 @@
1
+ class Scrab::Rally
2
+ class ProjectNotFoundError < StandardError
3
+ def initialize(project)
4
+ super "Project '#{project}' not found"
5
+ end
6
+ end
7
+
8
+ def initialize(options)
9
+ @conn = RallyRestAPI.new(options)
10
+ @project = @conn.find(:project, :pagesize => 1) { equal :name, options[:project] }.first
11
+ raise ProjectNotFoundError.new(@project) if @project.nil?
12
+ end
13
+
14
+ def update(story)
15
+ update_story(story)
16
+ end
17
+
18
+ private
19
+ def update_story(story)
20
+ rest_object = @conn.find(:hierarchical_requirement, :pagesize => 1, :project => @project) { equal :formatted_i_d, story.id }.first
21
+ @conn.update(rest_object, :name => story.name, :description => story.narrative)
22
+ update_story_scenarios(rest_object, story.scenarios)
23
+ end
24
+
25
+ def update_story_scenarios(story_rest, scenarios)
26
+ scenarios.each do |scenario|
27
+ description = scenario.description
28
+ description = description && !description.empty? ? description : scenario.name
29
+
30
+ values = {
31
+ :project => @project,
32
+ :work_product => story_rest,
33
+ :name => scenario.name,
34
+ :description => description,
35
+ :priority => 'Important',
36
+ :type => 'Acceptance',
37
+ :method => 'Automated',
38
+ :risk => 'Medium',
39
+ :pre_conditions => "N/A",
40
+ :post_conditions => "N/A"
41
+ }
42
+
43
+ if rest_object = @conn.find(:test_case, :pagesize => 1, :project => @project) { equal :name, scenario.name.strip }.first
44
+ @conn.update(rest_object, values)
45
+ else
46
+ rest_object = @conn.create(:test_case, values)
47
+ end
48
+
49
+ update_scenario_steps(rest_object, scenario.steps)
50
+ end
51
+ end
52
+
53
+ def update_scenario_steps(scenario_rest, steps)
54
+ Array(scenario_rest.steps).each { |step_rest| step_rest.delete }
55
+
56
+ steps.each_with_index do |step, idx|
57
+ @conn.create(:test_case_step, :test_case => scenario_rest, :project => @project, :step_index => idx, :input => step.full_name)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,8 @@
1
+ class Scrab::Scenario
2
+ attr_accessor :name, :description
3
+ attr_reader :steps
4
+
5
+ def initialize
6
+ @steps = []
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class Scrab::Step
2
+ attr_accessor :keyword, :name
3
+
4
+ def full_name
5
+ "#{keyword} #{name}"
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ class Scrab::Story
2
+ attr_accessor :id, :name, :narrative
3
+ attr_reader :scenarios
4
+
5
+ def initialize
6
+ @scenarios = []
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Scrab
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "scrab/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "scrab"
7
+ s.version = Scrab::VERSION
8
+ s.authors = ["Rodrigo Kochenburger"]
9
+ s.email = ["divoxx@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Simple tool for pushing cucumber features into Rally testcases}
12
+ s.description = %q{Simple tool for pushing cucumber features into Rally testcases}
13
+
14
+ s.rubyforge_project = "scrab"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency 'builder' # RallyRestAPI dep
22
+ s.add_runtime_dependency 'rally_rest_api'
23
+ s.add_runtime_dependency 'gherkin'
24
+
25
+ s.add_development_dependency 'rspec'
26
+ s.add_development_dependency 'guard'
27
+ s.add_development_dependency 'guard-rspec'
28
+ s.add_development_dependency 'rb-fsevent'
29
+ s.add_development_dependency 'growl_notify'
30
+ s.add_development_dependency 'fakeweb'
31
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe "Pushing a story to rally" do
4
+ it "push stories to rally" do
5
+ scrab("push #{fixture_path('story.feature')}").should output("Pushing '[US5836] Story name' to Rally... OK.")
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ Feature: Invalid story title
2
+ As a user
3
+ I want to do something
4
+ So that that something is done
@@ -0,0 +1,9 @@
1
+ Feature: [US5836] Story name
2
+ As a user
3
+ I want to do something
4
+ So that that something is done
5
+
6
+ Scenario: User does something
7
+ Given the user exists
8
+ When he does something
9
+ Then something is done
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+ require "scrab/command_set"
3
+
4
+ describe CommandSet do
5
+ let(:command) { mock(:command) }
6
+ let(:command_klass) { mock(:command_klass) }
7
+ let(:out) { mock(:out) }
8
+ let(:namespace) { Module.new }
9
+ let(:commands_path) { "some/path/for/commands" }
10
+
11
+ subject { CommandSet.new(namespace, commands_path) }
12
+
13
+ before do
14
+ namespace.const_set(:Command, command_klass)
15
+ end
16
+
17
+ it "loads commands dynamically" do
18
+ Kernel.should_receive(:load).with("#{commands_path}/command.rb").and_return
19
+ command_klass.should_receive(:new).with(out).and_return(command)
20
+ subject.get('command', out).should == command
21
+ end
22
+
23
+ it "raises a CommandNotFoundError when asking for a non-existing command" do
24
+ expect { subject.get('invalid-command', out) }.to raise_error(CommandSet::CommandNotFoundError)
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+ require "scrab/commands/push"
3
+
4
+ describe Commands::Push do
5
+ let(:rally) { mock(:rally, :update => true) }
6
+ let(:story) { mock(:story, :id => "US0000", :name => "Story name") }
7
+ let(:parser) { mock(:parser, :parse => story) }
8
+ let(:out) { StringIO.new }
9
+ let(:path) { "some/path.feature" }
10
+ subject { Commands::Push.new(out, parser) }
11
+ before { Scrab.stub(:rally => rally) }
12
+
13
+ it "outputs message for success" do
14
+ subject.run({}, path)
15
+ out.rewind
16
+ out.read.should =~ /OK/
17
+ end
18
+
19
+ it "outputs parsing error message" do
20
+ parser.should_receive(:parse).and_raise(Scrab::FeatureParser::ParsingError.new('foo', 'bar'))
21
+ subject.run({}, path)
22
+ out.rewind
23
+ out.read.should =~ /Couldn't parse/
24
+ end
25
+
26
+ it "parses and update the story" do
27
+ parser.should_receive(:parse).with(path).and_return(story)
28
+ rally.should_receive(:update).with(story).and_return(true)
29
+ subject.run({}, path)
30
+ end
31
+
32
+ it "outputs a failed message when fail to push story" do
33
+ rally.should_receive(:update).with(story).and_return(false)
34
+ subject.run({}, path)
35
+ out.rewind
36
+ out.read.should =~ /FAILED/
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+ require "scrab/feature_parser"
3
+
4
+ describe FeatureParser do
5
+ subject { FeatureParser.new(Story) }
6
+
7
+ def do_parse
8
+ subject.parse(fixture_path('story.feature'))
9
+ end
10
+
11
+ it "parses a feature and return a story" do
12
+ do_parse.should be_a(Story)
13
+ end
14
+
15
+ it "sets the id" do
16
+ story = do_parse
17
+ story.id.should == "US5836"
18
+ end
19
+
20
+ it "sets the story name" do
21
+ story = do_parse
22
+ story.name.should == "Story name"
23
+ end
24
+
25
+ it "sets the story narrative" do
26
+ story = do_parse
27
+ story.narrative.should == <<-EOS.strip
28
+ As a user
29
+ I want to do something
30
+ So that that something is done
31
+ EOS
32
+ end
33
+
34
+ it "raises an parsing error if cant parse the id and story name" do
35
+ expect { subject.parse(fixture_path('invalid-name.feature')) }.to raise_error(FeatureParser::ParsingError)
36
+ end
37
+
38
+ it "sets the scenarios" do
39
+ story = do_parse
40
+ story.should have(1).scenarios
41
+ end
42
+
43
+ it "sets the steps" do
44
+ story = do_parse
45
+ story.scenarios.first.should have(3).steps
46
+ end
47
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+ require "scrab/rally"
3
+
4
+ describe Scrab::Rally do
5
+ let(:conn) { mock(:conn) }
6
+ let(:project) { mock(:project) }
7
+ let(:scenario) { mock(:scenario, :name => "Scenario name", :description => "scenario desc", :steps => []) }
8
+ let(:story) { mock(:story, :name => "Story name", :narrative => "Story narrative", :scenarios => []) }
9
+ let(:rest_obj) { mock(:rest_obj) }
10
+ subject { Scrab::Rally.new(:username => 'username', :password => 'password', :project => "project") }
11
+
12
+ before do
13
+ RallyRestAPI.stub(:new => conn)
14
+ conn.stub(:find).with(:project, anything).and_return(mock(:first => project))
15
+ conn.stub(:find).with(:hierarchical_requirement, anything).and_return(mock(:first => rest_obj))
16
+ conn.stub(:update).with(rest_obj, anything)
17
+ end
18
+
19
+ it "establishes a rally connection" do
20
+ RallyRestAPI.should_receive(:new).with(hash_including(:username => 'username', :password => 'password'))
21
+ Scrab::Rally.new(:username => 'username', :password => 'password')
22
+ end
23
+
24
+ it "updates a given story" do
25
+ # FIXME: No idea how to test the yielded block (conditions) because its called with instance_eval
26
+ story.stub(:scenarios => [])
27
+ conn.should_receive(:find).with(:hierarchical_requirement, :pagesize => 1, :project => project).and_return(mock(:first => rest_obj))
28
+ conn.should_receive(:update).with(rest_obj, :name => story.name, :description => story.narrative)
29
+ subject.update(story)
30
+ end
31
+
32
+ it "creates new testcases" do
33
+ story.stub(:scenarios => [scenario])
34
+
35
+ conn.should_receive(:find).with(:test_case, :pagesize => 1, :project => project).and_return(mock(:first => nil))
36
+ conn.should_receive(:create).with(:test_case,
37
+ :project => project,
38
+ :work_product => rest_obj,
39
+ :name => scenario.name,
40
+ :description => scenario.description,
41
+ :priority => 'Important',
42
+ :type => 'Acceptance',
43
+ :method => "Automated",
44
+ :risk => 'Medium',
45
+ :pre_conditions => "N/A",
46
+ :post_conditions => "N/A"
47
+ ).and_return(rest_obj)
48
+
49
+ rest_obj.stub(:steps).and_return([])
50
+
51
+ subject.update(story)
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+ require "scrab/scenario"
3
+
4
+ describe Scenario do
5
+ let(:name) { "Scenario name" }
6
+ let(:description) { "Scenario desc" }
7
+
8
+ it "has a name" do
9
+ subject.name = name
10
+ subject.name.should == name
11
+ end
12
+
13
+ it "has a description" do
14
+ subject.description = description
15
+ subject.description.should == description
16
+ end
17
+
18
+ it "has steps" do
19
+ subject.steps.should be_a(Enumerable)
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+ require "scrab/step"
3
+
4
+ describe Step do
5
+ it "has a keyword" do
6
+ subject.keyword = "Given"
7
+ subject.keyword.should == "Given"
8
+ end
9
+
10
+ it "has a name" do
11
+ subject.name = "name"
12
+ subject.name.should == "name"
13
+ end
14
+
15
+ it "has a full_name" do
16
+ subject.keyword = "Given"
17
+ subject.name = "I am on that page"
18
+ subject.full_name.should == "Given I am on that page"
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+ require "scrab/story"
3
+
4
+ describe Story do
5
+ let(:id) { "US0000" }
6
+ let(:name) { "Story name" }
7
+ let(:narrative) { <<-EOS }
8
+ As a user
9
+ I want to do something
10
+ So that a get that value
11
+ EOS
12
+
13
+ let(:scenario) { mock(:scenario) }
14
+
15
+ it "has an id" do
16
+ subject.id = id
17
+ subject.id.should == id
18
+ end
19
+
20
+ it "has a name" do
21
+ subject.name = name
22
+ subject.name.should == name
23
+ end
24
+
25
+ it "has a narrative" do
26
+ subject.narrative = narrative
27
+ subject.narrative.should == narrative
28
+ end
29
+
30
+ it "has scenarios" do
31
+ subject.scenarios << scenario
32
+ subject.scenarios.should == [scenario]
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ require "scrab"
2
+ Bundler.setup(:development)
3
+ include Scrab
4
+
5
+ require "rspec"
6
+ require "fakeweb"
7
+
8
+ require "support/fixtures"
9
+ require "support/commands"
10
+
11
+ RSpec.configure do |c|
12
+ c.include(Support::Fixtures)
13
+ c.include(Support::Commands)
14
+ end
15
+
@@ -0,0 +1,40 @@
1
+ module Support
2
+ module Commands
3
+ def scrab(cmd)
4
+ ScrabProcess.new("ruby -Ilib bin/scrab #{cmd}")
5
+ end
6
+
7
+ class ScrabProcess
8
+ attr_reader :cmd, :out
9
+
10
+ def initialize(cmd)
11
+ @cmd = cmd
12
+
13
+ IO.popen(cmd) do |io|
14
+ @out = io.read.freeze
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ RSpec::Matchers.define :output do |expected_out|
22
+ match do |process|
23
+ process.out.strip == expected_out.strip
24
+ end
25
+
26
+ failure_message_for_should do |process|
27
+ fmt_expected_out = expected_out.split(/\n/).join("\n\t\t")
28
+ fmt_actual_out = process.out.split(/\n/).join("\n\t\t")
29
+
30
+ msg = <<-EOS
31
+ \tExpected command '#{process.cmd}' output to match:
32
+ \t\t#{fmt_expected_out}
33
+
34
+ \tBut got:
35
+ \t\t#{fmt_actual_out}
36
+ EOS
37
+
38
+ msg
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module Support
2
+ module Fixtures
3
+ Path = Pathname.new(File.dirname(__FILE__) + "/../fixtures")
4
+
5
+ def fixture_path(name)
6
+ Path.join(name.to_s)
7
+ end
8
+
9
+ def fixture_content(name)
10
+ File.read fixture_path(name)
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scrab
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rodrigo Kochenburger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: builder
16
+ requirement: &70203933972840 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70203933972840
25
+ - !ruby/object:Gem::Dependency
26
+ name: rally_rest_api
27
+ requirement: &70203933972400 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70203933972400
36
+ - !ruby/object:Gem::Dependency
37
+ name: gherkin
38
+ requirement: &70203933971880 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70203933971880
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70203933971420 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70203933971420
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard
60
+ requirement: &70203933970880 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70203933970880
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: &70203933970320 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70203933970320
80
+ - !ruby/object:Gem::Dependency
81
+ name: rb-fsevent
82
+ requirement: &70203933969740 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70203933969740
91
+ - !ruby/object:Gem::Dependency
92
+ name: growl_notify
93
+ requirement: &70203933968800 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70203933968800
102
+ - !ruby/object:Gem::Dependency
103
+ name: fakeweb
104
+ requirement: &70203933968320 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *70203933968320
113
+ description: Simple tool for pushing cucumber features into Rally testcases
114
+ email:
115
+ - divoxx@gmail.com
116
+ executables:
117
+ - scrab
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - .gitignore
122
+ - .rally.sample
123
+ - Gemfile
124
+ - Guardfile
125
+ - Rakefile
126
+ - bin/scrab
127
+ - custom_formatter.rb
128
+ - lib/scrab.rb
129
+ - lib/scrab/command_set.rb
130
+ - lib/scrab/commands/push.rb
131
+ - lib/scrab/feature_parser.rb
132
+ - lib/scrab/rally.rb
133
+ - lib/scrab/scenario.rb
134
+ - lib/scrab/step.rb
135
+ - lib/scrab/story.rb
136
+ - lib/scrab/version.rb
137
+ - scrab.gemspec
138
+ - spec/acceptance/pushing_story_spec.rb
139
+ - spec/fixtures/invalid-name.feature
140
+ - spec/fixtures/story.feature
141
+ - spec/objects/command_set_spec.rb
142
+ - spec/objects/commands/push_spec.rb
143
+ - spec/objects/feature_parse_spec.rb
144
+ - spec/objects/rally_spec.rb
145
+ - spec/objects/scenario_spec.rb
146
+ - spec/objects/step_spec.rb
147
+ - spec/objects/story_spec.rb
148
+ - spec/spec_helper.rb
149
+ - spec/support/commands.rb
150
+ - spec/support/fixtures.rb
151
+ homepage: ''
152
+ licenses: []
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ! '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project: scrab
171
+ rubygems_version: 1.8.11
172
+ signing_key:
173
+ specification_version: 3
174
+ summary: Simple tool for pushing cucumber features into Rally testcases
175
+ test_files:
176
+ - spec/acceptance/pushing_story_spec.rb
177
+ - spec/fixtures/invalid-name.feature
178
+ - spec/fixtures/story.feature
179
+ - spec/objects/command_set_spec.rb
180
+ - spec/objects/commands/push_spec.rb
181
+ - spec/objects/feature_parse_spec.rb
182
+ - spec/objects/rally_spec.rb
183
+ - spec/objects/scenario_spec.rb
184
+ - spec/objects/step_spec.rb
185
+ - spec/objects/story_spec.rb
186
+ - spec/spec_helper.rb
187
+ - spec/support/commands.rb
188
+ - spec/support/fixtures.rb