pivotal-to-trello 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +102 -0
- data/Dockerfile +24 -0
- data/Gemfile +5 -4
- data/README.markdown +11 -2
- data/Rakefile +10 -10
- data/VERSION +1 -1
- data/lib/pivotal-to-trello.rb +1 -1
- data/lib/pivotal_to_trello/core.rb +91 -83
- data/lib/pivotal_to_trello/pivotal_wrapper.rb +10 -11
- data/lib/pivotal_to_trello/trello_wrapper.rb +44 -25
- data/pivotal-to-trello.gemspec +22 -17
- data/spec/pivotal_to_trello/core_spec.rb +44 -44
- data/spec/pivotal_to_trello/pivotal_wrapper_spec.rb +12 -10
- data/spec/pivotal_to_trello/trello_wrapper_spec.rb +75 -53
- data/spec/spec_helper.rb +35 -34
- metadata +35 -17
| @@ -3,32 +3,31 @@ require 'pivotal-tracker' | |
| 3 3 | 
             
            module PivotalToTrello
         | 
| 4 4 | 
             
              # Interface to the Pivotal Tracker API.
         | 
| 5 5 | 
             
              class PivotalWrapper
         | 
| 6 | 
            -
             | 
| 7 6 | 
             
                # Constructor
         | 
| 8 7 | 
             
                def initialize(token)
         | 
| 9 8 | 
             
                  ::PivotalTracker::Client.token = token
         | 
| 9 | 
            +
                  ::PivotalTracker::Client.use_ssl = true
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 12 | 
             
                # Returns a hash of available projects keyed on project ID.
         | 
| 13 13 | 
             
                def project_choices
         | 
| 14 | 
            -
                  ::PivotalTracker::Project.all. | 
| 14 | 
            +
                  ::PivotalTracker::Project.all.each_with_object({}) do |project, hash|
         | 
| 15 15 | 
             
                    hash[project.id] = project.name
         | 
| 16 | 
            -
             | 
| 16 | 
            +
             | 
| 17 17 | 
             
                  end
         | 
| 18 18 | 
             
                end
         | 
| 19 19 |  | 
| 20 20 | 
             
                # Returns all stories for the given project.
         | 
| 21 21 | 
             
                def stories(project_id)
         | 
| 22 | 
            -
                  project(project_id).stories.all
         | 
| 22 | 
            +
                  project(project_id).stories.all.sort_by(&:created_at)
         | 
| 23 23 | 
             
                end
         | 
| 24 24 |  | 
| 25 25 | 
             
                private
         | 
| 26 26 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 27 | 
            +
                # Returns the Pivotal project that we're exporting.
         | 
| 28 | 
            +
                def project(project_id)
         | 
| 29 | 
            +
                  @projects             ||= {}
         | 
| 30 | 
            +
                  @projects[project_id] ||= ::PivotalTracker::Project.find(project_id)
         | 
| 31 | 
            +
                end
         | 
| 33 32 | 
             
              end
         | 
| 34 | 
            -
            end
         | 
| 33 | 
            +
            end
         | 
| @@ -3,7 +3,6 @@ require 'trello' | |
| 3 3 | 
             
            module PivotalToTrello
         | 
| 4 4 | 
             
              # Interface to the Trello API.
         | 
| 5 5 | 
             
              class TrelloWrapper
         | 
| 6 | 
            -
             | 
| 7 6 | 
             
                # Constructor
         | 
| 8 7 | 
             
                def initialize(key, token)
         | 
| 9 8 | 
             
                  Trello.configure do |config|
         | 
| @@ -18,15 +17,13 @@ module PivotalToTrello | |
| 18 17 | 
             
                  card ||= begin
         | 
| 19 18 | 
             
                    puts "Creating a card for #{pivotal_story.story_type} '#{pivotal_story.name}'."
         | 
| 20 19 | 
             
                    card = Trello::Card.create(
         | 
| 21 | 
            -
                      : | 
| 22 | 
            -
                      : | 
| 23 | 
            -
                      : | 
| 20 | 
            +
                      name:    pivotal_story.name,
         | 
| 21 | 
            +
                      desc:    pivotal_story.description,
         | 
| 22 | 
            +
                      list_id: list_id,
         | 
| 24 23 | 
             
                    )
         | 
| 25 24 |  | 
| 26 | 
            -
                    pivotal_story | 
| 27 | 
            -
             | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
             | 
| 25 | 
            +
                    create_notes(card, pivotal_story)
         | 
| 26 | 
            +
                    create_tasks(card, pivotal_story)
         | 
| 30 27 | 
             
                    card
         | 
| 31 28 | 
             
                  end
         | 
| 32 29 |  | 
| @@ -38,9 +35,8 @@ module PivotalToTrello | |
| 38 35 |  | 
| 39 36 | 
             
                # Returns a hash of available boards, keyed on board ID.
         | 
| 40 37 | 
             
                def board_choices
         | 
| 41 | 
            -
                  Trello::Board.all. | 
| 38 | 
            +
                  Trello::Board.all.each_with_object({}) do |board, hash|
         | 
| 42 39 | 
             
                    hash[board.id] = board.name
         | 
| 43 | 
            -
                    hash
         | 
| 44 40 | 
             
                  end
         | 
| 45 41 | 
             
                end
         | 
| 46 42 |  | 
| @@ -49,9 +45,8 @@ module PivotalToTrello | |
| 49 45 | 
             
                  # Cache the list to improve performance.
         | 
| 50 46 | 
             
                  @lists           ||= {}
         | 
| 51 47 | 
             
                  @lists[board_id] ||= begin
         | 
| 52 | 
            -
                    choices = Trello::Board.find(board_id).lists. | 
| 48 | 
            +
                    choices = Trello::Board.find(board_id).lists.each_with_object({}) do |list, hash|
         | 
| 53 49 | 
             
                      hash[list.id] = list.name
         | 
| 54 | 
            -
                      hash
         | 
| 55 50 | 
             
                    end
         | 
| 56 51 | 
             
                    choices        = Hash[choices.sort_by { |_, v| v }]
         | 
| 57 52 | 
             
                    choices[false] = "[don't import these stories]"
         | 
| @@ -64,17 +59,21 @@ module PivotalToTrello | |
| 64 59 | 
             
                # Returns a list of all cards in the given list, keyed on name.
         | 
| 65 60 | 
             
                def cards_for_list(list_id)
         | 
| 66 61 | 
             
                  @cards          ||= {}
         | 
| 67 | 
            -
                  @cards[list_id] ||= Trello::List.find(list_id).cards. | 
| 62 | 
            +
                  @cards[list_id] ||= Trello::List.find(list_id).cards.each_with_object({}) do |card, hash|
         | 
| 68 63 | 
             
                    hash[card_hash(card.name, card.desc)] = card
         | 
| 69 | 
            -
                    hash
         | 
| 70 64 | 
             
                  end
         | 
| 71 65 |  | 
| 72 66 | 
             
                  @cards[list_id]
         | 
| 73 67 | 
             
                end
         | 
| 74 68 |  | 
| 75 69 | 
             
                # Adds the given label to the card.
         | 
| 76 | 
            -
                def add_label(card,  | 
| 77 | 
            -
                   | 
| 70 | 
            +
                def add_label(card, label_name, label_color)
         | 
| 71 | 
            +
                  @labels                ||= {}
         | 
| 72 | 
            +
                  @labels[card.board_id] ||= Trello::Board.find(card.board_id).labels
         | 
| 73 | 
            +
                  label                    = @labels[card.board_id].find { |l| l.name == label_name && l.color == label_color }
         | 
| 74 | 
            +
                  label                  ||= Trello::Label.create(name: label_name, board_id: card.board_id, color: label_color)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  card.add_label(label) unless card.labels.detect { |l| l.id == label.id }
         | 
| 78 77 | 
             
                end
         | 
| 79 78 |  | 
| 80 79 | 
             
                # Returns a list of colors that can be used to label cards.
         | 
| @@ -86,22 +85,42 @@ module PivotalToTrello | |
| 86 85 | 
             
                    'purple' => 'Purple',
         | 
| 87 86 | 
             
                    'red'    => 'Red',
         | 
| 88 87 | 
             
                    'yellow' => 'Yellow',
         | 
| 89 | 
            -
                    false    => '[none]'
         | 
| 88 | 
            +
                    false    => '[none]',
         | 
| 90 89 | 
             
                  }
         | 
| 91 90 | 
             
                end
         | 
| 92 91 |  | 
| 93 92 | 
             
                private
         | 
| 94 93 |  | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 94 | 
            +
                # Copies notes from the pivotal story to the card.
         | 
| 95 | 
            +
                def create_notes(card, pivotal_story)
         | 
| 96 | 
            +
                  pivotal_story.notes.all.each do |note|
         | 
| 97 | 
            +
                    card.add_comment("[#{note.author}] #{note.text.to_s.strip}") unless note.text.to_s.strip.empty?
         | 
| 98 98 | 
             
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                # Copies notes from the pivotal story to the card.
         | 
| 102 | 
            +
                def create_tasks(card, pivotal_story)
         | 
| 103 | 
            +
                  tasks = pivotal_story.tasks.all
         | 
| 104 | 
            +
                  return if tasks.empty?
         | 
| 99 105 |  | 
| 100 | 
            -
                   | 
| 101 | 
            -
                   | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 106 | 
            +
                  checklist = Trello::Checklist.create(name: 'Tasks', card_id: card.id)
         | 
| 107 | 
            +
                  card.add_checklist(checklist)
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  tasks.each do |task|
         | 
| 110 | 
            +
                    puts " - Creating task '#{task.description}'"
         | 
| 111 | 
            +
                    checklist.add_item(task.description, task.complete)
         | 
| 104 112 | 
             
                  end
         | 
| 113 | 
            +
                end
         | 
| 105 114 |  | 
| 115 | 
            +
                # Returns a unique identifier for this list/name/description combination.
         | 
| 116 | 
            +
                def card_hash(name, description)
         | 
| 117 | 
            +
                  Digest::SHA1.hexdigest("#{name}_#{description}")
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                # Returns a card with the given name and description if it exists in the given list, nil otherwise.
         | 
| 121 | 
            +
                def get_card(list_id, name, description)
         | 
| 122 | 
            +
                  key = card_hash(name, description)
         | 
| 123 | 
            +
                  cards_for_list(list_id)[key] unless cards_for_list(list_id)[key].nil?
         | 
| 124 | 
            +
                end
         | 
| 106 125 | 
             
              end
         | 
| 107 | 
            -
            end
         | 
| 126 | 
            +
            end
         | 
    
        data/pivotal-to-trello.gemspec
    CHANGED
    
    | @@ -2,16 +2,16 @@ | |
| 2 2 | 
             
            # DO NOT EDIT THIS FILE DIRECTLY
         | 
| 3 3 | 
             
            # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
         | 
| 4 4 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 5 | 
            -
            # stub: pivotal-to-trello 0. | 
| 5 | 
            +
            # stub: pivotal-to-trello 0.2.0 ruby lib
         | 
| 6 6 |  | 
| 7 7 | 
             
            Gem::Specification.new do |s|
         | 
| 8 8 | 
             
              s.name = "pivotal-to-trello"
         | 
| 9 | 
            -
              s.version = "0. | 
| 9 | 
            +
              s.version = "0.2.0"
         | 
| 10 10 |  | 
| 11 11 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 12 12 | 
             
              s.require_paths = ["lib"]
         | 
| 13 | 
            -
              s.authors = ["Dave Perrett"]
         | 
| 14 | 
            -
              s.date = " | 
| 13 | 
            +
              s.authors = ["Dave Perrett", "Erik Frederiksen", "Kenneth Kalmer"]
         | 
| 14 | 
            +
              s.date = "2017-08-10"
         | 
| 15 15 | 
             
              s.description = "Pulls stories from Pivotal Tracker and imports them into Trello"
         | 
| 16 16 | 
             
              s.email = "hello@daveperrett.com"
         | 
| 17 17 | 
             
              s.executables = ["pivotal-to-trello"]
         | 
| @@ -21,6 +21,8 @@ Gem::Specification.new do |s| | |
| 21 21 | 
             
              ]
         | 
| 22 22 | 
             
              s.files = [
         | 
| 23 23 | 
             
                ".document",
         | 
| 24 | 
            +
                ".rubocop.yml",
         | 
| 25 | 
            +
                "Dockerfile",
         | 
| 24 26 | 
             
                "Gemfile",
         | 
| 25 27 | 
             
                "LICENSE.txt",
         | 
| 26 28 | 
             
                "README.markdown",
         | 
| @@ -40,34 +42,37 @@ Gem::Specification.new do |s| | |
| 40 42 | 
             
              ]
         | 
| 41 43 | 
             
              s.homepage = "http://github.com/recurser/pivotal-to-trello"
         | 
| 42 44 | 
             
              s.licenses = ["MIT"]
         | 
| 43 | 
            -
              s.rubygems_version = "2. | 
| 45 | 
            +
              s.rubygems_version = "2.4.5"
         | 
| 44 46 | 
             
              s.summary = "Pivotal Tracker to Trello exporter"
         | 
| 45 47 |  | 
| 46 48 | 
             
              if s.respond_to? :specification_version then
         | 
| 47 49 | 
             
                s.specification_version = 4
         | 
| 48 50 |  | 
| 49 51 | 
             
                if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
         | 
| 50 | 
            -
                  s.add_runtime_dependency(%q<highline>, [" | 
| 51 | 
            -
                  s.add_runtime_dependency(%q<ruby-trello>, [" | 
| 52 | 
            -
                  s.add_runtime_dependency(%q<pivotal-tracker>, [" | 
| 52 | 
            +
                  s.add_runtime_dependency(%q<highline>, ["~> 1.7.8"])
         | 
| 53 | 
            +
                  s.add_runtime_dependency(%q<ruby-trello>, ["~> 2.0.0"])
         | 
| 54 | 
            +
                  s.add_runtime_dependency(%q<pivotal-tracker>, ["~> 0.5.13"])
         | 
| 53 55 | 
             
                  s.add_development_dependency(%q<rspec>, [">= 2.14.1"])
         | 
| 54 56 | 
             
                  s.add_development_dependency(%q<rdoc>, [">= 4.1.1"])
         | 
| 55 | 
            -
                  s.add_development_dependency(%q<jeweler>, [">= 2. | 
| 57 | 
            +
                  s.add_development_dependency(%q<jeweler>, [">= 2.3.7"])
         | 
| 58 | 
            +
                  s.add_development_dependency(%q<rubocop>, [">= 0.49.1"])
         | 
| 56 59 | 
             
                else
         | 
| 57 | 
            -
                  s.add_dependency(%q<highline>, [" | 
| 58 | 
            -
                  s.add_dependency(%q<ruby-trello>, [" | 
| 59 | 
            -
                  s.add_dependency(%q<pivotal-tracker>, [" | 
| 60 | 
            +
                  s.add_dependency(%q<highline>, ["~> 1.7.8"])
         | 
| 61 | 
            +
                  s.add_dependency(%q<ruby-trello>, ["~> 2.0.0"])
         | 
| 62 | 
            +
                  s.add_dependency(%q<pivotal-tracker>, ["~> 0.5.13"])
         | 
| 60 63 | 
             
                  s.add_dependency(%q<rspec>, [">= 2.14.1"])
         | 
| 61 64 | 
             
                  s.add_dependency(%q<rdoc>, [">= 4.1.1"])
         | 
| 62 | 
            -
                  s.add_dependency(%q<jeweler>, [">= 2. | 
| 65 | 
            +
                  s.add_dependency(%q<jeweler>, [">= 2.3.7"])
         | 
| 66 | 
            +
                  s.add_dependency(%q<rubocop>, [">= 0.49.1"])
         | 
| 63 67 | 
             
                end
         | 
| 64 68 | 
             
              else
         | 
| 65 | 
            -
                s.add_dependency(%q<highline>, [" | 
| 66 | 
            -
                s.add_dependency(%q<ruby-trello>, [" | 
| 67 | 
            -
                s.add_dependency(%q<pivotal-tracker>, [" | 
| 69 | 
            +
                s.add_dependency(%q<highline>, ["~> 1.7.8"])
         | 
| 70 | 
            +
                s.add_dependency(%q<ruby-trello>, ["~> 2.0.0"])
         | 
| 71 | 
            +
                s.add_dependency(%q<pivotal-tracker>, ["~> 0.5.13"])
         | 
| 68 72 | 
             
                s.add_dependency(%q<rspec>, [">= 2.14.1"])
         | 
| 69 73 | 
             
                s.add_dependency(%q<rdoc>, [">= 4.1.1"])
         | 
| 70 | 
            -
                s.add_dependency(%q<jeweler>, [">= 2. | 
| 74 | 
            +
                s.add_dependency(%q<jeweler>, [">= 2.3.7"])
         | 
| 75 | 
            +
                s.add_dependency(%q<rubocop>, [">= 0.49.1"])
         | 
| 71 76 | 
             
              end
         | 
| 72 77 | 
             
            end
         | 
| 73 78 |  | 
| @@ -6,32 +6,32 @@ describe 'Core' do | |
| 6 6 | 
             
              let(:trello)  { mock_trello_wrapper }
         | 
| 7 7 |  | 
| 8 8 | 
             
              before(:each) do
         | 
| 9 | 
            -
                IO. | 
| 10 | 
            -
                core. | 
| 11 | 
            -
                core. | 
| 12 | 
            -
             | 
| 13 | 
            -
                core. | 
| 14 | 
            -
                core. | 
| 15 | 
            -
                core. | 
| 16 | 
            -
                core. | 
| 17 | 
            -
                core. | 
| 18 | 
            -
                core. | 
| 19 | 
            -
                core. | 
| 20 | 
            -
                core. | 
| 21 | 
            -
                core. | 
| 22 | 
            -
                core. | 
| 23 | 
            -
                core. | 
| 24 | 
            -
                core. | 
| 25 | 
            -
                core. | 
| 26 | 
            -
                core. | 
| 27 | 
            -
                core. | 
| 28 | 
            -
                core. | 
| 9 | 
            +
                allow_any_instance_of(IO).to receive(:puts)
         | 
| 10 | 
            +
                allow(core).to receive_messages(pivotal: pivotal)
         | 
| 11 | 
            +
                allow(core).to receive_messages(trello: trello)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                allow(core).to receive(:prompt_selection).with('Which Pivotal project would you like to export?', pivotal.project_choices).and_return('pivotal_project_id')
         | 
| 14 | 
            +
                allow(core).to receive(:prompt_selection).with('Which Trello board would you like to import into?', trello.board_choices).and_return('trello_board_id')
         | 
| 15 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'icebox' stories into?", trello.list_choices).and_return('icebox_list_id')
         | 
| 16 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'current' stories into?", trello.list_choices).and_return('current_list_id')
         | 
| 17 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'finished' stories into?", trello.list_choices).and_return('finished_list_id')
         | 
| 18 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'delivered' stories into?", trello.list_choices).and_return('delivered_list_id')
         | 
| 19 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'accepted' stories into?", trello.list_choices).and_return('accepted_list_id')
         | 
| 20 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'rejected' stories into?", trello.list_choices).and_return('rejected_list_id')
         | 
| 21 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'backlog' bugs into?", trello.list_choices).and_return('bug_list_id')
         | 
| 22 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'backlog' chores into?", trello.list_choices).and_return('chore_list_id')
         | 
| 23 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'backlog' features into?", trello.list_choices).and_return('feature_list_id')
         | 
| 24 | 
            +
                allow(core).to receive(:prompt_selection).with("Which Trello list would you like to put 'backlog' releases into?", trello.list_choices).and_return('release_list_id')
         | 
| 25 | 
            +
                allow(core).to receive(:prompt_selection).with('What color would you like to label bugs with?', trello.label_choices).and_return('bug_label')
         | 
| 26 | 
            +
                allow(core).to receive(:prompt_selection).with('What color would you like to label features with?', trello.label_choices).and_return('feature_label')
         | 
| 27 | 
            +
                allow(core).to receive(:prompt_selection).with('What color would you like to label chores with?', trello.label_choices).and_return('chore_label')
         | 
| 28 | 
            +
                allow(core).to receive(:prompt_selection).with('What color would you like to label releases with?', trello.label_choices).and_return('release_label')
         | 
| 29 29 | 
             
              end
         | 
| 30 30 |  | 
| 31 31 | 
             
              context '#import!' do
         | 
| 32 32 | 
             
                it 'prompts the user for details' do
         | 
| 33 33 | 
             
                  core.import!
         | 
| 34 | 
            -
                  core.options. | 
| 34 | 
            +
                  expect(core.options).to eq(mock_options)
         | 
| 35 35 | 
             
                end
         | 
| 36 36 |  | 
| 37 37 | 
             
                describe 'story handling' do
         | 
| @@ -39,68 +39,68 @@ describe 'Core' do | |
| 39 39 | 
             
                  let(:story) { mock_pivotal_story }
         | 
| 40 40 |  | 
| 41 41 | 
             
                  before(:each) do
         | 
| 42 | 
            -
                    pivotal. | 
| 43 | 
            -
                    trello. | 
| 42 | 
            +
                    expect(pivotal).to receive(:stories).and_return([story])
         | 
| 43 | 
            +
                    allow(trello).to receive_messages(add_label: true, create_card: card)
         | 
| 44 44 | 
             
                  end
         | 
| 45 45 |  | 
| 46 46 | 
             
                  it 'handles accepted stories' do
         | 
| 47 | 
            -
                    story. | 
| 48 | 
            -
                    trello. | 
| 47 | 
            +
                    allow(story).to receive_messages(current_state: 'accepted')
         | 
| 48 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.accepted_list_id, story).and_return(card)
         | 
| 49 49 | 
             
                    core.import!
         | 
| 50 50 | 
             
                  end
         | 
| 51 51 |  | 
| 52 52 | 
             
                  it 'handles rejected stories' do
         | 
| 53 | 
            -
                    story. | 
| 54 | 
            -
                    trello. | 
| 53 | 
            +
                    allow(story).to receive_messages(current_state: 'rejected')
         | 
| 54 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.rejected_list_id, story).and_return(card)
         | 
| 55 55 | 
             
                    core.import!
         | 
| 56 56 | 
             
                  end
         | 
| 57 57 |  | 
| 58 58 | 
             
                  it 'handles finished stories' do
         | 
| 59 | 
            -
                    story. | 
| 60 | 
            -
                    trello. | 
| 59 | 
            +
                    allow(story).to receive_messages(current_state: 'finished')
         | 
| 60 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.finished_list_id, story).and_return(card)
         | 
| 61 61 | 
             
                    core.import!
         | 
| 62 62 | 
             
                  end
         | 
| 63 63 |  | 
| 64 64 | 
             
                  it 'handles delivered stories' do
         | 
| 65 | 
            -
                    story. | 
| 66 | 
            -
                    trello. | 
| 65 | 
            +
                    allow(story).to receive_messages(current_state: 'delivered')
         | 
| 66 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.delivered_list_id, story).and_return(card)
         | 
| 67 67 | 
             
                    core.import!
         | 
| 68 68 | 
             
                  end
         | 
| 69 69 |  | 
| 70 70 | 
             
                  it 'handles unstarted features' do
         | 
| 71 | 
            -
                    story. | 
| 72 | 
            -
                    trello. | 
| 71 | 
            +
                    allow(story).to receive_messages(current_state: 'unstarted', story_type: 'feature')
         | 
| 72 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.feature_list_id, story).and_return(card)
         | 
| 73 73 | 
             
                    core.import!
         | 
| 74 74 | 
             
                  end
         | 
| 75 75 |  | 
| 76 76 | 
             
                  it 'handles unstarted chores' do
         | 
| 77 | 
            -
                    story. | 
| 78 | 
            -
                    trello. | 
| 77 | 
            +
                    allow(story).to receive_messages(current_state: 'unstarted', story_type: 'chore')
         | 
| 78 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.chore_list_id, story).and_return(card)
         | 
| 79 79 | 
             
                    core.import!
         | 
| 80 80 | 
             
                  end
         | 
| 81 81 |  | 
| 82 82 | 
             
                  it 'handles unstarted bugs' do
         | 
| 83 | 
            -
                    story. | 
| 84 | 
            -
                    trello. | 
| 83 | 
            +
                    allow(story).to receive_messages(current_state: 'unstarted', story_type: 'bug')
         | 
| 84 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.bug_list_id, story).and_return(card)
         | 
| 85 85 | 
             
                    core.import!
         | 
| 86 86 | 
             
                  end
         | 
| 87 87 |  | 
| 88 88 | 
             
                  it 'handles unstarted releases' do
         | 
| 89 | 
            -
                    story. | 
| 90 | 
            -
                    trello. | 
| 89 | 
            +
                    allow(story).to receive_messages(current_state: 'unstarted', story_type: 'release')
         | 
| 90 | 
            +
                    expect(trello).to receive(:create_card).with(core.options.release_list_id, story).and_return(card)
         | 
| 91 91 | 
             
                    core.import!
         | 
| 92 92 | 
             
                  end
         | 
| 93 93 |  | 
| 94 94 | 
             
                  it 'labels stories' do
         | 
| 95 | 
            -
                    story. | 
| 96 | 
            -
                    trello. | 
| 95 | 
            +
                    allow(story).to receive_messages(story_type: 'bug')
         | 
| 96 | 
            +
                    expect(trello).to receive(:add_label).with(card, 'bug', core.options.bug_label)
         | 
| 97 97 | 
             
                    core.import!
         | 
| 98 98 | 
             
                  end
         | 
| 99 99 |  | 
| 100 100 | 
             
                  it 'ignores nil labels' do
         | 
| 101 | 
            -
                    core.options. | 
| 102 | 
            -
                    story. | 
| 103 | 
            -
                    trello. | 
| 101 | 
            +
                    allow(core.options).to receive_messages(bug_label: nil)
         | 
| 102 | 
            +
                    allow(story).to receive_messages(story_type: 'bug')
         | 
| 103 | 
            +
                    expect(trello).not_to receive(:add_label)
         | 
| 104 104 | 
             
                    core.import!
         | 
| 105 105 | 
             
                  end
         | 
| 106 106 | 
             
                end
         | 
| @@ -5,26 +5,28 @@ describe 'PivotalWrapper' do | |
| 5 5 |  | 
| 6 6 | 
             
              context '#initialize' do
         | 
| 7 7 | 
             
                it 'sets the pivotal token' do
         | 
| 8 | 
            -
                  ::PivotalTracker::Client. | 
| 8 | 
            +
                  expect(::PivotalTracker::Client).to receive(:token=).with('token')
         | 
| 9 9 | 
             
                  PivotalToTrello::PivotalWrapper.new('token')
         | 
| 10 10 | 
             
                end
         | 
| 11 11 | 
             
              end
         | 
| 12 12 |  | 
| 13 13 | 
             
              context '#project_choices' do
         | 
| 14 14 | 
             
                it 'returns a hash of pivotal projects' do
         | 
| 15 | 
            -
                  project = OpenStruct.new(: | 
| 16 | 
            -
                  ::PivotalTracker::Project. | 
| 17 | 
            -
                  wrapper.project_choices. | 
| 15 | 
            +
                  project = OpenStruct.new(id: 'id', name: 'My Project')
         | 
| 16 | 
            +
                  expect(::PivotalTracker::Project).to receive(:all).and_return([project])
         | 
| 17 | 
            +
                  expect(wrapper.project_choices).to eq('id' => 'My Project')
         | 
| 18 18 | 
             
                end
         | 
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 21 | 
             
              context '#stories' do
         | 
| 22 | 
            -
                it 'returns  | 
| 23 | 
            -
                   | 
| 22 | 
            +
                it 'returns a sorted array of pivotal stories' do
         | 
| 23 | 
            +
                  first_story = mock_pivotal_story(created_at: Time.now - 10_000)
         | 
| 24 | 
            +
                  last_story  = mock_pivotal_story(created_at: Time.now + 10_000)
         | 
| 24 25 | 
             
                  project = double(PivotalTracker::Project)
         | 
| 25 | 
            -
                  ::PivotalTracker::Project. | 
| 26 | 
            -
                  project. | 
| 27 | 
            -
                  wrapper.stories('project_id'). | 
| 26 | 
            +
                  expect(::PivotalTracker::Project).to receive(:find).with('project_id').and_return(project)
         | 
| 27 | 
            +
                  allow(project).to receive_message_chain(:stories, :all).and_return([last_story, first_story])
         | 
| 28 | 
            +
                  expect(wrapper.stories('project_id').first).to eq(first_story)
         | 
| 29 | 
            +
                  expect(wrapper.stories('project_id').last).to eq(last_story)
         | 
| 28 30 | 
             
                end
         | 
| 29 31 | 
             
              end
         | 
| 30 | 
            -
            end
         | 
| 32 | 
            +
            end
         | 
| @@ -4,15 +4,15 @@ describe 'TrelloWrapper' do | |
| 4 4 | 
             
              let(:wrapper) { PivotalToTrello::TrelloWrapper.new('key', 'token') }
         | 
| 5 5 |  | 
| 6 6 | 
             
              before(:each) do
         | 
| 7 | 
            -
                IO. | 
| 7 | 
            +
                allow_any_instance_of(IO).to receive(:puts)
         | 
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 10 | 
             
              context '#initialize' do
         | 
| 11 11 | 
             
                it 'sets the auth credentials' do
         | 
| 12 12 | 
             
                  config = double
         | 
| 13 | 
            -
                  Trello. | 
| 14 | 
            -
                  config. | 
| 15 | 
            -
                  config. | 
| 13 | 
            +
                  expect(Trello).to receive(:configure).and_yield(config)
         | 
| 14 | 
            +
                  expect(config).to receive(:developer_public_key=).with('key')
         | 
| 15 | 
            +
                  expect(config).to receive(:member_token=).with('token')
         | 
| 16 16 | 
             
                  PivotalToTrello::TrelloWrapper.new('key', 'token')
         | 
| 17 17 | 
             
                end
         | 
| 18 18 | 
             
              end
         | 
| @@ -22,93 +22,115 @@ describe 'TrelloWrapper' do | |
| 22 22 |  | 
| 23 23 | 
             
                it 'creates a new card if none exists' do
         | 
| 24 24 | 
             
                  story = mock_pivotal_story
         | 
| 25 | 
            -
                  wrapper. | 
| 26 | 
            -
                  Trello::Card. | 
| 27 | 
            -
                    : | 
| 28 | 
            -
                    : | 
| 29 | 
            -
                    : | 
| 25 | 
            +
                  expect(wrapper).to receive(:get_card).and_return(nil)
         | 
| 26 | 
            +
                  expect(Trello::Card).to receive(:create).with(
         | 
| 27 | 
            +
                    name:    story.name,
         | 
| 28 | 
            +
                    desc:    story.description,
         | 
| 29 | 
            +
                    list_id: 'list_id',
         | 
| 30 30 | 
             
                  ).and_return(card)
         | 
| 31 | 
            -
                  wrapper.create_card('list_id', story). | 
| 31 | 
            +
                  expect(wrapper.create_card('list_id', story)).to eq(card)
         | 
| 32 32 | 
             
                end
         | 
| 33 33 |  | 
| 34 34 | 
             
                it 'does not create a new card if one exists with the same name' do
         | 
| 35 | 
            -
                  story = mock_pivotal_story(: | 
| 36 | 
            -
                  Trello::List. | 
| 37 | 
            -
                  Trello::Card. | 
| 38 | 
            -
                  wrapper.create_card('list_id', story). | 
| 35 | 
            +
                  story = mock_pivotal_story(name: 'My Card')
         | 
| 36 | 
            +
                  allow(Trello::List).to receive_message_chain(:find, :cards).and_return([card])
         | 
| 37 | 
            +
                  expect(Trello::Card).not_to receive(:create)
         | 
| 38 | 
            +
                  expect(wrapper.create_card('list_id', story)).to eq(card)
         | 
| 39 39 | 
             
                end
         | 
| 40 40 |  | 
| 41 41 | 
             
                it 'creates a new card if one exists with a different name' do
         | 
| 42 | 
            -
                  story = mock_pivotal_story(: | 
| 43 | 
            -
                  Trello::List. | 
| 44 | 
            -
                  Trello::Card. | 
| 45 | 
            -
                    : | 
| 46 | 
            -
                    : | 
| 47 | 
            -
                    : | 
| 42 | 
            +
                  story = mock_pivotal_story(name: 'My Foo')
         | 
| 43 | 
            +
                  allow(Trello::List).to receive_message_chain(:find, :cards).and_return([card])
         | 
| 44 | 
            +
                  expect(Trello::Card).to receive(:create).with(
         | 
| 45 | 
            +
                    name:    story.name,
         | 
| 46 | 
            +
                    desc:    story.description,
         | 
| 47 | 
            +
                    list_id: 'list_id',
         | 
| 48 48 | 
             
                  ).and_return(card)
         | 
| 49 | 
            -
                  wrapper.create_card('list_id', story). | 
| 49 | 
            +
                  expect(wrapper.create_card('list_id', story)).to eq(card)
         | 
| 50 50 | 
             
                end
         | 
| 51 51 |  | 
| 52 52 | 
             
                it 'adds comments' do
         | 
| 53 | 
            -
                  note  = OpenStruct.new(: | 
| 53 | 
            +
                  note  = OpenStruct.new(text: 'My Note', author: 'John Smith')
         | 
| 54 54 | 
             
                  story = mock_pivotal_story
         | 
| 55 | 
            -
                  story. | 
| 56 | 
            -
                  wrapper. | 
| 57 | 
            -
                  Trello::Card. | 
| 58 | 
            -
                  card. | 
| 59 | 
            -
                  wrapper.create_card('list_id', story). | 
| 55 | 
            +
                  allow(story).to receive_message_chain(:notes, :all).and_return([note])
         | 
| 56 | 
            +
                  expect(wrapper).to receive(:get_card).and_return(nil)
         | 
| 57 | 
            +
                  expect(Trello::Card).to receive(:create).and_return(card)
         | 
| 58 | 
            +
                  expect(card).to receive(:add_comment).with('[John Smith] My Note')
         | 
| 59 | 
            +
                  expect(wrapper.create_card('list_id', story)).to eq(card)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                it 'adds tasks' do
         | 
| 63 | 
            +
                  task      = OpenStruct.new(description: 'My Task', complete: false)
         | 
| 64 | 
            +
                  story     = mock_pivotal_story
         | 
| 65 | 
            +
                  checklist = double(Trello::Checklist)
         | 
| 66 | 
            +
                  allow(story).to receive_message_chain(:tasks, :all).and_return([task])
         | 
| 67 | 
            +
                  expect(wrapper).to receive(:get_card).and_return(nil)
         | 
| 68 | 
            +
                  expect(Trello::Card).to receive(:create).and_return(card)
         | 
| 69 | 
            +
                  expect(Trello::Checklist).to receive(:create).with(name: 'Tasks', card_id: card.id).and_return(checklist)
         | 
| 70 | 
            +
                  expect(card).to receive(:add_checklist).with(checklist)
         | 
| 71 | 
            +
                  expect(checklist).to receive(:add_item).with(task.description, task.complete)
         | 
| 72 | 
            +
                  expect(wrapper.create_card('list_id', story)).to eq(card)
         | 
| 60 73 | 
             
                end
         | 
| 61 74 | 
             
              end
         | 
| 62 75 |  | 
| 63 76 | 
             
              context '#board_choices' do
         | 
| 64 77 | 
             
                it 'returns a hash of Trello boards' do
         | 
| 65 | 
            -
                  board = OpenStruct.new(: | 
| 66 | 
            -
                  Trello::Board. | 
| 67 | 
            -
                  wrapper.board_choices. | 
| 78 | 
            +
                  board = OpenStruct.new(id: 'id', name: 'My Board')
         | 
| 79 | 
            +
                  expect(Trello::Board).to receive(:all).and_return([board])
         | 
| 80 | 
            +
                  expect(wrapper.board_choices).to eq('id' => 'My Board')
         | 
| 68 81 | 
             
                end
         | 
| 69 82 | 
             
              end
         | 
| 70 83 |  | 
| 71 84 | 
             
              context '#list_choices' do
         | 
| 72 85 | 
             
                it 'returns a hash of Trello lists' do
         | 
| 73 86 | 
             
                  board = double(Trello::Board)
         | 
| 74 | 
            -
                  list  = OpenStruct.new(: | 
| 75 | 
            -
                  Trello::Board. | 
| 76 | 
            -
                  board. | 
| 77 | 
            -
                  wrapper.list_choices('board_id'). | 
| 78 | 
            -
             | 
| 79 | 
            -
                    false => "[don't import these stories]",
         | 
| 80 | 
            -
                  }
         | 
| 87 | 
            +
                  list  = OpenStruct.new(id: 'id', name: 'My List')
         | 
| 88 | 
            +
                  expect(Trello::Board).to receive(:find).with('board_id').and_return(board)
         | 
| 89 | 
            +
                  expect(board).to receive(:lists).and_return([list])
         | 
| 90 | 
            +
                  expect(wrapper.list_choices('board_id')).to eq( 'id'  => 'My List',
         | 
| 91 | 
            +
                                                                  false => "[don't import these stories]")
         | 
| 81 92 | 
             
                end
         | 
| 82 93 | 
             
              end
         | 
| 83 94 |  | 
| 84 95 | 
             
              context '#cards_for_list' do
         | 
| 85 96 | 
             
                it 'returns a hash of Trello lists' do
         | 
| 86 97 | 
             
                  list = double(Trello::List)
         | 
| 87 | 
            -
                  card | 
| 88 | 
            -
                  Trello::List. | 
| 89 | 
            -
                  list. | 
| 90 | 
            -
                  expected = {'193060beddd00d64259bdc1271d6c5a330e92e7d' => card}
         | 
| 91 | 
            -
                  wrapper.cards_for_list('list_id'). | 
| 98 | 
            +
                  card = OpenStruct.new(name: 'My Card', desc: 'My Description')
         | 
| 99 | 
            +
                  expect(Trello::List).to receive(:find).with('list_id').and_return(list)
         | 
| 100 | 
            +
                  expect(list).to receive(:cards).and_return([card])
         | 
| 101 | 
            +
                  expected = { '193060beddd00d64259bdc1271d6c5a330e92e7d' => card }
         | 
| 102 | 
            +
                  expect(wrapper.cards_for_list('list_id')).to eq(expected)
         | 
| 92 103 | 
             
                  # Test caching.
         | 
| 93 | 
            -
                  Trello::List. | 
| 94 | 
            -
                  wrapper.cards_for_list('list_id'). | 
| 104 | 
            +
                  expect(Trello::List).not_to receive(:find)
         | 
| 105 | 
            +
                  expect(wrapper.cards_for_list('list_id')).to eq(expected)
         | 
| 95 106 | 
             
                end
         | 
| 96 107 | 
             
              end
         | 
| 97 108 |  | 
| 98 109 | 
             
              context '#add_label' do
         | 
| 110 | 
            +
                let(:board) { double(Trello::Board) }
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                before do
         | 
| 113 | 
            +
                  expect(Trello::Board).to receive(:find).with('board_id').and_return(board)
         | 
| 114 | 
            +
                  allow(board).to receive(:labels).and_return([])
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 99 117 | 
             
                it 'adds a label if it does not already exist' do
         | 
| 100 | 
            -
                   | 
| 101 | 
            -
                  card | 
| 102 | 
            -
                  card. | 
| 103 | 
            -
                   | 
| 118 | 
            +
                  label = double(Trello::Label)
         | 
| 119 | 
            +
                  card  = mock_trello_card(board_id: 'board_id')
         | 
| 120 | 
            +
                  allow(card).to receive_messages(labels: [])
         | 
| 121 | 
            +
                  expect(Trello::Label).to receive(:create).with(name: 'bug', board_id: 'board_id', color: 'red').and_return(label)
         | 
| 122 | 
            +
                  expect(card).to receive(:add_label).with(label)
         | 
| 123 | 
            +
                  wrapper.add_label(card, 'bug', 'red')
         | 
| 104 124 | 
             
                end
         | 
| 105 125 |  | 
| 106 126 | 
             
                it 'does not add a label if it already exists' do
         | 
| 107 | 
            -
                   | 
| 108 | 
            -
                  card | 
| 109 | 
            -
                   | 
| 110 | 
            -
                   | 
| 127 | 
            +
                  label = double(Trello::Label, id: '1234', name: 'bug', color: 'red')
         | 
| 128 | 
            +
                  card  = mock_trello_card(board_id: 'board_id')
         | 
| 129 | 
            +
                  allow(board).to receive(:labels).and_return([label])
         | 
| 130 | 
            +
                  allow(card).to receive_messages(labels: [label])
         | 
| 131 | 
            +
                  expect(card).not_to receive(:add_label)
         | 
| 132 | 
            +
                  wrapper.add_label(card, 'bug', 'red')
         | 
| 111 133 | 
             
                end
         | 
| 112 134 | 
             
              end
         | 
| 113 135 |  | 
| 114 | 
            -
            end
         | 
| 136 | 
            +
            end
         |