crab 0.1.7 → 0.1.8

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/.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