Commit 90033270a10b952161d1e545993761516d5e8566
1 parent
826c3ea3
Exists in
master
and in
1 other branch
Extract all IssueTracker system and migrate to errbit_plugin
This new system can avoid having all dependencies in Errbit directly. Now if you want new issue tracker you need add it. Not delete it like previous. This work is in progress actually
Showing
44 changed files
with
332 additions
and
1356 deletions
Show diff stats
Gemfile
| ... | ... | @@ -27,30 +27,13 @@ gem 'rails_autolink' |
| 27 | 27 | gem 'hoptoad_notifier', "~> 2.4" |
| 28 | 28 | gem 'draper', :require => false |
| 29 | 29 | |
| 30 | +gem 'errbit_plugin', | |
| 31 | + :git => 'https://github.com/errbit/errbit_plugin.git' | |
| 32 | + # :path => 'vendor/gems/errbit_plugin' | |
| 33 | +gem 'errbit_github_plugin', | |
| 34 | + :git => 'https://github.com/errbit/errbit_github_plugin.git' | |
| 35 | + # :path => 'vendor/gems/errbit_github_plugin' | |
| 30 | 36 | |
| 31 | -# Remove / comment out any of the gems below if you want to disable | |
| 32 | -# a given issue tracker, notification service, or authentication. | |
| 33 | - | |
| 34 | -# Issue Trackers | |
| 35 | -# --------------------------------------- | |
| 36 | -# Lighthouse | |
| 37 | -gem 'lighthouse-api' | |
| 38 | -# Redmine | |
| 39 | -gem 'oruen_redmine_client', :require => 'redmine_client' | |
| 40 | -# Pivotal Tracker | |
| 41 | -gem 'pivotal-tracker' | |
| 42 | -# Fogbugz | |
| 43 | -gem 'ruby-fogbugz', :require => 'fogbugz' | |
| 44 | -# Github Issues | |
| 45 | -gem 'octokit', '~> 1.18' | |
| 46 | -# Gitlab | |
| 47 | -gem 'gitlab', '~> 3.0.0' | |
| 48 | - | |
| 49 | -# Bitbucket Issues | |
| 50 | -gem 'bitbucket_rest_api', :require => false | |
| 51 | - | |
| 52 | -# Jira | |
| 53 | -gem 'jira-ruby', :require => 'jira' | |
| 54 | 37 | |
| 55 | 38 | # Notification services |
| 56 | 39 | # --------------------------------------- |
| ... | ... | @@ -80,7 +63,6 @@ group :development, :test do |
| 80 | 63 | gem 'rspec-rails' |
| 81 | 64 | gem 'webmock', :require => false |
| 82 | 65 | gem 'airbrake', :require => false |
| 83 | - gem 'ruby-debug', :platform => :mri_18 | |
| 84 | 66 | gem 'debugger', :platform => :mri_19 |
| 85 | 67 | gem 'pry-rails' |
| 86 | 68 | # gem 'rpm_contrib' |
| ... | ... | @@ -117,7 +99,6 @@ group :heroku, :production do |
| 117 | 99 | gem 'unicorn', :require => false |
| 118 | 100 | end |
| 119 | 101 | |
| 120 | - | |
| 121 | 102 | # Gems used only for assets and not required |
| 122 | 103 | # in production environments by default. |
| 123 | 104 | group :assets do | ... | ... |
Gemfile.lock
| 1 | +PATH | |
| 2 | + remote: vendor/gems/errbit_github_plugin | |
| 3 | + specs: | |
| 4 | + errbit_github_plugin (0.0.1) | |
| 5 | + errbit_plugin | |
| 6 | + octokit | |
| 7 | + | |
| 8 | +PATH | |
| 9 | + remote: vendor/gems/errbit_plugin | |
| 10 | + specs: | |
| 11 | + errbit_plugin (0.0.1) | |
| 12 | + | |
| 1 | 13 | GEM |
| 2 | 14 | remote: https://rubygems.org/ |
| 3 | 15 | specs: |
| ... | ... | @@ -44,13 +56,6 @@ GEM |
| 44 | 56 | erubis (>= 2.6.6) |
| 45 | 57 | binding_of_caller (0.7.2) |
| 46 | 58 | debug_inspector (>= 0.0.1) |
| 47 | - bitbucket_rest_api (0.1.4) | |
| 48 | - faraday (~> 0.8.1) | |
| 49 | - faraday_middleware (~> 0.9.0) | |
| 50 | - hashie (~> 2.0.5) | |
| 51 | - multi_json (~> 1.3) | |
| 52 | - nokogiri (~> 1.5.2) | |
| 53 | - simple_oauth | |
| 54 | 59 | builder (3.0.4) |
| 55 | 60 | callsite (0.0.11) |
| 56 | 61 | campy (1.0.0) |
| ... | ... | @@ -96,8 +101,9 @@ GEM |
| 96 | 101 | warden (~> 1.2.3) |
| 97 | 102 | diff-lcs (1.2.4) |
| 98 | 103 | dotenv (0.9.0) |
| 99 | - draper (1.2.1) | |
| 104 | + draper (1.3.0) | |
| 100 | 105 | actionpack (>= 3.0) |
| 106 | + activemodel (>= 3.0) | |
| 101 | 107 | activesupport (>= 3.0) |
| 102 | 108 | request_store (~> 1.0.3) |
| 103 | 109 | email_spec (1.5.0) |
| ... | ... | @@ -105,25 +111,19 @@ GEM |
| 105 | 111 | mail (~> 2.2) |
| 106 | 112 | erubis (2.7.0) |
| 107 | 113 | execjs (2.0.2) |
| 108 | - fabrication (2.8.1) | |
| 114 | + fabrication (2.9.0) | |
| 109 | 115 | faraday (0.8.8) |
| 110 | 116 | multipart-post (~> 1.2.0) |
| 111 | - faraday_middleware (0.9.0) | |
| 112 | - faraday (>= 0.7.4, < 0.9) | |
| 113 | 117 | flowdock (0.3.1) |
| 114 | 118 | httparty (~> 0.7) |
| 115 | 119 | multi_json |
| 116 | 120 | foreman (0.63.0) |
| 117 | 121 | dotenv (>= 0.7) |
| 118 | 122 | thor (>= 0.13.6) |
| 119 | - gitlab (3.0.0) | |
| 120 | - httparty | |
| 121 | 123 | haml (4.0.3) |
| 122 | 124 | tilt |
| 123 | - happymapper (0.4.1) | |
| 124 | - libxml-ruby (~> 2.0) | |
| 125 | 125 | hashie (2.0.5) |
| 126 | - highline (1.6.19) | |
| 126 | + highline (1.6.20) | |
| 127 | 127 | hike (1.2.3) |
| 128 | 128 | hipchat (0.12.0) |
| 129 | 129 | httparty |
| ... | ... | @@ -138,11 +138,7 @@ GEM |
| 138 | 138 | json (~> 1.8) |
| 139 | 139 | multi_xml (>= 0.5.2) |
| 140 | 140 | httpauth (0.2.0) |
| 141 | - i18n (0.6.9) | |
| 142 | - jira-ruby (0.1.2) | |
| 143 | - activesupport | |
| 144 | - oauth | |
| 145 | - railties | |
| 141 | + i18n (0.6.5) | |
| 146 | 142 | journey (1.0.4) |
| 147 | 143 | jquery-rails (2.1.4) |
| 148 | 144 | railties (>= 3.0, < 5.0) |
| ... | ... | @@ -157,10 +153,6 @@ GEM |
| 157 | 153 | launchy (2.3.0) |
| 158 | 154 | addressable (~> 2.3) |
| 159 | 155 | libv8 (3.16.14.3) |
| 160 | - libxml-ruby (2.7.0) | |
| 161 | - lighthouse-api (2.0) | |
| 162 | - activeresource (>= 3.0.0) | |
| 163 | - activesupport (>= 3.0.0) | |
| 164 | 156 | linecache (0.46) |
| 165 | 157 | rbx-require-relative (> 0.0.4) |
| 166 | 158 | mail (2.5.4) |
| ... | ... | @@ -197,24 +189,15 @@ GEM |
| 197 | 189 | net-ssh (2.7.0) |
| 198 | 190 | net-ssh-gateway (1.2.0) |
| 199 | 191 | net-ssh (>= 2.6.5) |
| 200 | - netrc (0.7.7) | |
| 201 | 192 | nokogiri (1.5.10) |
| 202 | - nokogiri-happymapper (0.5.8) | |
| 203 | - nokogiri (~> 1.5) | |
| 204 | - oauth (0.4.7) | |
| 205 | 193 | oauth2 (0.8.1) |
| 206 | 194 | faraday (~> 0.8) |
| 207 | 195 | httpauth (~> 0.1) |
| 208 | 196 | jwt (~> 0.1.4) |
| 209 | 197 | multi_json (~> 1.0) |
| 210 | 198 | rack (~> 1.2) |
| 211 | - octokit (1.25.0) | |
| 212 | - addressable (~> 2.2) | |
| 213 | - faraday (~> 0.8) | |
| 214 | - faraday_middleware (~> 0.9) | |
| 215 | - hashie (~> 2.0) | |
| 216 | - multi_json (~> 1.3) | |
| 217 | - netrc (~> 0.7.7) | |
| 199 | + octokit (2.1.2) | |
| 200 | + sawyer (~> 0.5.0) | |
| 218 | 201 | omniauth (1.1.4) |
| 219 | 202 | hashie (>= 1.2, < 3) |
| 220 | 203 | rack |
| ... | ... | @@ -226,18 +209,6 @@ GEM |
| 226 | 209 | omniauth (~> 1.0) |
| 227 | 210 | origin (1.1.0) |
| 228 | 211 | orm_adapter (0.4.0) |
| 229 | - oruen_redmine_client (0.0.1) | |
| 230 | - activeresource (>= 2.3.0) | |
| 231 | - pivotal-tracker (0.5.12) | |
| 232 | - builder | |
| 233 | - builder | |
| 234 | - crack | |
| 235 | - happymapper (>= 0.3.2) | |
| 236 | - nokogiri (>= 1.4.3) | |
| 237 | - nokogiri (>= 1.5.5) | |
| 238 | - nokogiri-happymapper (>= 0.5.4) | |
| 239 | - rest-client (~> 1.6.0) | |
| 240 | - rest-client (~> 1.6.0) | |
| 241 | 212 | pjax_rails (0.3.4) |
| 242 | 213 | jquery-rails |
| 243 | 214 | poltergeist (1.4.1) |
| ... | ... | @@ -246,7 +217,7 @@ GEM |
| 246 | 217 | multi_json (~> 1.0) |
| 247 | 218 | websocket-driver (>= 0.2.0) |
| 248 | 219 | polyglot (0.3.3) |
| 249 | - premailer (1.7.3) | |
| 220 | + premailer (1.7.9) | |
| 250 | 221 | css_parser (>= 1.1.9) |
| 251 | 222 | htmlentities (>= 4.0.0) |
| 252 | 223 | pry (0.9.12.2) |
| ... | ... | @@ -276,8 +247,7 @@ GEM |
| 276 | 247 | activeresource (= 3.2.16) |
| 277 | 248 | activesupport (= 3.2.16) |
| 278 | 249 | bundler (~> 1.0) |
| 279 | - railties (= 3.2.16) | |
| 280 | - rails_autolink (1.1.4) | |
| 250 | + rails_autolink (1.1.5) | |
| 281 | 251 | rails (> 3.1) |
| 282 | 252 | railties (3.2.16) |
| 283 | 253 | actionpack (= 3.2.16) |
| ... | ... | @@ -316,13 +286,13 @@ GEM |
| 316 | 286 | ruby-debug-base (~> 0.10.4.0) |
| 317 | 287 | ruby-debug-base (0.10.4) |
| 318 | 288 | linecache (>= 0.3) |
| 319 | - ruby-fogbugz (0.1.1) | |
| 320 | - crack | |
| 321 | 289 | rushover (0.3.0) |
| 322 | 290 | json |
| 323 | 291 | rest-client |
| 324 | 292 | safe_yaml (0.9.7) |
| 325 | - simple_oauth (0.2.0) | |
| 293 | + sawyer (0.5.0) | |
| 294 | + addressable (~> 2.3.5) | |
| 295 | + faraday (~> 0.8, < 0.10) | |
| 326 | 296 | simplecov (0.7.1) |
| 327 | 297 | multi_json (~> 1.0) |
| 328 | 298 | simplecov-html (~> 0.7.1) |
| ... | ... | @@ -355,15 +325,15 @@ GEM |
| 355 | 325 | railties (> 3.2.8, < 4.0.0) |
| 356 | 326 | sprockets (>= 2.0.0) |
| 357 | 327 | tzinfo (0.3.38) |
| 358 | - uglifier (2.2.1) | |
| 328 | + uglifier (2.3.0) | |
| 359 | 329 | execjs (>= 0.3.0) |
| 360 | - multi_json (~> 1.0, >= 1.0.2) | |
| 330 | + json (>= 1.8.0) | |
| 361 | 331 | underscore-rails (1.5.2) |
| 362 | 332 | unicorn (4.6.3) |
| 363 | 333 | kgio (~> 2.6) |
| 364 | 334 | rack |
| 365 | 335 | raindrops (~> 0.7) |
| 366 | - useragent (0.8.3) | |
| 336 | + useragent (0.9.0) | |
| 367 | 337 | warden (1.2.3) |
| 368 | 338 | rack (>= 1.0) |
| 369 | 339 | webmock (1.15.0) |
| ... | ... | @@ -385,7 +355,6 @@ DEPENDENCIES |
| 385 | 355 | airbrake |
| 386 | 356 | better_errors |
| 387 | 357 | binding_of_caller |
| 388 | - bitbucket_rest_api | |
| 389 | 358 | campy |
| 390 | 359 | capistrano (~> 2.0) |
| 391 | 360 | capybara |
| ... | ... | @@ -396,30 +365,26 @@ DEPENDENCIES |
| 396 | 365 | devise |
| 397 | 366 | draper |
| 398 | 367 | email_spec |
| 368 | + errbit_github_plugin! | |
| 369 | + errbit_plugin! | |
| 399 | 370 | execjs |
| 400 | 371 | fabrication |
| 401 | 372 | flowdock |
| 402 | 373 | foreman |
| 403 | - gitlab (~> 3.0.0) | |
| 404 | 374 | haml |
| 405 | 375 | hipchat |
| 406 | 376 | hoi |
| 407 | 377 | hoptoad_notifier (~> 2.4) |
| 408 | 378 | htmlentities |
| 409 | 379 | httparty |
| 410 | - jira-ruby | |
| 411 | 380 | jquery-rails (~> 2.1.4) |
| 412 | 381 | kaminari (>= 0.14.1) |
| 413 | 382 | launchy |
| 414 | - lighthouse-api | |
| 415 | 383 | meta_request |
| 416 | 384 | mongoid |
| 417 | 385 | mongoid-rspec |
| 418 | 386 | mongoid_rails_migrations |
| 419 | - octokit (~> 1.18) | |
| 420 | 387 | omniauth-github |
| 421 | - oruen_redmine_client | |
| 422 | - pivotal-tracker | |
| 423 | 388 | pjax_rails |
| 424 | 389 | poltergeist |
| 425 | 390 | pry-rails |
| ... | ... | @@ -432,7 +397,6 @@ DEPENDENCIES |
| 432 | 397 | ri_cal |
| 433 | 398 | rspec-rails |
| 434 | 399 | ruby-debug |
| 435 | - ruby-fogbugz | |
| 436 | 400 | rushover |
| 437 | 401 | strong_parameters |
| 438 | 402 | therubyracer | ... | ... |
app/controllers/apps_controller.rb
| ... | ... | @@ -99,7 +99,7 @@ class AppsController < ApplicationController |
| 99 | 99 | |
| 100 | 100 | def plug_params app |
| 101 | 101 | app.watchers.build if app.watchers.none? |
| 102 | - app.issue_tracker = IssueTracker.new unless app.issue_tracker_configured? | |
| 102 | + app.issue_tracker ||= IssueTracker.new | |
| 103 | 103 | app.notification_service = NotificationService.new unless app.notification_service_configured? |
| 104 | 104 | app.copy_attributes_from(params[:copy_attributes_from]) if params[:copy_attributes_from] |
| 105 | 105 | end | ... | ... |
app/controllers/problems_controller.rb
| ... | ... | @@ -62,8 +62,7 @@ class ProblemsController < ApplicationController |
| 62 | 62 | end |
| 63 | 63 | |
| 64 | 64 | def create_issue |
| 65 | - IssueTracker.update_url_options(request) | |
| 66 | - issue_creation = IssueCreation.new(problem, current_user, params[:tracker]) | |
| 65 | + issue_creation = IssueCreation.new(problem, current_user, params[:tracker], request) | |
| 67 | 66 | |
| 68 | 67 | unless issue_creation.execute |
| 69 | 68 | flash[:error] = issue_creation.errors.full_messages.join(', ') | ... | ... |
app/decorators/issue_tracker_decorator.rb
| 1 | 1 | class IssueTrackerDecorator < Draper::Decorator |
| 2 | 2 | |
| 3 | + def initialize(object, key) | |
| 4 | + @object = object | |
| 5 | + @key = key | |
| 6 | + end | |
| 7 | + attr_reader :key | |
| 8 | + | |
| 3 | 9 | delegate_all |
| 4 | 10 | |
| 5 | 11 | def issue_trackers |
| 6 | - @issue_trackers ||= [ | |
| 7 | - IssueTracker::None, | |
| 8 | - IssueTracker.subclasses.select{|klass| klass != IssueTracker::None } | |
| 9 | - ].flatten | |
| 10 | - @issue_trackers.each do |it| | |
| 11 | - yield IssueTrackerDecorator.new(it) | |
| 12 | + @issue_trackers ||= ErrbitPlugin::Register.issue_trackers | |
| 13 | + @issue_trackers.each do |key, it| | |
| 14 | + yield IssueTrackerDecorator.new(it.new(app, {}), key) | |
| 12 | 15 | end |
| 13 | 16 | end |
| 14 | 17 | |
| 15 | 18 | def note |
| 16 | - object::Note.html_safe | |
| 19 | + object.note.html_safe | |
| 17 | 20 | end |
| 18 | 21 | |
| 19 | 22 | def fields |
| 20 | - object::Fields.each do |field, field_info| | |
| 23 | + object.fields.each do |field, field_info| | |
| 21 | 24 | yield IssueTrackerFieldDecorator.new(field, field_info) |
| 22 | 25 | end |
| 23 | 26 | end |
| ... | ... | @@ -29,7 +32,7 @@ class IssueTrackerDecorator < Draper::Decorator |
| 29 | 32 | private |
| 30 | 33 | |
| 31 | 34 | def choosen?(issue_tracker) |
| 32 | - object.to_s == issue_tracker._type ? 'chosen' : '' | |
| 35 | + key == issue_tracker.type_tracker.to_s ? 'chosen' : '' | |
| 33 | 36 | end |
| 34 | 37 | |
| 35 | 38 | end | ... | ... |
app/decorators/issue_tracker_field_decorator.rb
| ... | ... | @@ -13,10 +13,10 @@ class IssueTrackerFieldDecorator < Draper::Decorator |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | 15 | |
| 16 | - def input(form) | |
| 17 | - form.send(input_field, object, | |
| 16 | + def input(form, issue_tracker) | |
| 17 | + form.send(input_field, key.to_s, | |
| 18 | 18 | :placeholder => field_info[:placeholder], |
| 19 | - :value => form.object.send(object)) | |
| 19 | + :value => issue_tracker.options[key.to_s]) | |
| 20 | 20 | end |
| 21 | 21 | |
| 22 | 22 | private | ... | ... |
app/interactors/issue_creation.rb
| ... | ... | @@ -5,12 +5,24 @@ class IssueCreation |
| 5 | 5 | |
| 6 | 6 | delegate :app, :to => :problem |
| 7 | 7 | |
| 8 | - def initialize(problem, user, tracker_name) | |
| 8 | + def initialize(problem, user, tracker_name, request) | |
| 9 | 9 | @problem = problem |
| 10 | 10 | @user = user |
| 11 | 11 | @tracker_name = tracker_name |
| 12 | + IssueTracker.update_url_options(request) | |
| 12 | 13 | end |
| 13 | 14 | |
| 15 | + def execute | |
| 16 | + tracker.create_issue(problem, user) if tracker | |
| 17 | + errors.empty? | |
| 18 | + rescue => ex | |
| 19 | + Rails.logger.error "Error during issue creation: " << ex.message | |
| 20 | + errors.add :base, "There was an error during issue creation: #{ex.message}" | |
| 21 | + false | |
| 22 | + end | |
| 23 | + | |
| 24 | + private | |
| 25 | + | |
| 14 | 26 | def tracker |
| 15 | 27 | return @tracker if @tracker |
| 16 | 28 | |
| ... | ... | @@ -21,10 +33,11 @@ class IssueCreation |
| 21 | 33 | elsif !user.github_account? |
| 22 | 34 | errors.add :base, "You haven't linked your Github account." |
| 23 | 35 | else |
| 24 | - @tracker = GithubIssuesTracker.new( | |
| 25 | - :app => app, | |
| 26 | - :username => user.github_login, | |
| 27 | - :oauth_token => user.github_oauth_token | |
| 36 | + @tracker = ErrbitGithubPlugin::IssueTracker.new( | |
| 37 | + app, { | |
| 38 | + :username => user.github_login, | |
| 39 | + :oauth_token => user.github_oauth_token | |
| 40 | + } | |
| 28 | 41 | ) |
| 29 | 42 | end |
| 30 | 43 | |
| ... | ... | @@ -39,13 +52,4 @@ class IssueCreation |
| 39 | 52 | |
| 40 | 53 | @tracker |
| 41 | 54 | end |
| 42 | - | |
| 43 | - def execute | |
| 44 | - tracker.create_issue problem, user if tracker | |
| 45 | - errors.empty? | |
| 46 | - rescue => ex | |
| 47 | - Rails.logger.error "Error during issue creation: " << ex.message | |
| 48 | - errors.add :base, "There was an error during issue creation: #{ex.message}" | |
| 49 | - false | |
| 50 | - end | |
| 51 | 55 | end | ... | ... |
app/models/app.rb
| ... | ... | @@ -25,7 +25,7 @@ class App |
| 25 | 25 | |
| 26 | 26 | embeds_many :watchers |
| 27 | 27 | embeds_many :deploys |
| 28 | - embeds_one :issue_tracker | |
| 28 | + embeds_one :issue_tracker, :class_name => 'IssueTracker' | |
| 29 | 29 | embeds_one :notification_service |
| 30 | 30 | |
| 31 | 31 | has_many :problems, :inverse_of => :app, :dependent => :destroy |
| ... | ... | @@ -43,7 +43,7 @@ class App |
| 43 | 43 | accepts_nested_attributes_for :watchers, :allow_destroy => true, |
| 44 | 44 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } |
| 45 | 45 | accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, |
| 46 | - :reject_if => proc { |attrs| !IssueTracker.subclasses.map(&:to_s).include?(attrs[:type].to_s) } | |
| 46 | + :reject_if => proc { |attrs| !ErrbitPlugin::Register.issue_trackers.keys.map(&:to_s).include?(attrs[:type_tracker].to_s) } | |
| 47 | 47 | accepts_nested_attributes_for :notification_service, :allow_destroy => true, |
| 48 | 48 | :reject_if => proc { |attrs| !NotificationService.subclasses.map(&:to_s).include?(attrs[:type].to_s) } |
| 49 | 49 | |
| ... | ... | @@ -119,7 +119,7 @@ class App |
| 119 | 119 | |
| 120 | 120 | |
| 121 | 121 | def issue_tracker_configured? |
| 122 | - !!(issue_tracker.class < IssueTracker && issue_tracker.configured?) | |
| 122 | + !!issue_tracker && !!(issue_tracker.configured?) | |
| 123 | 123 | end |
| 124 | 124 | |
| 125 | 125 | def notification_service_configured? |
| ... | ... | @@ -174,6 +174,13 @@ class App |
| 174 | 174 | set(:api_key, SecureRandom.hex) |
| 175 | 175 | end |
| 176 | 176 | |
| 177 | + ## | |
| 178 | + # Check if comments can be allowed on this application | |
| 179 | + # | |
| 180 | + def comments_allowed? | |
| 181 | + !issue_tracker || issue_tracker.comments_allowed? | |
| 182 | + end | |
| 183 | + | |
| 177 | 184 | protected |
| 178 | 185 | |
| 179 | 186 | def store_cached_attributes_on_problems |
| ... | ... | @@ -199,5 +206,6 @@ class App |
| 199 | 206 | github_repo.sub!(/(git@|https?:\/\/)github\.com(\/|:)/, '') |
| 200 | 207 | github_repo.sub!(/\.git$/, '') |
| 201 | 208 | end |
| 209 | + | |
| 202 | 210 | end |
| 203 | 211 | ... | ... |
app/models/issue_tracker.rb
| 1 | 1 | class IssueTracker |
| 2 | 2 | include Mongoid::Document |
| 3 | 3 | include Mongoid::Timestamps |
| 4 | - include HashHelper | |
| 5 | - include Rails.application.routes.url_helpers | |
| 6 | 4 | |
| 7 | - Note = '' | |
| 5 | + include Rails.application.routes.url_helpers | |
| 8 | 6 | |
| 9 | 7 | default_url_options[:host] = ActionMailer::Base.default_url_options[:host] |
| 10 | 8 | |
| 11 | 9 | embedded_in :app, :inverse_of => :issue_tracker |
| 12 | 10 | |
| 13 | - field :project_id, :type => String | |
| 14 | - field :alt_project_id, :type => String # Specify an alternative project id. e.g. for viewing files | |
| 15 | - field :api_token, :type => String | |
| 16 | - field :account, :type => String | |
| 17 | - field :username, :type => String | |
| 18 | - field :password, :type => String | |
| 19 | - field :ticket_properties, :type => String | |
| 20 | - field :subdomain, :type => String | |
| 21 | - field :milestone_id, :type => String | |
| 22 | - | |
| 23 | - # Is there any better way to enhance the props? Putting them into the subclass leads to | |
| 24 | - # an error while rendering the form fields -.- | |
| 25 | - field :base_url, :type => String | |
| 26 | - field :context_path, :type => String | |
| 27 | - field :issue_type, :type => String | |
| 28 | - field :issue_component, :type => String | |
| 29 | - field :issue_priority, :type => String | |
| 30 | - | |
| 31 | - validate :check_params | |
| 32 | - | |
| 33 | - # Subclasses are responsible for overwriting this method. | |
| 34 | - def check_params; true; end | |
| 35 | - | |
| 36 | - def issue_title(problem) | |
| 37 | - "[#{ problem.environment }][#{ problem.where }] #{problem.message.to_s.truncate(100)}" | |
| 38 | - end | |
| 39 | - | |
| 40 | - # Allows us to set the issue tracker class from a single form. | |
| 41 | - def type; self._type; end | |
| 42 | - def type=(t); self._type=t; end | |
| 43 | - | |
| 44 | - def url; nil; end | |
| 45 | - | |
| 46 | - # Retrieve tracker label from either class or instance. | |
| 47 | - def self.label; self::Label; end | |
| 48 | - def label; self.class.label; end | |
| 49 | - | |
| 50 | - def configured? | |
| 51 | - project_id.present? | |
| 52 | - end | |
| 11 | + field :type_tracker, :type => String | |
| 12 | + field :options, :type => Hash, :default => {} | |
| 53 | 13 | |
| 54 | 14 | ## |
| 55 | 15 | # Update default_url_option with valid data from the request information |
| ... | ... | @@ -61,4 +21,14 @@ class IssueTracker |
| 61 | 21 | IssueTracker.default_url_options[:port] = request.port |
| 62 | 22 | IssueTracker.default_url_options[:protocol] = request.scheme |
| 63 | 23 | end |
| 24 | + | |
| 25 | + def tracker | |
| 26 | + @tracker ||= ErrbitPlugin::Register.issue_tracker(self.type_tracker).new(app, self.options) | |
| 27 | + rescue NameError | |
| 28 | + ErrbitPlugin::NoneIssueTracker.new(app, {}) | |
| 29 | + end | |
| 30 | + delegate :configured?, :to => :tracker | |
| 31 | + delegate :create_issue, :to => :tracker | |
| 32 | + delegate :label, :to => :tracker | |
| 33 | + delegate :comments_allowed?, :to => :tracker | |
| 64 | 34 | end | ... | ... |
app/models/issue_trackers/bitbucket_issues_tracker.rb
| ... | ... | @@ -1,55 +0,0 @@ |
| 1 | -begin | |
| 2 | - require 'bitbucket_rest_api' | |
| 3 | -rescue LoadError | |
| 4 | -end | |
| 5 | - | |
| 6 | -if defined? BitBucket | |
| 7 | - class IssueTrackers::BitbucketIssuesTracker < IssueTracker | |
| 8 | - Label = "bitbucket" | |
| 9 | - Note = 'Please configure your Bitbucket repository in the <strong>BITBUCKET REPO</strong> field above.' | |
| 10 | - Fields = [ | |
| 11 | - [:api_token, { | |
| 12 | - :placeholder => "Your username on Bitbucket account", | |
| 13 | - :label => "Username" | |
| 14 | - }], | |
| 15 | - [:project_id, { | |
| 16 | - :placeholder => "Password for your Bitbucket account", | |
| 17 | - :label => "Password" | |
| 18 | - }] | |
| 19 | - ] | |
| 20 | - | |
| 21 | - def check_params | |
| 22 | - if Fields.detect {|f| self[f[0]].blank? } | |
| 23 | - errors.add :base, 'You must specify your Bitbucket username and password' | |
| 24 | - end | |
| 25 | - end | |
| 26 | - | |
| 27 | - def repo_name | |
| 28 | - app.bitbucket_repo | |
| 29 | - end | |
| 30 | - | |
| 31 | - def create_issue(problem, reported_by = nil) | |
| 32 | - bitbucket = BitBucket.new :basic_auth => "#{api_token}:#{project_id}" | |
| 33 | - | |
| 34 | - begin | |
| 35 | - r_user = repo_name.split('/')[0] | |
| 36 | - r_name = repo_name.split('/')[1] | |
| 37 | - issue = bitbucket.issues.create r_user, r_name, :title => issue_title(problem), :content => body_template.result(binding), :priority => 'critical' | |
| 38 | - problem.update_attributes( | |
| 39 | - :issue_link => "https://bitbucket.org/#{repo_name}/issue/#{issue.local_id}/", | |
| 40 | - :issue_type => Label | |
| 41 | - ) | |
| 42 | - rescue BitBucket::Error::Unauthorized | |
| 43 | - raise IssueTrackers::AuthenticationError, "Could not authenticate with BitBucket. Please check your username and password." | |
| 44 | - end | |
| 45 | - end | |
| 46 | - | |
| 47 | - def body_template | |
| 48 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/bitbucket_issues_body.txt.erb")) | |
| 49 | - end | |
| 50 | - | |
| 51 | - def url | |
| 52 | - "https://www.bitbucket.org/#{repo_name}/issues" | |
| 53 | - end | |
| 54 | - end | |
| 55 | -end |
app/models/issue_trackers/fogbugz_tracker.rb
| ... | ... | @@ -1,53 +0,0 @@ |
| 1 | -if defined? Fogbugz | |
| 2 | - class IssueTrackers::FogbugzTracker < IssueTracker | |
| 3 | - Label = "fogbugz" | |
| 4 | - Fields = [ | |
| 5 | - [:project_id, { | |
| 6 | - :label => "Area Name" | |
| 7 | - }], | |
| 8 | - [:account, { | |
| 9 | - :label => "FogBugz URL", | |
| 10 | - :placeholder => "abc from http://abc.fogbugz.com/" | |
| 11 | - }], | |
| 12 | - [:username, { | |
| 13 | - :placeholder => "Username/Email for your account" | |
| 14 | - }], | |
| 15 | - [:password, { | |
| 16 | - :placeholder => "Password for your account" | |
| 17 | - }] | |
| 18 | - ] | |
| 19 | - | |
| 20 | - def check_params | |
| 21 | - if Fields.detect {|f| self[f[0]].blank? } | |
| 22 | - errors.add :base, 'You must specify your FogBugz Area Name, FogBugz URL, Username, and Password' | |
| 23 | - end | |
| 24 | - end | |
| 25 | - | |
| 26 | - def create_issue(problem, reported_by = nil) | |
| 27 | - fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com") | |
| 28 | - fogbugz.authenticate | |
| 29 | - | |
| 30 | - issue = {} | |
| 31 | - issue['sTitle'] = issue_title problem | |
| 32 | - issue['sArea'] = project_id | |
| 33 | - issue['sEvent'] = body_template.result(binding) | |
| 34 | - issue['sTags'] = ['errbit'].join(',') | |
| 35 | - issue['cols'] = ['ixBug'].join(',') | |
| 36 | - | |
| 37 | - fb_resp = fogbugz.command(:new, issue) | |
| 38 | - problem.update_attributes( | |
| 39 | - :issue_link => "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}", | |
| 40 | - :issue_type => Label | |
| 41 | - ) | |
| 42 | - | |
| 43 | - end | |
| 44 | - | |
| 45 | - def body_template | |
| 46 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/fogbugz_body.txt.erb")) | |
| 47 | - end | |
| 48 | - | |
| 49 | - def url | |
| 50 | - "http://#{account}.fogbugz.com/" | |
| 51 | - end | |
| 52 | - end | |
| 53 | -end |
app/models/issue_trackers/github_issues_tracker.rb
| ... | ... | @@ -1,61 +0,0 @@ |
| 1 | -if defined? Octokit | |
| 2 | - class IssueTrackers::GithubIssuesTracker < IssueTracker | |
| 3 | - Label = "github" | |
| 4 | - Note = 'Please configure your github repository in the <strong>GITHUB REPO</strong> field above.<br/>' << | |
| 5 | - 'Instead of providing your username & password, you can link your Github account ' << | |
| 6 | - 'to your user profile, and allow Errbit to create issues using your OAuth token.' | |
| 7 | - | |
| 8 | - Fields = [ | |
| 9 | - [:username, { | |
| 10 | - :placeholder => "Your username on GitHub" | |
| 11 | - }], | |
| 12 | - [:password, { | |
| 13 | - :placeholder => "Password for your account" | |
| 14 | - }] | |
| 15 | - ] | |
| 16 | - | |
| 17 | - attr_accessor :oauth_token | |
| 18 | - | |
| 19 | - def project_id | |
| 20 | - app.github_repo | |
| 21 | - end | |
| 22 | - | |
| 23 | - def check_params | |
| 24 | - if Fields.detect {|f| self[f[0]].blank? } | |
| 25 | - errors.add :base, 'You must specify your GitHub username and password' | |
| 26 | - end | |
| 27 | - end | |
| 28 | - | |
| 29 | - def create_issue(problem, reported_by = nil) | |
| 30 | - # Login using OAuth token, if given. | |
| 31 | - if oauth_token | |
| 32 | - client = Octokit::Client.new(:login => username, :oauth_token => oauth_token) | |
| 33 | - else | |
| 34 | - client = Octokit::Client.new(:login => username, :password => password) | |
| 35 | - end | |
| 36 | - | |
| 37 | - begin | |
| 38 | - issue = client.create_issue( | |
| 39 | - project_id, | |
| 40 | - issue_title(problem), | |
| 41 | - body_template.result(binding).unpack('C*').pack('U*') | |
| 42 | - ) | |
| 43 | - problem.update_attributes( | |
| 44 | - :issue_link => issue.html_url, | |
| 45 | - :issue_type => Label | |
| 46 | - ) | |
| 47 | - | |
| 48 | - rescue Octokit::Unauthorized | |
| 49 | - raise IssueTrackers::AuthenticationError, "Could not authenticate with GitHub. Please check your username and password." | |
| 50 | - end | |
| 51 | - end | |
| 52 | - | |
| 53 | - def body_template | |
| 54 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/github_issues_body.txt.erb").gsub(/^\s*/, '')) | |
| 55 | - end | |
| 56 | - | |
| 57 | - def url | |
| 58 | - "https://github.com/#{project_id}/issues" | |
| 59 | - end | |
| 60 | - end | |
| 61 | -end |
app/models/issue_trackers/gitlab_tracker.rb
| ... | ... | @@ -1,53 +0,0 @@ |
| 1 | -if defined? Gitlab | |
| 2 | - class IssueTrackers::GitlabTracker < IssueTracker | |
| 3 | - Label = "gitlab" | |
| 4 | - Fields = [ | |
| 5 | - [:account, { | |
| 6 | - :label => "Gitlab URL", | |
| 7 | - :placeholder => "e.g. https://example.net" | |
| 8 | - }], | |
| 9 | - [:api_token, { | |
| 10 | - :placeholder => "API Token for your account" | |
| 11 | - }], | |
| 12 | - [:project_id, { | |
| 13 | - :label => "Ticket Project ID (use Number)", | |
| 14 | - :placeholder => "Gitlab Project where issues will be created" | |
| 15 | - }], | |
| 16 | - [:alt_project_id, { | |
| 17 | - :label => "Project Name (namespace/project)", | |
| 18 | - :placeholder => "Gitlab Project where issues will be created" | |
| 19 | - }] | |
| 20 | - ] | |
| 21 | - | |
| 22 | - def check_params | |
| 23 | - if Fields.detect {|f| self[f[0]].blank?} | |
| 24 | - errors.add :base, 'You must specify your Gitlab URL, API token, Project ID and Project Name' | |
| 25 | - end | |
| 26 | - end | |
| 27 | - | |
| 28 | - def create_issue(problem, reported_by = nil) | |
| 29 | - Gitlab.configure do |config| | |
| 30 | - config.endpoint = "#{account}/api/v3" | |
| 31 | - config.private_token = api_token | |
| 32 | - config.user_agent = 'Errbit User Agent' | |
| 33 | - end | |
| 34 | - title = issue_title problem | |
| 35 | - description_summary = summary_template.result(binding) | |
| 36 | - description_body = body_template.result(binding) | |
| 37 | - ticket = Gitlab.create_issue(project_id, title, { :description => description_summary, :labels => "errbit" } ) | |
| 38 | - Gitlab.create_issue_note(project_id, ticket.id, description_body) | |
| 39 | - end | |
| 40 | - | |
| 41 | - def summary_template | |
| 42 | - @@summary_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/gitlab_summary.txt.erb").gsub(/^\s*/, '')) | |
| 43 | - end | |
| 44 | - | |
| 45 | - def body_template | |
| 46 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/gitlab_body.txt.erb").gsub(/^\s*/, '')) | |
| 47 | - end | |
| 48 | - | |
| 49 | - def url | |
| 50 | - "#{account}/#{alt_project_id}/issues" | |
| 51 | - end | |
| 52 | - end | |
| 53 | -end |
app/models/issue_trackers/jira_tracker.rb
| ... | ... | @@ -1,110 +0,0 @@ |
| 1 | -if defined? JIRA | |
| 2 | - class IssueTrackers::JiraTracker < IssueTracker | |
| 3 | - Label = 'jira' | |
| 4 | - | |
| 5 | - Fields = [ | |
| 6 | - [:base_url, { | |
| 7 | - :label => 'Jira URL without trailing slash', | |
| 8 | - :placeholder => 'https://jira.example.org/' | |
| 9 | - }], | |
| 10 | - [:context_path, { | |
| 11 | - :optional => true, | |
| 12 | - :label => 'Context Path (Just "/" if empty otherwise with leading slash)', | |
| 13 | - :placeholder => "/jira" | |
| 14 | - }], | |
| 15 | - [:username, { | |
| 16 | - :optional => true, | |
| 17 | - :label => 'HTTP Basic Auth User', | |
| 18 | - :placeholder => 'johndoe' | |
| 19 | - }], | |
| 20 | - [:password, { | |
| 21 | - :optional => true, | |
| 22 | - :label => 'HTTP Basic Auth Password', | |
| 23 | - :placeholder => 'p@assW0rd' | |
| 24 | - }], | |
| 25 | - [:project_id, { | |
| 26 | - :label => 'Project Key', | |
| 27 | - :placeholder => 'The project Key where the issue will be created' | |
| 28 | - }], | |
| 29 | - [:account, { | |
| 30 | - :optional => true, | |
| 31 | - :label => 'Assign to this user. If empty, Jira takes the project default.', | |
| 32 | - :placeholder => "username" | |
| 33 | - }], | |
| 34 | - [:issue_component, { | |
| 35 | - :label => 'Issue category', | |
| 36 | - :placeholder => 'Website - Other' | |
| 37 | - }], | |
| 38 | - [:issue_type, { | |
| 39 | - :label => 'Issue type', | |
| 40 | - :placeholder => 'Bug' | |
| 41 | - }], | |
| 42 | - [:issue_priority, { | |
| 43 | - :label => 'Priority', | |
| 44 | - :placeholder => 'Normal' | |
| 45 | - }] | |
| 46 | - ] | |
| 47 | - | |
| 48 | - def check_params | |
| 49 | - if Fields.detect { |f| self[f[0]].blank? && !f[1][:optional] } | |
| 50 | - errors.add :base, 'You must specify all non optional values!' | |
| 51 | - end | |
| 52 | - end | |
| 53 | - | |
| 54 | - | |
| 55 | - # | |
| 56 | - # @param problem Problem | |
| 57 | - def create_issue(problem, reported_by = nil) | |
| 58 | - options = { | |
| 59 | - :username => username, | |
| 60 | - :password => password, | |
| 61 | - :site => base_url, | |
| 62 | - :context_path => context_path, | |
| 63 | - :auth_type => :basic, | |
| 64 | - :use_ssl => base_url.match(/^https/) ? true : false | |
| 65 | - } | |
| 66 | - client = JIRA::Client.new(options) | |
| 67 | - | |
| 68 | - issue = { | |
| 69 | - :fields => { | |
| 70 | - :project => { | |
| 71 | - :key => project_id | |
| 72 | - }, | |
| 73 | - :summary => issue_title(problem), | |
| 74 | - :description => body_template.result(binding), | |
| 75 | - :issuetype => { | |
| 76 | - :name => issue_type | |
| 77 | - }, | |
| 78 | - :priority => { | |
| 79 | - :name => issue_priority, | |
| 80 | - }, | |
| 81 | - | |
| 82 | - :components => [{:name => issue_component}] | |
| 83 | - } | |
| 84 | - } | |
| 85 | - | |
| 86 | - issue[:fields][:assignee] = {:name => account} if account | |
| 87 | - | |
| 88 | - issue_build = client.Issue.build | |
| 89 | - issue_build.save(issue) | |
| 90 | - issue_build.fetch | |
| 91 | - | |
| 92 | - problem.update_attributes( | |
| 93 | - :issue_link => "#{base_url}#{context_path}browse/#{issue_build.key}", | |
| 94 | - :issue_type => Label | |
| 95 | - ) | |
| 96 | - | |
| 97 | - # Maybe in a later version? | |
| 98 | - #remote_link = { | |
| 99 | - # :url => app_problem_url(problem.app, problem), | |
| 100 | - # :name => "Link to Errbit Issue" | |
| 101 | - #} | |
| 102 | - #remote_link_build = issue_build.remotelink.build | |
| 103 | - #remote_link_build.save(remote_link) | |
| 104 | - end | |
| 105 | - | |
| 106 | - def body_template | |
| 107 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/jira_body.txt.erb")) | |
| 108 | - end | |
| 109 | - end | |
| 110 | -end | |
| 111 | 0 | \ No newline at end of file |
app/models/issue_trackers/lighthouse_tracker.rb
| ... | ... | @@ -1,53 +0,0 @@ |
| 1 | -if defined? Lighthouse | |
| 2 | - class IssueTrackers::LighthouseTracker < IssueTracker | |
| 3 | - Label = "lighthouseapp" | |
| 4 | - Fields = [ | |
| 5 | - [:account, { | |
| 6 | - :label => "Subdomain", | |
| 7 | - :placeholder => "subdomain from http://{{subdomain}}.lighthouseapp.com" | |
| 8 | - }], | |
| 9 | - [:api_token, { | |
| 10 | - :label => "API Token", | |
| 11 | - :placeholder => "123456789abcdef123456789abcdef" | |
| 12 | - }], | |
| 13 | - [:project_id, { | |
| 14 | - :label => "Project ID", | |
| 15 | - :placeholder => "123456" | |
| 16 | - }] | |
| 17 | - ] | |
| 18 | - | |
| 19 | - def check_params | |
| 20 | - if Fields.detect {|f| self[f[0]].blank? } | |
| 21 | - errors.add :base, 'You must specify your Lighthouseapp Subdomain, API token and Project ID' | |
| 22 | - end | |
| 23 | - end | |
| 24 | - | |
| 25 | - def create_issue(problem, reported_by = nil) | |
| 26 | - Lighthouse.account = account | |
| 27 | - Lighthouse.token = api_token | |
| 28 | - # updating lighthouse account | |
| 29 | - Lighthouse::Ticket.site | |
| 30 | - Lighthouse::Ticket.format = :xml | |
| 31 | - ticket = Lighthouse::Ticket.new(:project_id => project_id) | |
| 32 | - ticket.title = issue_title problem | |
| 33 | - | |
| 34 | - ticket.body = body_template.result(binding) | |
| 35 | - | |
| 36 | - ticket.tags << "errbit" | |
| 37 | - ticket.save! | |
| 38 | - problem.update_attributes( | |
| 39 | - :issue_link => "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, ''), | |
| 40 | - :issue_type => Label | |
| 41 | - ) | |
| 42 | - | |
| 43 | - end | |
| 44 | - | |
| 45 | - def body_template | |
| 46 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/lighthouseapp_body.txt.erb").gsub(/^\s*/, '')) | |
| 47 | - end | |
| 48 | - | |
| 49 | - def url | |
| 50 | - "http://#{account}.lighthouseapp.com" | |
| 51 | - end | |
| 52 | - end | |
| 53 | -end | |
| 54 | 0 | \ No newline at end of file |
app/models/issue_trackers/mingle_tracker.rb
| ... | ... | @@ -1,71 +0,0 @@ |
| 1 | -class IssueTrackers::MingleTracker < IssueTracker | |
| 2 | - Label = "mingle" | |
| 3 | - Note = "Note: <strong>CARD PROPERTIES</strong> must be comma separated <em>key = value</em> pairs" | |
| 4 | - | |
| 5 | - Fields = [ | |
| 6 | - [:account, { | |
| 7 | - :label => "Mingle URL", | |
| 8 | - :placeholder => "http://mingle.example.com/" | |
| 9 | - }], | |
| 10 | - [:project_id, { | |
| 11 | - :placeholder => "Mingle project" | |
| 12 | - }], | |
| 13 | - [:ticket_properties, { | |
| 14 | - :label => "Card Properties", | |
| 15 | - :placeholder => "card_type = Defect, defect_status = Open, priority = Essential" | |
| 16 | - }], | |
| 17 | - [:username, { | |
| 18 | - :label => "Sign-in name", | |
| 19 | - :placeholder => "Sign-in name for your account" | |
| 20 | - }], | |
| 21 | - [:password, { | |
| 22 | - :placeholder => "Password for your account" | |
| 23 | - }] | |
| 24 | - ] | |
| 25 | - | |
| 26 | - def check_params | |
| 27 | - if Fields.detect {|f| self[f[0]].blank? } or !ticket_properties_hash["card_type"] | |
| 28 | - errors.add :base, 'You must specify your Mingle URL, Project ID, Card Type (in default card properties), Sign-in name, and Password' | |
| 29 | - end | |
| 30 | - end | |
| 31 | - | |
| 32 | - def create_issue(problem, reported_by = nil) | |
| 33 | - properties = ticket_properties_hash | |
| 34 | - basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@") | |
| 35 | - Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/" | |
| 36 | - | |
| 37 | - card = Mingle::Card.new | |
| 38 | - card.card_type_name = properties.delete("card_type") | |
| 39 | - card.name = issue_title(problem) | |
| 40 | - card.description = body_template.result(binding) | |
| 41 | - properties.each do |property, value| | |
| 42 | - card.send("cp_#{property}=", value) | |
| 43 | - end | |
| 44 | - | |
| 45 | - card.save! | |
| 46 | - problem.update_attributes( | |
| 47 | - :issue_link => URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s, | |
| 48 | - :issue_type => Label | |
| 49 | - ) | |
| 50 | - end | |
| 51 | - | |
| 52 | - def body_template | |
| 53 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/textile_body.txt.erb")) | |
| 54 | - end | |
| 55 | - | |
| 56 | - def ticket_properties_hash | |
| 57 | - # Parses 'key=value, key2=value2' from ticket_properties into a ruby hash. | |
| 58 | - self.ticket_properties.to_s.split(",").inject({}) do |hash, pair| | |
| 59 | - key, value = pair.split("=").map(&:strip) | |
| 60 | - hash[key] = value | |
| 61 | - hash | |
| 62 | - end | |
| 63 | - end | |
| 64 | - | |
| 65 | - def url | |
| 66 | - acc_url = account.start_with?('http') ? account : "http://#{account}" | |
| 67 | - URI.parse("#{acc_url}/projects/#{project_id}").to_s | |
| 68 | - rescue URI::InvalidURIError | |
| 69 | - end | |
| 70 | -end | |
| 71 | - |
app/models/issue_trackers/none.rb
app/models/issue_trackers/pivotal_labs_tracker.rb
| ... | ... | @@ -1,43 +0,0 @@ |
| 1 | -if defined? PivotalTracker | |
| 2 | - class IssueTrackers::PivotalLabsTracker < IssueTracker | |
| 3 | - Label = "pivotal" | |
| 4 | - Fields = [ | |
| 5 | - [:api_token, { | |
| 6 | - :placeholder => "API Token for your account" | |
| 7 | - }], | |
| 8 | - [:project_id, {}] | |
| 9 | - ] | |
| 10 | - | |
| 11 | - def check_params | |
| 12 | - if Fields.detect {|f| self[f[0]].blank? } | |
| 13 | - errors.add :base, 'You must specify your Pivotal Tracker API token and Project ID' | |
| 14 | - end | |
| 15 | - end | |
| 16 | - | |
| 17 | - def create_issue(problem, reported_by = nil) | |
| 18 | - PivotalTracker::Client.token = api_token | |
| 19 | - PivotalTracker::Client.use_ssl = true | |
| 20 | - project = PivotalTracker::Project.find project_id.to_i | |
| 21 | - story = project.stories.create :name => issue_title(problem), | |
| 22 | - :story_type => 'bug', :description => body_template.result(binding), | |
| 23 | - :requested_by => reported_by.name | |
| 24 | - | |
| 25 | - if story.errors.present? | |
| 26 | - raise IssueTrackers::IssueTrackerError, story.errors.first | |
| 27 | - else | |
| 28 | - problem.update_attributes( | |
| 29 | - :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}", | |
| 30 | - :issue_type => Label | |
| 31 | - ) | |
| 32 | - end | |
| 33 | - end | |
| 34 | - | |
| 35 | - def body_template | |
| 36 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/pivotal_body.txt.erb")) | |
| 37 | - end | |
| 38 | - | |
| 39 | - def url | |
| 40 | - "https://www.pivotaltracker.com/" | |
| 41 | - end | |
| 42 | - end | |
| 43 | -end | |
| 44 | 0 | \ No newline at end of file |
app/models/issue_trackers/redmine_tracker.rb
| ... | ... | @@ -1,75 +0,0 @@ |
| 1 | -if defined? RedmineClient | |
| 2 | - class IssueTrackers::RedmineTracker < IssueTracker | |
| 3 | - Label = "redmine" | |
| 4 | - Fields = [ | |
| 5 | - [:account, { | |
| 6 | - :label => "Redmine URL", | |
| 7 | - :placeholder => "http://www.redmine.org/" | |
| 8 | - }], | |
| 9 | - [:api_token, { | |
| 10 | - :placeholder => "API Token for your account" | |
| 11 | - }], | |
| 12 | - [:username, { | |
| 13 | - :placeholder => "Your username" | |
| 14 | - }], | |
| 15 | - [:password, { | |
| 16 | - :placeholder => "Your password" | |
| 17 | - }], | |
| 18 | - [:project_id, { | |
| 19 | - :label => "Ticket Project", | |
| 20 | - :placeholder => "Redmine Project where tickets will be created" | |
| 21 | - }], | |
| 22 | - [:alt_project_id, { | |
| 23 | - :optional => true, | |
| 24 | - :label => "App Project", | |
| 25 | - :placeholder => "Where app's files & revisions can be viewed. (Leave blank to use the above project by default)" | |
| 26 | - }] | |
| 27 | - ] | |
| 28 | - | |
| 29 | - def check_params | |
| 30 | - if Fields.detect {|f| self[f[0]].blank? && !f[1][:optional]} | |
| 31 | - errors.add :base, 'You must specify your Redmine URL, API token, Username, Password and Project ID' | |
| 32 | - end | |
| 33 | - end | |
| 34 | - | |
| 35 | - def create_issue(problem, reported_by = nil) | |
| 36 | - token = api_token | |
| 37 | - acc = account | |
| 38 | - user = username | |
| 39 | - passwd = password | |
| 40 | - RedmineClient::Base.configure do | |
| 41 | - self.token = token | |
| 42 | - self.user = user | |
| 43 | - self.password = passwd | |
| 44 | - self.site = acc | |
| 45 | - self.format = :xml | |
| 46 | - end | |
| 47 | - issue = RedmineClient::Issue.new(:project_id => project_id) | |
| 48 | - issue.subject = issue_title problem | |
| 49 | - issue.description = body_template.result(binding) | |
| 50 | - issue.save! | |
| 51 | - problem.update_attributes( | |
| 52 | - :issue_link => "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}"), | |
| 53 | - :issue_type => Label | |
| 54 | - ) | |
| 55 | - end | |
| 56 | - | |
| 57 | - def url_to_file(file_path, line_number = nil) | |
| 58 | - # alt_project_id let's users specify a different project for tickets / app files. | |
| 59 | - project = self.alt_project_id.present? ? self.alt_project_id : self.project_id | |
| 60 | - url = "#{self.account.gsub(/\/$/, '')}/projects/#{project}/repository/revisions/#{app.repository_branch}/changes/#{file_path.sub(/\[PROJECT_ROOT\]/, '').sub(/^\//,'')}" | |
| 61 | - line_number ? url << "#L#{line_number}" : url | |
| 62 | - end | |
| 63 | - | |
| 64 | - def body_template | |
| 65 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/redmine_body.txt.erb")) | |
| 66 | - end | |
| 67 | - | |
| 68 | - def url | |
| 69 | - acc_url = account.start_with?('http') ? account : "http://#{account}" | |
| 70 | - acc_url = acc_url.gsub(/\/$/, '') | |
| 71 | - URI.parse("#{acc_url}/projects/#{project_id}").to_s | |
| 72 | - rescue URI::InvalidURIError | |
| 73 | - end | |
| 74 | - end | |
| 75 | -end |
app/models/issue_trackers/unfuddle_tracker.rb
| ... | ... | @@ -1,69 +0,0 @@ |
| 1 | -class IssueTrackers::UnfuddleTracker < IssueTracker | |
| 2 | - Label = "unfuddle" | |
| 3 | - Fields = [ | |
| 4 | - | |
| 5 | - [:account, { | |
| 6 | - :placeholder => "Your domain" | |
| 7 | - }], | |
| 8 | - | |
| 9 | - | |
| 10 | - [:username, { | |
| 11 | - :placeholder => "Your username" | |
| 12 | - }], | |
| 13 | - | |
| 14 | - [:password, { | |
| 15 | - :placeholder => "Your password" | |
| 16 | - }], | |
| 17 | - | |
| 18 | - [:project_id, { | |
| 19 | - :label => "Ticket Project", | |
| 20 | - :placeholder => "Project where tickets will be created" | |
| 21 | - }], | |
| 22 | - | |
| 23 | - [:milestone_id, { | |
| 24 | - :optional => true, | |
| 25 | - :label => "Ticket Milestone", | |
| 26 | - :placeholder => "Milestone where tickets will be created" | |
| 27 | - }] | |
| 28 | - | |
| 29 | - | |
| 30 | - ] | |
| 31 | - | |
| 32 | - def check_params | |
| 33 | - if Fields.detect {|f| self[f[0]].blank? && !f[1][:optional]} | |
| 34 | - errors.add :base, 'You must specify your Account, Username, Password and Project ID' | |
| 35 | - end | |
| 36 | - end | |
| 37 | - | |
| 38 | - def create_issue(problem, reported_by = nil) | |
| 39 | - | |
| 40 | - Unfuddle.config(account, username, password) | |
| 41 | - begin | |
| 42 | - issue_options = {:project_id => project_id, | |
| 43 | - :summary => issue_title(problem), | |
| 44 | - :priority => '5', | |
| 45 | - :status => "new", | |
| 46 | - :description => body_template.result(binding), | |
| 47 | - 'description-format' => 'textile' } | |
| 48 | - | |
| 49 | - issue_options[:milestone_id] = milestone_id if milestone_id.present? | |
| 50 | - | |
| 51 | - issue = Unfuddle::Ticket.create(issue_options) | |
| 52 | - problem.update_attributes( | |
| 53 | - :issue_link => File.join("#{url}/tickets/#{issue.id}"), | |
| 54 | - :issue_type => Label | |
| 55 | - ) | |
| 56 | - rescue ActiveResource::UnauthorizedAccess | |
| 57 | - raise ActiveResource::UnauthorizedAccess, "Could not authenticate with Unfuddle. Please check your username and password." | |
| 58 | - end | |
| 59 | - | |
| 60 | - end | |
| 61 | - | |
| 62 | - def body_template | |
| 63 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/textile_body.txt.erb")) | |
| 64 | - end | |
| 65 | - | |
| 66 | - def url | |
| 67 | - "https://#{account}.unfuddle.com/projects/#{project_id}" | |
| 68 | - end | |
| 69 | -end |
app/models/problem.rb
app/views/apps/_issue_tracker_fields.html.haml
| 1 | 1 | %fieldset |
| 2 | 2 | %legend= t('.legend') |
| 3 | - = f.fields_for :issue_tracker do |w| | |
| 3 | + = f.fields_for :issue_tracker do |issue_tracker_form| | |
| 4 | 4 | %div.issue_tracker.nested |
| 5 | 5 | %div.choose |
| 6 | - - w.object.issue_trackers do |tracker| | |
| 7 | - = w.label :type, :class => "label_radio #{tracker.label}", :value => tracker.name do | |
| 8 | - = w.radio_button :type, tracker.name, 'data-section' => tracker.label | |
| 6 | + - issue_tracker_form.object.issue_trackers do |tracker| | |
| 7 | + = issue_tracker_form.label :type_tracker, :class => "label_radio #{tracker.label}", :value => tracker.key do | |
| 8 | + = issue_tracker_form.radio_button :type_tracker, tracker.key, 'data-section' => tracker.label | |
| 9 | 9 | = tracker.label |
| 10 | 10 | |
| 11 | - - w.object.issue_trackers do |tracker| | |
| 12 | - %div.tracker_params{:class => tracker.params_class(w.object)} | |
| 13 | - %p= tracker.note | |
| 14 | - - tracker.fields do |field| | |
| 15 | - = w.label field.key, field.label | |
| 16 | - = field.input(w) | |
| 11 | + - issue_tracker_form.object.issue_trackers do |tracker| | |
| 12 | + %div.tracker_params{:class => tracker.params_class(issue_tracker_form.object)} | |
| 13 | + = issue_tracker_form.fields_for(:options) do |options_form| | |
| 14 | + %p= tracker.note | |
| 15 | + - tracker.fields do |field| | |
| 16 | + = options_form.label field.key, field.label | |
| 17 | + = field.input(options_form, issue_tracker_form.object) | |
| 17 | 18 | |
| 18 | 19 | .image_preloader |
| 19 | - - w.object.issue_trackers do |tracker| | |
| 20 | + - issue_tracker_form.object.issue_trackers do |tracker| | |
| 20 | 21 | = image_tag "#{tracker.label}_inactive.png" |
| 21 | 22 | = image_tag "#{tracker.label}_create.png" |
| 22 | 23 | ... | ... |
| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | +class ExtractIssueTracker < Mongoid::Migration | |
| 2 | + | |
| 3 | + def self.up | |
| 4 | + App.collection.find.each do |app| | |
| 5 | + if app['issue_tracker'] && !app['issue_tracker'].empty? | |
| 6 | + it = app['issue_tracker'] | |
| 7 | + it['type_tracker'] = 'IssueTrackers::BitbucketIssuesTracker' | |
| 8 | + it['options'] = app['issue_tracker'].dup | |
| 9 | + it.delete('_type') | |
| 10 | + App.collection.find( | |
| 11 | + :_id => app['_id'] | |
| 12 | + ).update(app) | |
| 13 | + end | |
| 14 | + end | |
| 15 | + end | |
| 16 | + | |
| 17 | + def self.down | |
| 18 | + end | |
| 19 | +end | ... | ... |
spec/acceptance/app_regenerate_api_key_spec.rb
| ... | ... | @@ -72,17 +72,17 @@ feature "Create an application" do |
| 72 | 72 | log_in admin |
| 73 | 73 | click_on I18n.t('apps.index.new_app') |
| 74 | 74 | fill_in 'app_name', :with => 'My new app' |
| 75 | - find('.label_radio.bitbucket').click | |
| 76 | - within ".bitbucket.tracker_params" do | |
| 77 | - fill_in 'app_issue_tracker_attributes_api_token', :with => 'token' | |
| 78 | - fill_in 'app_issue_tracker_attributes_project_id', :with => 'pass' | |
| 75 | + find('.label_radio.github').click | |
| 76 | + within ".github.tracker_params" do | |
| 77 | + fill_in 'app_issue_tracker_attributes_options_username', :with => 'token' | |
| 78 | + fill_in 'app_issue_tracker_attributes_options_password', :with => 'pass' | |
| 79 | 79 | end |
| 80 | 80 | click_on I18n.t('apps.new.add_app') |
| 81 | 81 | expect(page.has_content?(I18n.t('controllers.apps.flash.create.success'))).to eql true |
| 82 | 82 | app = App.where(:name => 'My new app').first |
| 83 | - expect(app.issue_tracker).to be_a IssueTracker::BitbucketIssuesTracker | |
| 84 | - expect(app.issue_tracker.api_token).to eql 'token' | |
| 85 | - expect(app.issue_tracker.project_id).to eql 'pass' | |
| 83 | + expect(app.issue_tracker.type_tracker).to eql 'IssueTrackers::GithubIssuesTracker' | |
| 84 | + expect(app.issue_tracker.options['username']).to eql 'token' | |
| 85 | + expect(app.issue_tracker.options['password']).to eql 'pass' | |
| 86 | 86 | |
| 87 | 87 | click_on I18n.t('shared.navigation.apps') |
| 88 | 88 | click_on 'My new app' |
| ... | ... | @@ -91,6 +91,6 @@ feature "Create an application" do |
| 91 | 91 | click_on I18n.t('apps.edit.update') |
| 92 | 92 | expect(page.has_content?(I18n.t('controllers.apps.flash.update.success'))).to eql true |
| 93 | 93 | app = App.where(:name => 'My new app').first |
| 94 | - expect(app.issue_tracker).to be_a IssueTracker::None | |
| 94 | + expect(app.issue_tracker.tracker).to be_a ErrbitPlugin::NoneIssueTracker | |
| 95 | 95 | end |
| 96 | 96 | end | ... | ... |
spec/controllers/apps_controller_spec.rb
| ... | ... | @@ -289,7 +289,7 @@ describe AppsController do |
| 289 | 289 | context "unknown tracker type" do |
| 290 | 290 | before(:each) do |
| 291 | 291 | put :update, :id => @app.id, :app => { :issue_tracker_attributes => { |
| 292 | - :type => 'unknown', :project_id => '1234', :api_token => '123123', :account => 'myapp' | |
| 292 | + :type_tracker => 'unknown', :options => {:project_id => '1234', :api_token => '123123', :account => 'myapp'} | |
| 293 | 293 | } } |
| 294 | 294 | @app.reload |
| 295 | 295 | end |
| ... | ... | @@ -299,24 +299,24 @@ describe AppsController do |
| 299 | 299 | end |
| 300 | 300 | end |
| 301 | 301 | |
| 302 | - IssueTracker.subclasses.each do |tracker_klass| | |
| 303 | - context tracker_klass do | |
| 302 | + ErrbitPlugin::Register.issue_trackers.each do |key, klass| | |
| 303 | + context key do | |
| 304 | 304 | it "should save tracker params" do |
| 305 | - params = tracker_klass::Fields.inject({}){|hash,f| hash[f[0]] = "test_value"; hash } | |
| 306 | - params[:ticket_properties] = "card_type = defect" if tracker_klass == MingleTracker | |
| 307 | - params[:type] = tracker_klass.to_s | |
| 305 | + issue_tracker_klass = klass.new(@app, {}) | |
| 306 | + params = { | |
| 307 | + :options => issue_tracker_klass.fields.inject({}){|hash,f| hash[f[0]] = "test_value"; hash }, | |
| 308 | + :type_tracker => key.dup.to_s | |
| 309 | + } | |
| 308 | 310 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} |
| 309 | 311 | |
| 310 | 312 | @app.reload |
| 311 | 313 | |
| 312 | 314 | tracker = @app.issue_tracker |
| 313 | - expect(tracker).to be_a(tracker_klass) | |
| 314 | - tracker_klass::Fields.each do |field, field_info| | |
| 315 | + expect(tracker.tracker).to be_a(ErrbitPlugin::Register.issue_tracker(key)) | |
| 316 | + issue_tracker_klass.fields.each do |field, field_info| | |
| 315 | 317 | case field |
| 316 | - when :ticket_properties | |
| 317 | - expect(tracker.send(field.to_sym)).to eq 'card_type = defect' | |
| 318 | - else | |
| 319 | - expect(tracker.send(field.to_sym)).to eq 'test_value' | |
| 318 | + when :ticket_properties; tracker.send(field.to_sym).should == 'card_type = defect' | |
| 319 | + else tracker.options[field.to_s].should == 'test_value' | |
| 320 | 320 | end |
| 321 | 321 | end |
| 322 | 322 | end |
| ... | ... | @@ -324,8 +324,11 @@ describe AppsController do |
| 324 | 324 | it "should show validation notice when sufficient params are not present" do |
| 325 | 325 | # Leave out one required param |
| 326 | 326 | # TODO. previous test was not relevant because one params can be enough. So put noone |
| 327 | - params = {:type => tracker_klass.to_s } | |
| 328 | - put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} | |
| 327 | + put :update, :id => @app.id, :app => { | |
| 328 | + :issue_tracker_attributes => { | |
| 329 | + :type_tracker => key.dup.to_s | |
| 330 | + } | |
| 331 | + } | |
| 329 | 332 | |
| 330 | 333 | @app.reload |
| 331 | 334 | expect(@app.issue_tracker_configured?).to eq false | ... | ... |
spec/controllers/problems_controller_spec.rb
| ... | ... | @@ -8,15 +8,16 @@ describe ProblemsController do |
| 8 | 8 | :params => {:app_id => 'dummyid', :id => 'dummyid'} |
| 9 | 9 | |
| 10 | 10 | let(:app) { Fabricate(:app) } |
| 11 | - let(:err) { Fabricate(:err, :problem => Fabricate(:problem, :app => app, :environment => "production")) } | |
| 11 | + let(:err) { Fabricate(:err, :problem => problem) } | |
| 12 | + let(:admin) { Fabricate(:admin) } | |
| 13 | + let(:problem) { Fabricate(:problem, :app => app, :environment => "production") } | |
| 12 | 14 | |
| 13 | 15 | |
| 14 | 16 | describe "GET /problems" do |
| 15 | 17 | #render_views |
| 16 | 18 | context 'when logged in as an admin' do |
| 17 | 19 | before(:each) do |
| 18 | - @user = Fabricate(:admin) | |
| 19 | - sign_in @user | |
| 20 | + sign_in admin | |
| 20 | 21 | @problem = Fabricate(:notice, :err => Fabricate(:err, :problem => Fabricate(:problem, :app => app, :environment => "production"))).problem |
| 21 | 22 | end |
| 22 | 23 | |
| ... | ... | @@ -31,7 +32,7 @@ describe ProblemsController do |
| 31 | 32 | end |
| 32 | 33 | |
| 33 | 34 | it "should be able to override default per_page value" do |
| 34 | - @user.update_attribute :per_page, 10 | |
| 35 | + admin.update_attribute :per_page, 10 | |
| 35 | 36 | get :index |
| 36 | 37 | expect(controller.problems.to_a.size).to eq 10 |
| 37 | 38 | end |
| ... | ... | @@ -98,7 +99,7 @@ describe ProblemsController do |
| 98 | 99 | describe "GET /problems - previously all" do |
| 99 | 100 | context 'when logged in as an admin' do |
| 100 | 101 | it "gets a paginated list of all problems" do |
| 101 | - sign_in Fabricate(:admin) | |
| 102 | + sign_in admin | |
| 102 | 103 | problems = Kaminari.paginate_array((1..30).to_a) |
| 103 | 104 | 3.times { problems << Fabricate(:err).problem } |
| 104 | 105 | 3.times { problems << Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)).problem } |
| ... | ... | @@ -128,7 +129,7 @@ describe ProblemsController do |
| 128 | 129 | |
| 129 | 130 | context 'when logged in as an admin' do |
| 130 | 131 | before do |
| 131 | - sign_in Fabricate(:admin) | |
| 132 | + sign_in admin | |
| 132 | 133 | end |
| 133 | 134 | |
| 134 | 135 | it "finds the app" do |
| ... | ... | @@ -192,7 +193,7 @@ describe ProblemsController do |
| 192 | 193 | |
| 193 | 194 | describe "PUT /apps/:app_id/problems/:id/resolve" do |
| 194 | 195 | before do |
| 195 | - sign_in Fabricate(:admin) | |
| 196 | + sign_in admin | |
| 196 | 197 | |
| 197 | 198 | @problem = Fabricate(:err) |
| 198 | 199 | App.stub(:find).with(@problem.app.id.to_s).and_return(@problem.app) |
| ... | ... | @@ -231,76 +232,50 @@ describe ProblemsController do |
| 231 | 232 | end |
| 232 | 233 | |
| 233 | 234 | describe "POST /apps/:app_id/problems/:id/create_issue" do |
| 234 | - #render_views | |
| 235 | 235 | |
| 236 | 236 | before(:each) do |
| 237 | - sign_in Fabricate(:admin) | |
| 237 | + sign_in admin | |
| 238 | 238 | end |
| 239 | 239 | |
| 240 | 240 | context "successful issue creation" do |
| 241 | 241 | context "lighthouseapp tracker" do |
| 242 | 242 | let(:notice) { Fabricate :notice } |
| 243 | - let(:tracker) { Fabricate :lighthouse_tracker, :app => notice.app } | |
| 244 | 243 | let(:problem) { notice.problem } |
| 245 | 244 | |
| 246 | 245 | before(:each) do |
| 247 | - number = 5 | |
| 248 | - @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | |
| 249 | - body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | |
| 250 | - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). | |
| 251 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | |
| 252 | - | |
| 246 | + controller.stub(:problem).and_return(problem) | |
| 247 | + controller.stub(:current_user).and_return(admin) | |
| 248 | + IssueCreation.should_receive(:new).with(problem, admin, nil, request).and_return(double(:execute => true)) | |
| 253 | 249 | post :create_issue, :app_id => problem.app.id, :id => problem.id |
| 254 | - problem.reload | |
| 255 | 250 | end |
| 256 | 251 | |
| 257 | 252 | it "should redirect to problem page" do |
| 258 | 253 | expect(response).to redirect_to( app_problem_path(problem.app, problem) ) |
| 254 | + expect(flash[:error]).to be_blank | |
| 259 | 255 | end |
| 260 | 256 | end |
| 261 | 257 | end |
| 262 | 258 | |
| 263 | - context "absent issue tracker" do | |
| 264 | - let(:problem) { Fabricate :problem } | |
| 265 | - | |
| 259 | + context "error during request to a tracker" do | |
| 266 | 260 | before(:each) do |
| 261 | + IssueCreation.should_receive(:new).with(problem, admin, nil, request).and_return( | |
| 262 | + double(:execute => false, :errors => double(:full_messages => ['not create'])) | |
| 263 | + ) | |
| 264 | + controller.stub(:problem).and_return(problem) | |
| 267 | 265 | post :create_issue, :app_id => problem.app.id, :id => problem.id |
| 268 | 266 | end |
| 269 | 267 | |
| 270 | 268 | it "should redirect to problem page" do |
| 271 | 269 | expect(response).to redirect_to( app_problem_path(problem.app, problem) ) |
| 272 | - end | |
| 273 | - | |
| 274 | - it "should set flash error message telling issue tracker of the app doesn't exist" do | |
| 275 | - expect(flash[:error]).to eq "This app has no issue tracker setup." | |
| 270 | + expect(flash[:error]).to eql 'not create' | |
| 276 | 271 | end |
| 277 | 272 | end |
| 278 | 273 | |
| 279 | - context "error during request to a tracker" do | |
| 280 | - context "lighthouseapp tracker" do | |
| 281 | - let(:tracker) { Fabricate :lighthouse_tracker } | |
| 282 | - let(:err) { Fabricate(:err, :problem => Fabricate(:problem, :app => tracker.app)) } | |
| 283 | - | |
| 284 | - before(:each) do | |
| 285 | - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 500) | |
| 286 | - | |
| 287 | - post :create_issue, :app_id => err.app.id, :id => err.problem.id | |
| 288 | - end | |
| 289 | - | |
| 290 | - it "should redirect to problem page" do | |
| 291 | - expect(response).to redirect_to( app_problem_path(err.app, err.problem) ) | |
| 292 | - end | |
| 293 | - | |
| 294 | - it "should notify of connection error" do | |
| 295 | - expect(flash[:error]).to include("There was an error during issue creation:") | |
| 296 | - end | |
| 297 | - end | |
| 298 | - end | |
| 299 | 274 | end |
| 300 | 275 | |
| 301 | 276 | describe "DELETE /apps/:app_id/problems/:id/unlink_issue" do |
| 302 | 277 | before(:each) do |
| 303 | - sign_in Fabricate(:admin) | |
| 278 | + sign_in admin | |
| 304 | 279 | end |
| 305 | 280 | |
| 306 | 281 | context "problem with issue" do |
| ... | ... | @@ -336,7 +311,7 @@ describe ProblemsController do |
| 336 | 311 | |
| 337 | 312 | describe "Bulk Actions" do |
| 338 | 313 | before(:each) do |
| 339 | - sign_in Fabricate(:admin) | |
| 314 | + sign_in admin | |
| 340 | 315 | @problem1 = Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)).problem |
| 341 | 316 | @problem2 = Fabricate(:err, :problem => Fabricate(:problem, :resolved => false)).problem |
| 342 | 317 | end | ... | ... |
spec/decorators/issue_tracker_decorator_spec.rb
| ... | ... | @@ -2,37 +2,28 @@ require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe IssueTrackerDecorator do |
| 4 | 4 | |
| 5 | - class Foo | |
| 6 | - Note = 'hello <strong></strong>' | |
| 7 | - Fields = [ | |
| 8 | - [:foo, :bar] | |
| 9 | - ] | |
| 10 | - Label = 'foo' | |
| 11 | - def self.label; Label; end | |
| 12 | - def _type | |
| 13 | - 'Foo' | |
| 14 | - end | |
| 5 | + let(:none_tracker) do | |
| 6 | + ErrbitPlugin::NoneIssueTracker.new(Object.new, 'none') | |
| 15 | 7 | end |
| 16 | 8 | |
| 17 | - class Bar | |
| 18 | - Label = 'bar' | |
| 19 | - def self.label; Label; end | |
| 20 | - def _type | |
| 21 | - 'Bar' | |
| 22 | - end | |
| 9 | + let(:tracker) do | |
| 10 | + ErrbitPlugin::FakeIssueTracker.new(Object.new, 'fake') | |
| 23 | 11 | end |
| 24 | 12 | |
| 25 | - describe "#note" do | |
| 13 | + let(:decorator) do | |
| 14 | + IssueTrackerDecorator.new(tracker, 'fake') | |
| 15 | + end | |
| 26 | 16 | |
| 17 | + describe "#note" do | |
| 27 | 18 | |
| 28 | 19 | it 'return the html_safe of Note' do |
| 29 | - expect(IssueTrackerDecorator.new(Foo).note).to eql 'hello <strong></strong>'.html_safe | |
| 20 | + expect(decorator.note).to eql tracker.note | |
| 30 | 21 | end |
| 31 | 22 | end |
| 32 | 23 | |
| 33 | 24 | describe "#issue_trackers" do |
| 34 | 25 | it 'return an array of IssueTrackerDecorator' do |
| 35 | - IssueTrackerDecorator.new(Foo).issue_trackers do |it| | |
| 26 | + decorator.issue_trackers do |it| | |
| 36 | 27 | expect(it).to be_a(IssueTrackerDecorator) |
| 37 | 28 | end |
| 38 | 29 | end |
| ... | ... | @@ -40,20 +31,20 @@ describe IssueTrackerDecorator do |
| 40 | 31 | |
| 41 | 32 | describe "#fields" do |
| 42 | 33 | it 'return all Fields define decorate' do |
| 43 | - IssueTrackerDecorator.new(Foo).fields do |itf| | |
| 34 | + decorator.fields do |itf| | |
| 44 | 35 | expect(itf).to be_a(IssueTrackerFieldDecorator) |
| 45 | - expect(itf.object).to eql :foo | |
| 46 | - expect(itf.field_info).to eql :bar | |
| 36 | + expect([:foo, :bar]).to be_include(itf.object) | |
| 37 | + expect([{:label => 'foo'}, {:label => 'bar'}]).to be_include(itf.field_info) | |
| 47 | 38 | end |
| 48 | 39 | end |
| 49 | 40 | end |
| 50 | 41 | |
| 51 | 42 | describe "#params_class" do |
| 52 | 43 | it 'add the label in class' do |
| 53 | - expect(IssueTrackerDecorator.new(Bar).params_class(Foo.new)).to eql 'bar' | |
| 44 | + expect(decorator.params_class(IssueTracker.new(:type_tracker => 'none'))).to eql 'fake' | |
| 54 | 45 | end |
| 55 | 46 | it 'add chosen class if _type is same' do |
| 56 | - expect(IssueTrackerDecorator.new(Foo).params_class(Foo.new)).to eql 'chosen foo' | |
| 47 | + expect(decorator.params_class(IssueTracker.new(:type_tracker => 'fake'))).to eql 'chosen fake' | |
| 57 | 48 | end |
| 58 | 49 | end |
| 59 | 50 | end | ... | ... |
spec/fabricators/issue_tracker_fabricator.rb
| 1 | 1 | Fabricator :issue_tracker do |
| 2 | 2 | app |
| 3 | - api_token { sequence :word } | |
| 4 | - project_id { sequence :word } | |
| 5 | - account { sequence :word } | |
| 6 | - username { sequence :word } | |
| 7 | - password { sequence :word } | |
| 8 | -end | |
| 9 | - | |
| 10 | -%w(lighthouse pivotal_labs fogbugz).each do |t| | |
| 11 | - Fabricator "#{t}_tracker".to_sym, :from => :issue_tracker, :class_name => "IssueTrackers::#{t.camelcase}Tracker" | |
| 12 | -end | |
| 13 | - | |
| 14 | -Fabricator :redmine_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::RedmineTracker" do | |
| 15 | - account 'http://redmine.example.com' | |
| 16 | -end | |
| 17 | - | |
| 18 | -Fabricator :gitlab_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::GitlabTracker" do | |
| 19 | - account 'http://gitlab.example.com' | |
| 20 | - alt_project_id 'foo' | |
| 21 | -end | |
| 22 | - | |
| 23 | -Fabricator :mingle_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::MingleTracker" do | |
| 24 | - account 'https://mingle.example.com' | |
| 25 | - ticket_properties 'card_type = Defect, defect_status = open, priority = essential' | |
| 26 | -end | |
| 27 | - | |
| 28 | -Fabricator :github_issues_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::GithubIssuesTracker" do | |
| 29 | - project_id 'test_account/test_project' | |
| 30 | - username 'test_username' | |
| 31 | -end | |
| 32 | - | |
| 33 | -Fabricator :bitbucket_issues_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::BitbucketIssuesTracker" do | |
| 34 | - project_id 'password' | |
| 35 | - api_token 'test_username' | |
| 36 | -end | |
| 37 | - | |
| 38 | -Fabricator :unfuddle_issues_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::UnfuddleTracker" do | |
| 39 | - account 'test' | |
| 40 | - project_id 15 | |
| 41 | 3 | end | ... | ... |
spec/interactors/issue_creation_spec.rb
| 1 | 1 | require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe IssueCreation do |
| 4 | - subject(:issue_creation) { IssueCreation.new(problem, user, tracker_name) } | |
| 4 | + class FakeIssueTracker | |
| 5 | + def initialize(app, params); end | |
| 6 | + def configured?; true; end | |
| 7 | + def create_issue(problem,user) ; true; end | |
| 8 | + end | |
| 9 | + subject(:issue_creation) do | |
| 10 | + IssueCreation.new(problem, user, tracker_name, request) | |
| 11 | + end | |
| 5 | 12 | |
| 13 | + let(:request) do | |
| 14 | + double(:request, | |
| 15 | + :host => 'github.com', | |
| 16 | + :port => '80', | |
| 17 | + :scheme => 'http' | |
| 18 | + ) | |
| 19 | + end | |
| 6 | 20 | let(:problem) { notice.problem } |
| 7 | 21 | let(:notice) { Fabricate(:notice) } |
| 8 | 22 | let(:user) { Fabricate(:admin) } |
| ... | ... | @@ -15,8 +29,9 @@ describe IssueCreation do |
| 15 | 29 | end |
| 16 | 30 | |
| 17 | 31 | it 'creates an issue if issue tracker is configured' do |
| 18 | - tracker = Fabricate(:lighthouse_tracker, :app => notice.app) | |
| 19 | - expect(tracker).to receive(:create_issue) | |
| 32 | + a = problem.app | |
| 33 | + a.build_issue_tracker | |
| 34 | + expect(ErrbitPlugin::Register).to receive(:issue_tracker).and_return(FakeIssueTracker) | |
| 20 | 35 | issue_creation.execute |
| 21 | 36 | expect(errors).to be_empty |
| 22 | 37 | end |
| ... | ... | @@ -44,7 +59,9 @@ describe IssueCreation do |
| 44 | 59 | user.github_oauth_token = 'oauthtoken' |
| 45 | 60 | user.save! |
| 46 | 61 | |
| 47 | - GithubIssuesTracker.any_instance.should_receive(:create_issue) | |
| 62 | + ErrbitGithubPlugin::IssueTracker.should_receive(:new).and_return( | |
| 63 | + double(:create_issue => true) | |
| 64 | + ) | |
| 48 | 65 | issue_creation.execute |
| 49 | 66 | expect(errors).to be_empty |
| 50 | 67 | end | ... | ... |
spec/interactors/problem_updater_cache_spec.rb
| ... | ... | @@ -74,7 +74,7 @@ describe ProblemUpdaterCache do |
| 74 | 74 | end |
| 75 | 75 | |
| 76 | 76 | it 'update last_notice_at' do |
| 77 | - expect(problem.last_notice_at.to_i).to be_within(1).of(notice.created_at.to_i) | |
| 77 | + expect(problem.last_notice_at.to_i).to be_within(2).of(notice.created_at.to_i) | |
| 78 | 78 | end |
| 79 | 79 | |
| 80 | 80 | it 'update stats messages' do | ... | ... |
spec/models/app_spec.rb
| 1 | 1 | require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe App do |
| 4 | + | |
| 4 | 5 | context "Attributes" do |
| 5 | 6 | it { should have_field(:_id).of_type(String) } |
| 6 | 7 | it { should have_field(:name).of_type(String) } |
| ... | ... | @@ -220,5 +221,26 @@ describe App do |
| 220 | 221 | end |
| 221 | 222 | end |
| 222 | 223 | |
| 224 | + describe "#comments_allowed?" do | |
| 225 | + context "without issue_tracker" do | |
| 226 | + it 'return true' do | |
| 227 | + expect(App.new.comments_allowed?).to be_true | |
| 228 | + end | |
| 229 | + end | |
| 230 | + | |
| 231 | + context "with issue_tracker" do | |
| 232 | + let(:issue_tracker) do | |
| 233 | + ist = IssueTracker.new | |
| 234 | + ist.stub(:comments_allowed?).and_return('foo') | |
| 235 | + ist | |
| 236 | + end | |
| 237 | + it 'delegate to issue_tracker' do | |
| 238 | + expect(App.new( | |
| 239 | + :issue_tracker => issue_tracker | |
| 240 | + ).comments_allowed?).to eql 'foo' | |
| 241 | + end | |
| 242 | + end | |
| 243 | + end | |
| 244 | + | |
| 223 | 245 | end |
| 224 | 246 | ... | ... |
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe IssueTracker do | |
| 4 | + describe "Association" do | |
| 5 | + it { should be_embedded_in(:app) } | |
| 6 | + end | |
| 7 | + | |
| 8 | + describe "Attributes" do | |
| 9 | + it { should have_field(:type_tracker).of_type(String) } | |
| 10 | + it { should have_field(:options).of_type(Hash).with_default_value_of({}) } | |
| 11 | + end | |
| 12 | + | |
| 13 | + describe "#tracker" do | |
| 14 | + context "with type_tracker class not exist" do | |
| 15 | + it 'return NullIssueTracker' do | |
| 16 | + expect(IssueTracker.new(:type_tracker => 'Foo').tracker).to be_a ErrbitPlugin::NoneIssueTracker | |
| 17 | + end | |
| 18 | + end | |
| 19 | + end | |
| 20 | +end | ... | ... |
spec/models/issue_trackers/bitbucket_issues_tracker_spec.rb
| ... | ... | @@ -1,40 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::BitbucketIssuesTracker do | |
| 4 | - it "should create an issue on BitBucket Issues with problem params, and set issue link for problem" do | |
| 5 | - repo = "test_user/test_repo" | |
| 6 | - notice = Fabricate :notice | |
| 7 | - notice.app.bitbucket_repo = repo | |
| 8 | - tracker = Fabricate :bitbucket_issues_tracker, :app => notice.app | |
| 9 | - problem = notice.problem | |
| 10 | - | |
| 11 | - number = 123 | |
| 12 | - @issue_link = "https://bitbucket.org/#{repo}/issue/#{number}/" | |
| 13 | - body = <<EOF | |
| 14 | -{ | |
| 15 | - "status": "new", | |
| 16 | - "priority": "critical", | |
| 17 | - "title": "[production][foo#bar] FooError: Too Much Bar", | |
| 18 | - "comment_count": 0, | |
| 19 | - "content": "This is the content", | |
| 20 | - "created_on": "2012-07-29 04:35:38", | |
| 21 | - "local_id": 123, | |
| 22 | - "follower_count": 0, | |
| 23 | - "utc_created_on": "2012-07-29 02:35:38+00:00", | |
| 24 | - "resource_uri": "/1.0/repositories/test_user/test_repo/issue/123/", | |
| 25 | - "is_spam": false | |
| 26 | -} | |
| 27 | -EOF | |
| 28 | - | |
| 29 | - stub_request(:post, "https://#{tracker.api_token}:#{tracker.project_id}@bitbucket.org/api/1.0/repositories/test_user/test_repo/issues/").to_return(:status => 200, :headers => {}, :body => body ) | |
| 30 | - | |
| 31 | - problem.app.issue_tracker.create_issue(problem) | |
| 32 | - problem.reload | |
| 33 | - | |
| 34 | - requested = have_requested(:post, "https://#{tracker.api_token}:#{tracker.project_id}@bitbucket.org/api/1.0/repositories/test_user/test_repo/issues/") | |
| 35 | - expect(WebMock).to requested.with(:title => /[production][foo#bar] FooError: Too Much Bar/) | |
| 36 | - expect(WebMock).to requested.with(:content => /See this exception on Errbit/) | |
| 37 | - | |
| 38 | - expect(problem.issue_link).to eq @issue_link | |
| 39 | - end | |
| 40 | -end |
spec/models/issue_trackers/fogbugz_tracker_spec.rb
| ... | ... | @@ -1,23 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::FogbugzTracker do | |
| 4 | - it "should create an issue on Fogbugz with problem params, and set issue link for problem" do | |
| 5 | - notice = Fabricate :notice | |
| 6 | - tracker = Fabricate :fogbugz_tracker, :app => notice.app | |
| 7 | - problem = notice.problem | |
| 8 | - | |
| 9 | - number = 123 | |
| 10 | - @issue_link = "https://#{tracker.account}.fogbugz.com/default.asp?#{number}" | |
| 11 | - response = "<response><token>12345</token><case><ixBug>123</ixBug></case></response>" | |
| 12 | - http_mock = double | |
| 13 | - expect(http_mock).to receive(:new).and_return(http_mock) | |
| 14 | - expect(http_mock).to receive(:request).twice.and_return(response) | |
| 15 | - Fogbugz.adapter[:http] = http_mock | |
| 16 | - | |
| 17 | - problem.app.issue_tracker.create_issue(problem) | |
| 18 | - problem.reload | |
| 19 | - | |
| 20 | - expect(problem.issue_link).to eq @issue_link | |
| 21 | - end | |
| 22 | -end | |
| 23 | - |
spec/models/issue_trackers/github_issues_tracker_spec.rb
| ... | ... | @@ -1,47 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::GithubIssuesTracker do | |
| 4 | - it "should create an issue on GitHub Issues with problem params, and set issue link for problem" do | |
| 5 | - repo = "test_user/test_repo" | |
| 6 | - notice = Fabricate :notice | |
| 7 | - notice.app.github_repo = repo | |
| 8 | - tracker = Fabricate :github_issues_tracker, :app => notice.app | |
| 9 | - problem = notice.problem | |
| 10 | - | |
| 11 | - number = 5 | |
| 12 | - @issue_link = "https://github.com/#{repo}/issues/#{number}" | |
| 13 | - body = <<EOF | |
| 14 | -{ | |
| 15 | - "position": 1.0, | |
| 16 | - "number": #{number}, | |
| 17 | - "votes": 0, | |
| 18 | - "created_at": "2010/01/21 13:45:59 -0800", | |
| 19 | - "comments": 0, | |
| 20 | - "body": "Test Body", | |
| 21 | - "title": "Test Issue", | |
| 22 | - "user": "test_user", | |
| 23 | - "state": "open", | |
| 24 | - "html_url": "#{@issue_link}" | |
| 25 | -} | |
| 26 | -EOF | |
| 27 | - | |
| 28 | - stub_request(:post, | |
| 29 | - "https://#{tracker.username}:#{tracker.password}@api.github.com/repos/#{repo}/issues"). | |
| 30 | - to_return(:status => 201, | |
| 31 | - :headers => { | |
| 32 | - 'Location' => @issue_link, | |
| 33 | - 'Content-Type' => 'application/json', | |
| 34 | - }, | |
| 35 | - :body => body ) | |
| 36 | - | |
| 37 | - problem.app.issue_tracker.create_issue(problem) | |
| 38 | - problem.reload | |
| 39 | - | |
| 40 | - requested = have_requested(:post, "https://#{tracker.username}:#{tracker.password}@api.github.com/repos/#{repo}/issues") | |
| 41 | - expect(WebMock).to requested.with(:body => /[production][foo#bar] FooError: Too Much Bar/) | |
| 42 | - expect(WebMock).to requested.with(:body => /See this exception on Errbit/) | |
| 43 | - | |
| 44 | - expect(problem.issue_link).to eq @issue_link | |
| 45 | - end | |
| 46 | -end | |
| 47 | - |
spec/models/issue_trackers/gitlab_issues_tracker_spec.rb
| ... | ... | @@ -1,32 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::GitlabTracker do | |
| 4 | - it "should create an issue on Gitlab with problem params" do | |
| 5 | - notice = Fabricate :notice | |
| 6 | - tracker = Fabricate :gitlab_tracker, :app => notice.app | |
| 7 | - problem = notice.problem | |
| 8 | - | |
| 9 | - issue_id = 5 | |
| 10 | - note_id = 10 | |
| 11 | - issue_body = {:id => issue_id, :title => "[production][foo#bar] FooError: Too Much Bar", :description => "[See this exception on Errbit]"}.to_json | |
| 12 | - note_body = {:id => note_id, :body => "Example note body"}.to_json | |
| 13 | - | |
| 14 | - stub_request(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues?private_token=#{tracker.api_token}"). | |
| 15 | - with(:body => /.+/, :headers => {'Accept'=>'application/json'}). | |
| 16 | - to_return(:status => 200, :body => issue_body, :headers => {'Accept'=>'application/json'}) | |
| 17 | - | |
| 18 | - stub_request(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues/#{issue_id}/notes?private_token=#{tracker.api_token}"). | |
| 19 | - with(:body => /.+/, :headers => {'Accept'=>'application/json'}). | |
| 20 | - to_return(:status => 200, :body => note_body, :headers => {'Accept'=>'application/json'}) | |
| 21 | - | |
| 22 | - problem.app.issue_tracker.create_issue(problem) | |
| 23 | - problem.reload | |
| 24 | - | |
| 25 | - requested_issue = have_requested(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues?private_token=#{tracker.api_token}").with(:body => /.+/, :headers => {'Accept'=>'application/json'}) | |
| 26 | - requested_note = have_requested(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues/#{issue_id}/notes?private_token=#{tracker.api_token}") | |
| 27 | - expect(WebMock).to requested_issue.with(:body => /%5Bproduction%5D%5Bfoo%23bar%5D%20FooError%3A%20Too%20Much%20Bar/) | |
| 28 | - expect(WebMock).to requested_issue.with(:body => /See%20this%20exception%20on%20Errbit/) | |
| 29 | - | |
| 30 | - end | |
| 31 | -end | |
| 32 | - |
spec/models/issue_trackers/lighthouse_tracker_spec.rb
| ... | ... | @@ -1,27 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::LighthouseTracker do | |
| 4 | - it "should create an issue on Lighthouse with problem params, and set issue link for problem" do | |
| 5 | - notice = Fabricate :notice | |
| 6 | - tracker = Fabricate :lighthouse_tracker, :app => notice.app | |
| 7 | - problem = notice.problem | |
| 8 | - | |
| 9 | - number = 5 | |
| 10 | - @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | |
| 11 | - body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | |
| 12 | - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). | |
| 13 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | |
| 14 | - | |
| 15 | - problem.app.issue_tracker.create_issue(problem) | |
| 16 | - problem.reload | |
| 17 | - | |
| 18 | - requested = have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") | |
| 19 | - expect(WebMock).to requested.with(:headers => {'X-Lighthousetoken' => tracker.api_token}) | |
| 20 | - expect(WebMock).to requested.with(:body => /<tag>errbit<\/tag>/) | |
| 21 | - expect(WebMock).to requested.with(:body => /<title>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/title>/) | |
| 22 | - expect(WebMock).to requested.with(:body => /<body>.+<\/body>/m) | |
| 23 | - | |
| 24 | - expect(problem.issue_link).to eq @issue_link.sub(/\.xml$/, '') | |
| 25 | - end | |
| 26 | -end | |
| 27 | - |
spec/models/issue_trackers/mingle_tracker_spec.rb
| ... | ... | @@ -1,28 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::MingleTracker do | |
| 4 | - it "should create an issue on Mingle with problem params, and set issue link for problem" do | |
| 5 | - notice = Fabricate :notice | |
| 6 | - tracker = Fabricate :mingle_tracker, :app => notice.app | |
| 7 | - problem = notice.problem | |
| 8 | - | |
| 9 | - number = 5 | |
| 10 | - @issue_link = "#{tracker.account}/projects/#{tracker.project_id}/cards/#{number}.xml" | |
| 11 | - @basic_auth = tracker.account.gsub("://", "://#{tracker.username}:#{tracker.password}@") | |
| 12 | - body = "<card><id type=\"integer\">#{number}</id></card>" | |
| 13 | - stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml"). | |
| 14 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | |
| 15 | - | |
| 16 | - problem.app.issue_tracker.create_issue(problem) | |
| 17 | - problem.reload | |
| 18 | - | |
| 19 | - requested = have_requested(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml") | |
| 20 | - expect(WebMock).to requested.with(:headers => {'Content-Type' => 'application/xml'}) | |
| 21 | - expect(WebMock).to requested.with(:body => /FooError: Too Much Bar/) | |
| 22 | - expect(WebMock).to requested.with(:body => /See this exception on Errbit/) | |
| 23 | - expect(WebMock).to requested.with(:body => /<card-type-name>Defect<\/card-type-name>/) | |
| 24 | - | |
| 25 | - expect(problem.issue_link).to eq @issue_link.sub(/\.xml$/, '') | |
| 26 | - end | |
| 27 | -end | |
| 28 | - |
spec/models/issue_trackers/pivotal_labs_tracker_spec.rb
| ... | ... | @@ -1,46 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::PivotalLabsTracker do | |
| 4 | - | |
| 5 | - let(:user) { Fabricate(:user) } | |
| 6 | - let(:notice) { Fabricate(:notice) } | |
| 7 | - let(:tracker) { Fabricate :pivotal_labs_tracker, :app => notice.app, :project_id => 10 } | |
| 8 | - let(:problem) { notice.problem } | |
| 9 | - let(:story_id) { 5 } | |
| 10 | - let(:issue_link) { "https://www.pivotaltracker.com/story/show/#{story_id}" } | |
| 11 | - | |
| 12 | - it "creates an issue on Pivotal Tracker with problem params, and set issue link for problem" do | |
| 13 | - project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>" | |
| 14 | - stub_request(:get, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}"). | |
| 15 | - to_return(:status => 200, :headers => {'Location' => issue_link}, :body => project_body ) | |
| 16 | - story_body = "<story><name>Test Story</name><id>#{story_id}</id></story>" | |
| 17 | - stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories"). | |
| 18 | - to_return(:status => 201, :headers => {'Location' => issue_link}, :body => story_body ) | |
| 19 | - | |
| 20 | - problem.app.issue_tracker.create_issue(problem, user) | |
| 21 | - problem.reload | |
| 22 | - | |
| 23 | - requested = have_requested(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories") | |
| 24 | - expect(WebMock).to requested.with(:headers => {'X-Trackertoken' => tracker.api_token}) | |
| 25 | - expect(WebMock).to requested.with(:body => /See this exception on Errbit/) | |
| 26 | - expect(WebMock).to requested.with(:body => /<name>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/name>/) | |
| 27 | - expect(WebMock).to requested.with(:body => /<description>.+<\/description>/m) | |
| 28 | - | |
| 29 | - expect(problem.issue_link).to eq issue_link | |
| 30 | - end | |
| 31 | - | |
| 32 | - it "raises IssueTrackers::IssueTrackerError exception when invalid params and does not set issue link for problem" do | |
| 33 | - project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>" | |
| 34 | - stub_request(:get, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}"). | |
| 35 | - to_return(:status => 200, :body => project_body ) | |
| 36 | - story_body = "<errors><error>Requested by can't be blank</error><error>Requested by can't be blank</error></errors>" | |
| 37 | - stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories"). | |
| 38 | - to_return(:status => 422, :body => story_body ) | |
| 39 | - | |
| 40 | - expect{ | |
| 41 | - problem.app.issue_tracker.create_issue(problem, user) | |
| 42 | - }.to raise_exception(IssueTrackers::IssueTrackerError, "Requested by can't be blank") | |
| 43 | - expect(problem.issue_link).to be_nil | |
| 44 | - end | |
| 45 | -end | |
| 46 | - |
spec/models/issue_trackers/redmine_tracker_spec.rb
| ... | ... | @@ -1,45 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::RedmineTracker do | |
| 4 | - it "should create an issue on Redmine with problem params, and set issue link for problem" do | |
| 5 | - notice = Fabricate(:notice) | |
| 6 | - tracker = Fabricate(:redmine_tracker, :app => notice.app, :project_id => 10) | |
| 7 | - problem = notice.problem | |
| 8 | - number = 5 | |
| 9 | - @issue_link = "#{tracker.account}/issues/#{number}.xml?project_id=#{tracker.project_id}" | |
| 10 | - body = "<issue><subject>my subject</subject><id>#{number}</id></issue>" | |
| 11 | - | |
| 12 | - # Build base url with account URL, and username/password basic auth | |
| 13 | - base_url = tracker.account.gsub 'http://', "http://#{tracker.username}:#{tracker.password}@" | |
| 14 | - | |
| 15 | - stub_request(:post, "#{base_url}/issues.xml"). | |
| 16 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | |
| 17 | - | |
| 18 | - problem.app.issue_tracker.create_issue(problem) | |
| 19 | - problem.reload | |
| 20 | - | |
| 21 | - requested = have_requested(:post, "#{base_url}/issues.xml") | |
| 22 | - expect(WebMock).to requested.with(:headers => {'X-Redmine-API-Key' => tracker.api_token}) | |
| 23 | - expect(WebMock).to requested.with(:body => /<project-id>#{tracker.project_id}<\/project-id>/) | |
| 24 | - expect(WebMock).to requested.with(:body => /<subject>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/subject>/) | |
| 25 | - expect(WebMock).to requested.with(:body => /<description>.+<\/description>/m) | |
| 26 | - | |
| 27 | - expect(problem.issue_link).to eq @issue_link.sub(/\.xml/, '') | |
| 28 | - end | |
| 29 | - | |
| 30 | - it "should generate a url where a file with line number can be viewed" do | |
| 31 | - t = Fabricate(:redmine_tracker, :account => 'http://redmine.example.com', :project_id => "errbit") | |
| 32 | - expect(t.url_to_file("/example/file")). | |
| 33 | - to eq 'http://redmine.example.com/projects/errbit/repository/revisions/master/changes/example/file' | |
| 34 | - expect(t.url_to_file("/example/file", 25)). | |
| 35 | - to eq 'http://redmine.example.com/projects/errbit/repository/revisions/master/changes/example/file#L25' | |
| 36 | - end | |
| 37 | - | |
| 38 | - it "should use the alt_project_id to generate a file/linenumber url, if given" do | |
| 39 | - t = Fabricate(:redmine_tracker, :account => 'http://redmine.example.com', | |
| 40 | - :project_id => "errbit", | |
| 41 | - :alt_project_id => "actual_project") | |
| 42 | - expect(t.url_to_file("/example/file", 25)). | |
| 43 | - to eq 'http://redmine.example.com/projects/actual_project/repository/revisions/master/changes/example/file#L25' | |
| 44 | - end | |
| 45 | -end |
spec/models/issue_trackers/unfuddle_issues_tracker_spec.rb
| ... | ... | @@ -1,92 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe IssueTrackers::UnfuddleTracker do | |
| 4 | - | |
| 5 | - let(:issue_link) { "https://test.unfuddle.com/projects/15/tickets/2436" } | |
| 6 | - let(:notice) { Fabricate :notice } | |
| 7 | - let(:tracker) { Fabricate :unfuddle_issues_tracker, :app => notice.app } | |
| 8 | - let(:problem) { notice.problem } | |
| 9 | - | |
| 10 | - it "should create an issue on Unfuddle Issues with problem params, and set issue link for problem" do | |
| 11 | -project_xml = <<EOF | |
| 12 | -<?xml version="1.0" encoding="UTF-8"?> | |
| 13 | -<project> | |
| 14 | - <account-id type="integer">1</account-id> | |
| 15 | - <archived type="boolean">false</archived> | |
| 16 | - <assignee-on-resolve>reporter</assignee-on-resolve> | |
| 17 | - <backup-frequency type="integer">0</backup-frequency> | |
| 18 | - <close-ticket-simultaneously-default type="boolean">false</close-ticket-simultaneously-default> | |
| 19 | - <default-ticket-report-id type="integer" nil="true"></default-ticket-report-id> | |
| 20 | - <description nil="true"></description> | |
| 21 | - <disk-usage type="integer">27932</disk-usage> | |
| 22 | - <enable-time-tracking type="boolean">true</enable-time-tracking> | |
| 23 | - <id type="integer">#{tracker.project_id}</id> | |
| 24 | - <s3-access-key-id></s3-access-key-id> | |
| 25 | - <s3-backup-enabled type="boolean">false</s3-backup-enabled> | |
| 26 | - <s3-bucket-name></s3-bucket-name> | |
| 27 | - <short-name>test-project</short-name> | |
| 28 | - <theme>blue</theme> | |
| 29 | - <ticket-field1-active type="boolean">false</ticket-field1-active> | |
| 30 | - <ticket-field1-disposition>text</ticket-field1-disposition> | |
| 31 | - <ticket-field1-title>Field 1</ticket-field1-title> | |
| 32 | - <ticket-field2-active type="boolean">false</ticket-field2-active> | |
| 33 | - <ticket-field2-disposition>text</ticket-field2-disposition> | |
| 34 | - <ticket-field2-title>Field 2</ticket-field2-title> | |
| 35 | - <ticket-field3-active type="boolean">false</ticket-field3-active> | |
| 36 | - <ticket-field3-disposition>text</ticket-field3-disposition> | |
| 37 | - <ticket-field3-title>Field 3</ticket-field3-title> | |
| 38 | - <title>test-project</title> | |
| 39 | - <created-at>2011-04-25T09:21:43Z</created-at> | |
| 40 | - <updated-at>2013-03-08T08:03:02Z</updated-at> | |
| 41 | -</project> | |
| 42 | -EOF | |
| 43 | - | |
| 44 | - ticket_xml =<<EOF | |
| 45 | -<?xml version="1.0" encoding="UTF-8"?> | |
| 46 | -<ticket> | |
| 47 | - <assignee-id type="integer">40</assignee-id> | |
| 48 | - <component-id type="integer" nil="true"></component-id> | |
| 49 | - <description nil="true"></description> | |
| 50 | - <description-format>markdown</description-format> | |
| 51 | - <due-on type="date" nil="true"></due-on> | |
| 52 | - <field1-value-id type="integer" nil="true"></field1-value-id> | |
| 53 | - <field2-value-id type="integer" nil="true"></field2-value-id> | |
| 54 | - <field3-value-id type="integer" nil="true"></field3-value-id> | |
| 55 | - <hours-estimate-current type="float">1268.7</hours-estimate-current> | |
| 56 | - <hours-estimate-initial type="float">0.0</hours-estimate-initial> | |
| 57 | - <id type="integer">2436</id> | |
| 58 | - <milestone-id type="integer">78</milestone-id> | |
| 59 | - <number type="integer">119</number> | |
| 60 | - <priority>3</priority> | |
| 61 | - <project-id type="integer">15</project-id> | |
| 62 | - <reporter-id type="integer">40</reporter-id> | |
| 63 | - <resolution></resolution> | |
| 64 | - <resolution-description></resolution-description> | |
| 65 | - <resolution-description-format>markdown</resolution-description-format> | |
| 66 | - <severity-id type="integer" nil="true"></severity-id> | |
| 67 | - <status>reopened</status> | |
| 68 | - <summary>TEST-ticket.</summary> | |
| 69 | - <version-id type="integer" nil="true"></version-id> | |
| 70 | - <created-at>2012-06-27T17:49:06Z</created-at> | |
| 71 | - <updated-at>2013-03-07T16:04:05Z</updated-at> | |
| 72 | -</ticket> | |
| 73 | -EOF | |
| 74 | - | |
| 75 | - stub_request(:get, "https://#{tracker.username}:#{tracker.password}@test.unfuddle.com/api/v1/projects/#{tracker.project_id}.xml"). | |
| 76 | - to_return(:status => 200, :body => project_xml, :headers => {}) | |
| 77 | - | |
| 78 | - | |
| 79 | - stub_request(:post, "https://#{tracker.username}:#{tracker.password}@test.unfuddle.com/api/v1/projects/#{tracker.project_id}/tickets.xml"). | |
| 80 | - to_return(:status => 200, :body => ticket_xml, :headers => {}) | |
| 81 | - | |
| 82 | - problem.app.issue_tracker.create_issue(problem) | |
| 83 | - problem.reload | |
| 84 | - | |
| 85 | - requested = have_requested(:post,"https://#{tracker.username}:#{tracker.password}@test.unfuddle.com/api/v1/projects/#{tracker.project_id}/tickets.xml" ) | |
| 86 | - expect(WebMock).to requested.with(:title => /[production][foo#bar] FooError: Too Much Bar/) | |
| 87 | - expect(WebMock).to requested.with(:content => /See this exception on Errbit/) | |
| 88 | - | |
| 89 | - expect(problem.issue_link).to eq issue_link | |
| 90 | - expect(problem.issue_type).to eq IssueTrackers::UnfuddleTracker::Label | |
| 91 | - end | |
| 92 | -end |
spec/models/problem_spec.rb
| ... | ... | @@ -370,6 +370,56 @@ describe Problem do |
| 370 | 370 | end |
| 371 | 371 | end |
| 372 | 372 | |
| 373 | + describe "#issue_type" do | |
| 374 | + context "without issue_type fill in Problem" do | |
| 375 | + let(:problem) do | |
| 376 | + Problem.new(:app => app) | |
| 377 | + end | |
| 378 | + | |
| 379 | + let(:app) do | |
| 380 | + App.new(:issue_tracker => issue_tracker) | |
| 381 | + end | |
| 382 | + | |
| 383 | + context "without issue_tracker associate to app" do | |
| 384 | + let(:issue_tracker) do | |
| 385 | + nil | |
| 386 | + end | |
| 387 | + it 'return nil' do | |
| 388 | + expect(problem.issue_type).to be_nil | |
| 389 | + end | |
| 390 | + end | |
| 391 | + | |
| 392 | + context "with issue_tracker valid associate to app" do | |
| 393 | + let(:issue_tracker) do | |
| 394 | + it = IssueTracker.new | |
| 395 | + it.stub(:tracker).and_return(double(:configured? => true, :label => 'foo')) | |
| 396 | + it | |
| 397 | + end | |
| 398 | + | |
| 399 | + it 'return the issue_tracker label' do | |
| 400 | + expect(problem.issue_type).to eql 'foo' | |
| 401 | + end | |
| 402 | + end | |
| 403 | + | |
| 404 | + context "with issue_tracker not valid associate to app" do | |
| 405 | + let(:issue_tracker) do | |
| 406 | + it = IssueTracker.new | |
| 407 | + it.stub(:tracker).and_return(double(:configured? => false)) | |
| 408 | + it | |
| 409 | + end | |
| 410 | + it 'return nil' do | |
| 411 | + expect(problem.issue_type).to be_nil | |
| 412 | + end | |
| 413 | + end | |
| 414 | + end | |
| 415 | + | |
| 416 | + context "with issue_type fill in Problem" do | |
| 417 | + it 'return the value associate' do | |
| 418 | + expect(Problem.new(:issue_type => 'foo').issue_type).to eql 'foo' | |
| 419 | + end | |
| 420 | + end | |
| 421 | + end | |
| 422 | + | |
| 373 | 423 | |
| 374 | 424 | end |
| 375 | 425 | ... | ... |
spec/spec_helper.rb
| ... | ... | @@ -24,6 +24,7 @@ require 'webmock/rspec' |
| 24 | 24 | require 'xmpp4r' |
| 25 | 25 | require 'xmpp4r/muc' |
| 26 | 26 | require 'mongoid-rspec' |
| 27 | +require 'errbit_plugin/issue_trackers/fake' | |
| 27 | 28 | |
| 28 | 29 | |
| 29 | 30 | # Requires supporting files with custom matchers and macros, etc, |
| ... | ... | @@ -54,7 +55,7 @@ RSpec.configure do |config| |
| 54 | 55 | init_haml_helpers |
| 55 | 56 | end |
| 56 | 57 | |
| 57 | - config.after(:all) do | |
| 58 | + config.before(:all) do | |
| 58 | 59 | WebMock.disable_net_connect! :allow => /coveralls\.io|127\.0\.0\.1/ |
| 59 | 60 | end |
| 60 | 61 | end | ... | ... |
spec/views/problems/show.html.haml_spec.rb
| 1 | 1 | require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe "problems/show.html.haml" do |
| 4 | + class PivotalLabsTracker | |
| 5 | + def initialize(app, params); end | |
| 6 | + def label; 'pivotal'; end | |
| 7 | + def configured?; true; end | |
| 8 | + def comments_allowed?; false; end | |
| 9 | + end | |
| 10 | + | |
| 11 | + class GithubIssuesTracker | |
| 12 | + def initialize(app, params); end | |
| 13 | + def label; 'github'; end | |
| 14 | + def configured?; true; end | |
| 15 | + def comments_allowed?; false; end | |
| 16 | + end | |
| 17 | + | |
| 4 | 18 | let(:problem) { Fabricate(:problem) } |
| 5 | 19 | let(:comment) { Fabricate(:comment) } |
| 6 | 20 | |
| ... | ... | @@ -16,7 +30,8 @@ describe "problems/show.html.haml" do |
| 16 | 30 | end |
| 17 | 31 | |
| 18 | 32 | def with_issue_tracker(tracker, problem) |
| 19 | - problem.app.issue_tracker = tracker.new :api_token => "token token token", :project_id => "1234" | |
| 33 | + problem.app.issue_tracker = IssueTracker.new :type_tracker => tracker, :options => {:api_token => "token token token", :project_id => "1234"} | |
| 34 | + ErrbitPlugin::Register.stub(:issue_tracker).with(tracker).and_return(tracker.constantize) | |
| 20 | 35 | view.stub(:problem).and_return(problem) |
| 21 | 36 | view.stub(:app).and_return(problem.app) |
| 22 | 37 | end |
| ... | ... | @@ -75,7 +90,7 @@ describe "problems/show.html.haml" do |
| 75 | 90 | |
| 76 | 91 | it 'should allow creating issue for github if application has a github tracker' do |
| 77 | 92 | problem = Fabricate(:problem_with_comments, :app => Fabricate(:app, :github_repo => "test_user/test_repo")) |
| 78 | - with_issue_tracker(GithubIssuesTracker, problem) | |
| 93 | + with_issue_tracker("GithubIssuesTracker", problem) | |
| 79 | 94 | view.stub(:problem).and_return(problem) |
| 80 | 95 | view.stub(:app).and_return(problem.app) |
| 81 | 96 | render |
| ... | ... | @@ -96,16 +111,15 @@ describe "problems/show.html.haml" do |
| 96 | 111 | |
| 97 | 112 | end |
| 98 | 113 | |
| 99 | - context "with lighthouse tracker on app" do | |
| 100 | - let(:app) { App.new(:new_record => false, :issue_tracker => tracker ) } | |
| 101 | - let(:tracker) { | |
| 102 | - IssueTrackers::LighthouseTracker.new(:project_id => 'x') | |
| 103 | - } | |
| 114 | + context "with tracker associate on app" do | |
| 115 | + before do | |
| 116 | + with_issue_tracker("PivotalLabsTracker", problem) | |
| 117 | + end | |
| 104 | 118 | context "with problem without issue link" do |
| 105 | - let(:problem){ Problem.new(:new_record => false, :app => app) } | |
| 119 | + before do | |
| 120 | + problem.issue_link = nil | |
| 121 | + end | |
| 106 | 122 | it 'not see link if no issue tracker' do |
| 107 | - view.stub(:problem).and_return(problem) | |
| 108 | - view.stub(:app).and_return(problem.app) | |
| 109 | 123 | render |
| 110 | 124 | expect(view.content_for(:action_bar)).to match(/create issue/) |
| 111 | 125 | end |
| ... | ... | @@ -113,11 +127,11 @@ describe "problems/show.html.haml" do |
| 113 | 127 | end |
| 114 | 128 | |
| 115 | 129 | context "with problem with issue link" do |
| 116 | - let(:problem){ Problem.new(:new_record => false, :app => app, :issue_link => 'http://foo') } | |
| 130 | + before do | |
| 131 | + problem.issue_link = 'http://foo' | |
| 132 | + end | |
| 117 | 133 | |
| 118 | 134 | it 'not see link if no issue tracker' do |
| 119 | - view.stub(:problem).and_return(problem) | |
| 120 | - view.stub(:app).and_return(problem.app) | |
| 121 | 135 | render |
| 122 | 136 | expect(view.content_for(:action_bar)).to_not match(/create issue/) |
| 123 | 137 | end |
| ... | ... | @@ -147,7 +161,7 @@ describe "problems/show.html.haml" do |
| 147 | 161 | context "with issue tracker" do |
| 148 | 162 | it 'should not display the comments section' do |
| 149 | 163 | problem = Fabricate(:problem) |
| 150 | - with_issue_tracker(PivotalLabsTracker, problem) | |
| 164 | + with_issue_tracker("PivotalLabsTracker", problem) | |
| 151 | 165 | render |
| 152 | 166 | expect(view.view_flow.get(:comments)).to be_blank |
| 153 | 167 | end |
| ... | ... | @@ -155,7 +169,7 @@ describe "problems/show.html.haml" do |
| 155 | 169 | it 'should display existing comments' do |
| 156 | 170 | problem = Fabricate(:problem_with_comments) |
| 157 | 171 | problem.reload |
| 158 | - with_issue_tracker(PivotalLabsTracker, problem) | |
| 172 | + with_issue_tracker("PivotalLabsTracker", problem) | |
| 159 | 173 | render |
| 160 | 174 | |
| 161 | 175 | expect(view.content_for(:comments)).to include('Test comment') | ... | ... |