crab 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ *.swp
1
2
  *.gem
2
3
  .bundle
3
4
  Gemfile.lock
data/README.md CHANGED
@@ -144,7 +144,6 @@ To do / Roadmap
144
144
 
145
145
  ### 0.2.0
146
146
 
147
- - Add a `move` subcommand which moves the story from one state to the next (potentially, `move --back`)
148
147
  - `pull` is not very smart and could detect feature files being moved from one dir to another
149
148
  - Recursively look for a `.crab` directory like Git does with `.git`
150
149
  - Verbose logging (especially before any change or destructive operations in Rally)
data/Rakefile CHANGED
@@ -12,12 +12,27 @@ namespace :cucumber do
12
12
  desc "Basic test suite set-up"
13
13
  task :setup do
14
14
  require 'highline/import'
15
- test_project = ask("Name of the project in Rally to be used for tests: ")
16
15
 
17
- dot_crab = File.expand_path("~/.crab")
16
+ puts """
17
+ In order to run the Cucumber tests for crab itself, we need a few
18
+ details from you. These are stored in ~/.crab/tests/ and do not affect
19
+ normal usage.
20
+ """
21
+
22
+ username = ask("Username: ")
23
+ password = ask("Password: ") {|q| q.echo = false }
24
+ project = ask("Project to test against: ")
25
+
26
+ dot_crab = File.expand_path("~/.crab/tests")
18
27
  FileUtils.mkdir_p dot_crab
19
- File.open(File.join(dot_crab, 'test_project'), 'w') do |file|
20
- file.puts test_project
28
+
29
+ File.open(File.join(dot_crab, 'credentials'), 'w') do |file|
30
+ file.puts username
31
+ file.puts password
32
+ end
33
+
34
+ File.open(File.join(dot_crab, 'project'), 'w') do |file|
35
+ file.puts project
21
36
  end
22
37
  end
23
38
 
@@ -0,0 +1,19 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab create: create a new story in Rally
7
+
8
+ Usage: crab create name [options]
9
+ BANNER
10
+ end
11
+
12
+ rally = Crab::Rally.new
13
+ name = ARGV.join(" ")
14
+ Trollop::die "Please specify a name for the story" if name.blank?
15
+
16
+ rally.connect
17
+ story = rally.create_story :name => name
18
+
19
+ puts "#{story.formatted_id}: #{story.name} (#{story.state})"
@@ -0,0 +1,61 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ include Crab::Utilities
5
+
6
+ cmd_opts = Trollop::options do
7
+ banner <<-BANNER
8
+ crab update: update a story in Rally
9
+
10
+ Usage: crab update story [options]
11
+ BANNER
12
+ opt :name, "Name (title)", :type => String, :short => "-n"
13
+ opt :state, "State (one of: #{Crab::Story::VALID_STATES.join(" ")})", :type => String, :short => "-t"
14
+ opt :estimate, "Estimate", :type => :int, :short => "-e"
15
+ opt :iteration, "Iteration", :type => String, :short => "-i"
16
+ opt :release, "Release", :type => String, :short => "-r"
17
+ opt :blocked, "Blocked", :short => "-b"
18
+ opt :unblocked, "Unblocked", :short => "-u"
19
+ opt :parent, "Parent", :type => String, :short => "-p"
20
+ end
21
+
22
+ rally = Crab::Rally.new
23
+
24
+ Trollop::die "No story given" if ARGV.empty?
25
+ Trollop::die "Nothing to update. Please provide some options" unless cmd_opts.any? {|k, v| k.to_s =~ /_given$/ }
26
+
27
+ opts = {}
28
+ opts[:name] = cmd_opts[:name] if cmd_opts[:name_given]
29
+ opts[:schedule_state] = state_from(cmd_opts[:state]) if cmd_opts[:state_given]
30
+
31
+ if cmd_opts[:estimate_given]
32
+ opts[:plan_estimate] = cmd_opts[:estimate] # nobody is going to remember "Plan Estimate", really
33
+ end
34
+
35
+ opts[:blocked] = cmd_opts[:blocked] if cmd_opts[:blocked_given]
36
+ opts[:blocked] = !cmd_opts[:unblocked] if cmd_opts[:unblocked_given]
37
+
38
+ rally.connect
39
+
40
+ story = rally.find_story_with_id ARGV.first
41
+
42
+ if cmd_opts[:iteration_given]
43
+ opts[:iteration] = rally.find_iteration_by_name cmd_opts[:iteration]
44
+ Trollop::die "Unknown iteration \"#{cmd_opts[:iteration]}\"" if opts[:iteration].nil?
45
+ end
46
+
47
+ if cmd_opts[:release_given]
48
+ opts[:release] = rally.find_release_by_name cmd_opts[:release]
49
+ Trollop::die "Unknown release \"#{cmd_opts[:release]}\"" if opts[:release].nil?
50
+ end
51
+
52
+ if cmd_opts[:parent_given]
53
+ opts[:parent] = rally.find_story_with_id(cmd_opts[:parent]).rally_object
54
+ Trollop::die "Unknown story \"#{cmd_opts[:parent]}\"" if opts[:parent].nil?
55
+ end
56
+
57
+ opts[:name] = story.name if opts[:name].blank?
58
+
59
+ story.update opts
60
+
61
+ puts "#{story.formatted_id}: #{story.name} (#{story.state})"
@@ -0,0 +1,19 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab create: create a new story in Rally
7
+
8
+ Usage: crab create name [options]
9
+ BANNER
10
+ end
11
+
12
+ rally = Crab::Rally.new
13
+ name = ARGV.join(" ")
14
+ Trollop::die "Please specify a name for the story" if name.blank?
15
+
16
+ rally.connect
17
+ story = rally.create_story :name => name
18
+
19
+ puts "#{story.formatted_id}: #{story.name} (#{story.state})"
@@ -0,0 +1,22 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab delete: delete an existing story in Rally
7
+
8
+ Usage: crab delete story [options]
9
+ BANNER
10
+ end
11
+
12
+ rally = Crab::Rally.new
13
+
14
+ story_id = ARGV.join(" ")
15
+ Trollop::die "Story ID must be specified" if story_id.blank?
16
+
17
+ rally.connect
18
+ story = rally.find_story_with_id story_id
19
+
20
+ story.delete
21
+
22
+ puts "Story #{story_id} deleted."
@@ -0,0 +1,22 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab delete: delete an existing story in Rally
7
+
8
+ Usage: crab delete story [options]
9
+ BANNER
10
+ end
11
+
12
+ rally = Crab::Rally.new
13
+
14
+ story_id = ARGV.join(" ")
15
+ Trollop::die "Story ID must be specified" if story_id.blank?
16
+
17
+ rally.connect
18
+ story = rally.find_story_with_id story_id
19
+
20
+ story.delete
21
+
22
+ puts "Story #{story_id} deleted."
@@ -0,0 +1,27 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ include Crab::Utilities
5
+
6
+ cmd_opts = Trollop::options do
7
+ banner <<-BANNER
8
+ crab find: find a story in Rally
9
+
10
+ Usage: crab find [options] [text]
11
+ BANNER
12
+ opt :project, "Project to use (required unless set by 'crab project')", :short => "-p", :type => String
13
+ end
14
+
15
+ rally = Crab::Rally.new
16
+ pattern = ARGV.map(&:strip).reject(&:empty?)
17
+ project_name = valid_project_name(cmd_opts)
18
+
19
+ rally.connect
20
+
21
+ project = rally.find_project(project_name)
22
+
23
+ Trollop::die "Project #{cmd_opts[:project].inspect} not found" if project.nil?
24
+
25
+ rally.find_stories(project, pattern).each do |story|
26
+ puts "#{story.formatted_id}: #{story.name} (#{story.state})"
27
+ end
@@ -0,0 +1,25 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ include Crab::Utilities
5
+
6
+ opts = Trollop::options do
7
+ banner <<-BANNER
8
+ Usage: crab login [options]
9
+
10
+ Log into Rally. Your credentials will be written to ~/.crab/credentials.
11
+ BANNER
12
+
13
+ opt :username, "Username", :type => String, :short => "-u"
14
+ opt :password, "Password", :type => String, :short => "-p"
15
+ end
16
+
17
+ username = opts[:username_given] ? opts[:username] : ask("Username: ")
18
+ password = opts[:password_given] ? opts[:password] : ask("Password: ") {|q| q.echo = false }
19
+
20
+ File.open(credentials_file, 'w') do |file|
21
+ file.puts username
22
+ file.puts password
23
+ end
24
+
25
+ puts "Logged in as #{username}"
@@ -0,0 +1,26 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ include Crab::Utilities
5
+
6
+ cmd_opts = Trollop::options do
7
+ banner <<-BANNER
8
+ crab move: move a story from one status to the next (or previous)
9
+
10
+ Usage: crab move story [options]
11
+ BANNER
12
+ opt :back, "Move story backwards (from accepted to completed, for example)"
13
+ end
14
+
15
+ rally = Crab::Rally.new
16
+ story_id = ARGV.join(" ")
17
+ Trollop::die "No story given" if story_id.empty?
18
+
19
+ rally.connect
20
+
21
+ story = rally.find_story_with_id story_id
22
+ state = state_from(story.state)
23
+
24
+ story.update :schedule_state => (cmd_opts[:back] ? state_before(state) : state_after(state))
25
+
26
+ puts "#{story.formatted_id}: #{story.name} (#{story.state})"
@@ -0,0 +1,26 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ include Crab::Utilities
5
+
6
+ cmd_opts = Trollop::options do
7
+ banner <<-BANNER
8
+ crab move: move a story from one status to the next (or previous)
9
+
10
+ Usage: crab move story [options]
11
+ BANNER
12
+ opt :back, "Move story backwards (from accepted to completed, for example)"
13
+ end
14
+
15
+ rally = Crab::Rally.new
16
+ story_id = ARGV.join(" ")
17
+ Trollop::die "No story given" if story_id.empty?
18
+
19
+ rally.connect
20
+
21
+ story = rally.find_story_with_id story_id
22
+ state = state_from(story.state)
23
+
24
+ story.update :schedule_state => (cmd_opts[:back] ? state_before(state) : state_after(state))
25
+
26
+ puts "#{story.formatted_id}: #{story.name} (#{story.state})"
@@ -0,0 +1,35 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ include Crab::Utilities
5
+
6
+ cmd_opts = Trollop::options do
7
+ banner <<-BANNER
8
+ crab project: show or persistently select project to work with in Rally
9
+
10
+ Usage: crab project [name]
11
+ BANNER
12
+ end
13
+
14
+ rally = Crab::Rally.new
15
+
16
+ current_project_name = self.class.current_project_name
17
+ if current_project_name.present?
18
+ puts current_project_name
19
+
20
+ elsif ARGV.reject {|arg| arg.blank? }.empty?
21
+ puts "No project currently selected."
22
+
23
+ else
24
+ name = ARGV.join(" ").strip
25
+
26
+ rally.connect
27
+
28
+ project = rally.find_project name
29
+ Trollop::die "#{name.inspect} is not a valid project" if project.nil?
30
+
31
+ FileUtils.mkdir_p ".crab"
32
+ File.open(".crab/project", "w") do |file|
33
+ file.puts project.name
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab pull: pulls stories from Rally and writes them out as Cucumber features
7
+
8
+ Usage: crab pull [options] story1 [story2 ...]
9
+ BANNER
10
+ opt :language, "Language to generate Cucumber features in (ISO code)", :default => "en", :short => "-l"
11
+ end
12
+
13
+ rally = Crab::Rally.new
14
+ rally.connect
15
+
16
+ ARGV.each do |story_number|
17
+ story = rally.find_story_with_id story_number
18
+ Trollop::die "Could not find story with ID #{story_number}" if story.nil?
19
+
20
+ puts "#{story.formatted_id}: #{story.full_file_name}"
21
+
22
+ FileUtils.mkdir_p File.dirname(story.full_file_name)
23
+ FileUtils.touch story.full_file_name
24
+
25
+ File.open(story.full_file_name, "w") do |file|
26
+ file.write Crab::CucumberFeature.new(cmd_opts[:language]).generate_from story
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab delete: delete an existing story in Rally
7
+
8
+ Usage: crab delete story [options]
9
+ BANNER
10
+ end
11
+
12
+ rally = Crab::Rally.new
13
+
14
+ story_id = ARGV.join(" ")
15
+ Trollop::die "Story ID must be specified" if story_id.blank?
16
+
17
+ rally.connect
18
+ story = rally.find_story_with_id story_id
19
+
20
+ story.delete
21
+
22
+ puts "Story #{story_id} deleted."
@@ -0,0 +1,22 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab delete: delete an existing story in Rally
7
+
8
+ Usage: crab delete story [options]
9
+ BANNER
10
+ end
11
+
12
+ rally = Crab::Rally.new
13
+
14
+ story_id = ARGV.join(" ")
15
+ Trollop::die "Story ID must be specified" if story_id.blank?
16
+
17
+ rally.connect
18
+ story = rally.find_story_with_id story_id
19
+
20
+ story.delete
21
+
22
+ puts "Story #{story_id} deleted."
@@ -0,0 +1,20 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ cmd_opts = Trollop::options do
5
+ banner <<-BANNER
6
+ crab show: displays a story in Rally as a Cucumber feature
7
+
8
+ Usage: crab show story"
9
+ BANNER
10
+ opt :language, "Language to display Cucumber features in (ISO code)", :default => "en", :short => "-l"
11
+ end
12
+
13
+ story_id = ARGV.first
14
+ rally = Crab::Rally.new
15
+
16
+ rally.connect
17
+
18
+ story = rally.find_story_with_id story_id
19
+
20
+ puts Crab::CucumberFeature.new(cmd_opts[:language]).generate_from story
@@ -0,0 +1,79 @@
1
+ # vim: set ft=ruby :
2
+ require 'crab'
3
+
4
+ def add_or_update_options(banner)
5
+ Trollop::options do
6
+ banner banner
7
+ opt :priority, "Priority (one of: #{Crab::TestCase::PRIORITIES.join(" ")}", :default => "important", :short => '-p'
8
+ opt :risk, "Risk (one of: #{Crab::TestCase::RISKS.join(" ")})", :default => "medium", :short => '-r'
9
+ opt :method, "Method (one of: #{Crab::TestCase::METHODS.join(" ")})", :default => "automated", :short => '-m'
10
+ opt :type, "Type (one of: #{Crab::TestCase::TYPES.join(" ")})", :default => "acceptance", :short => '-t'
11
+ opt :pre, "Pre-conditions", :default => "N/A"
12
+ opt :post, "Post-conditions", :default => "N/A"
13
+ opt :desc, "Description", :default => "N/A", :short => '-d'
14
+ end
15
+ end
16
+
17
+ def sanitize_options(opts, creating=true)
18
+ result = {}
19
+ result[:priority] = opts[:priority].capitalize if creating || opts[:priority_given]
20
+ result[:risk] = opts[:risk].capitalize if creating || opts[:risk_given]
21
+ result[:method] = opts[:method].capitalize if creating || opts[:method_given]
22
+ result[:type] = opts[:type].capitalize if creating || opts[:type_given]
23
+ result[:pre_conditions] = opts[:pre] if creating || opts[:pre_given]
24
+ result[:post_conditions] = opts[:post] if creating || opts[:post_given]
25
+ result[:description] = opts[:desc] if creating || opts[:desc_given]
26
+ result
27
+ end
28
+
29
+ rally = Crab::Rally.new
30
+
31
+ Trollop::options do
32
+ banner <<-BANNER
33
+ crab testcase: manage test cases in a story (add, update, delete)
34
+
35
+ Usage: crab testcase add story name [options]
36
+ crab testcase update testcase [options]
37
+ crab testcase delete testcase [options]
38
+ BANNER
39
+ stop_on %w{add update delete}
40
+ end
41
+
42
+ sub = ARGV.shift
43
+ case sub
44
+ when "add"
45
+ opts = add_or_update_options "crab testcase add: add a test case to a story in Rally"
46
+
47
+ story_id = ARGV.shift
48
+ name = ARGV.join(" ")
49
+ add(story_id, name, opts)
50
+
51
+ rally.connect
52
+ tc = rally.create_test_case(story_id, name, sanitize_options(opts))
53
+
54
+ puts "#{tc.story.formatted_id}/#{tc.formatted_id}: #{tc.name} (#{tc.tags.join(" ")})"
55
+
56
+ when "update"
57
+ opts = add_or_update_options "crab testcase update: update a test case in Rally"
58
+
59
+ tc_id = ARGV.shift
60
+
61
+ rally.connect
62
+ tc = rally.find_test_case(tc_id)
63
+ tc.update(sanitize_options(opts))
64
+ puts "#{tc.story.formatted_id}/#{tc.formatted_id}: #{tc.name} (#{tc.tags.join(" ")})"
65
+
66
+ when "delete"
67
+ Trollop::options do
68
+ banner "crab testcase delete: delete a test case in Rally"
69
+ end
70
+
71
+ tc_id = ARGV.shift
72
+ rally.connect
73
+ tc = rally.find_test_case(tc_id)
74
+ tc.delete
75
+ puts "Test case #{tc_id} deleted."
76
+
77
+ else
78
+ Trollop::die "Unknown subcommand#{' ' + sub.inspect if sub}"
79
+ end