tracco 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +28 -0
- data/.rspec +3 -0
- data/.rvmrc.template +2 -0
- data/.travis.yml +20 -0
- data/CHANGELOG +32 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +110 -0
- data/LICENSE.txt +15 -0
- data/README.md +292 -0
- data/Rakefile +8 -0
- data/config/config.template.yml +6 -0
- data/config/config.yml.trackinguser_for_test +7 -0
- data/config/mongoid.template.yml +24 -0
- data/lib/patches/trello/card.rb +19 -0
- data/lib/patches/trello/member.rb +20 -0
- data/lib/startup_trello.rb +2 -0
- data/lib/tasks/rspec.rake +17 -0
- data/lib/tasks/tasks.rake +55 -0
- data/lib/tracco.rb +30 -0
- data/lib/tracco/effort.rb +35 -0
- data/lib/tracco/estimate.rb +25 -0
- data/lib/tracco/google_docs_exporter.rb +67 -0
- data/lib/tracco/member.rb +61 -0
- data/lib/tracco/mongoid_helper.rb +13 -0
- data/lib/tracco/tracked_card.rb +121 -0
- data/lib/tracco/tracking/base.rb +82 -0
- data/lib/tracco/tracking/card_done_tracking.rb +10 -0
- data/lib/tracco/tracking/effort_tracking.rb +45 -0
- data/lib/tracco/tracking/estimate_tracking.rb +23 -0
- data/lib/tracco/tracking/invalid_tracking.rb +19 -0
- data/lib/tracco/tracking_factory.rb +22 -0
- data/lib/tracco/trello_authorize.rb +16 -0
- data/lib/tracco/trello_configuration.rb +39 -0
- data/lib/tracco/trello_tracker.rb +44 -0
- data/lib/tracco/version.rb +3 -0
- data/script/ci/before_script.sh +1 -0
- data/script/ci/run_build.sh +2 -0
- data/script/crontab.template +8 -0
- data/script/mate.sh +1 -0
- data/spec/effort_spec.rb +59 -0
- data/spec/estimate_spec.rb +38 -0
- data/spec/integration/trello_authorization_spec.rb +12 -0
- data/spec/integration/trello_tracker_spec.rb +66 -0
- data/spec/member_spec.rb +81 -0
- data/spec/patches/trello/card_spec.rb +25 -0
- data/spec/spec_helper.rb +66 -0
- data/spec/support/database_cleaner.rb +12 -0
- data/spec/tracked_card_spec.rb +336 -0
- data/spec/tracking/card_done_tracking_spec.rb +18 -0
- data/spec/tracking/effort_tracking_spec.rb +114 -0
- data/spec/tracking/estimate_tracking_spec.rb +44 -0
- data/spec/tracking_factory_spec.rb +42 -0
- data/spec/trello_authorize_spec.rb +65 -0
- data/spec/trello_configuration_spec.rb +43 -0
- data/spec/trello_tracker_spec.rb +26 -0
- data/tracco.gemspec +41 -0
- data/tracco.sublime-project +10 -0
- metadata +316 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe TrelloTracker do
|
5
|
+
# This integration test works on a real Trello board created just for this purpose
|
6
|
+
# (see https://trello.com/board/testingboard/50ff225c7162123e3600074f)
|
7
|
+
# The board has some cards in todo, one card in progress and one done
|
8
|
+
|
9
|
+
TODO_CARD_ID = "51062b71df2bfec47b0039fb"
|
10
|
+
IN_PROGRESS_CARD_ID = "51062b99d81a343121004046"
|
11
|
+
DONE_CARD_ID = "51062b936d2deed22100157e"
|
12
|
+
ANOTHER_DONE_CARD_ID = "510c46d7c7bc9ac6020007ab"
|
13
|
+
|
14
|
+
let(:config) {
|
15
|
+
# auth params for trackinguser_for_test/testinguser!
|
16
|
+
OpenStruct.new(tracker: "trackinguser_for_test",
|
17
|
+
dev_key: "ef7c400e711057d7ba5e00be20139a33",
|
18
|
+
token: "9047d8fdbfdc960d41910673e300516cc8630dd4967e9b418fc27e410516362e")
|
19
|
+
}
|
20
|
+
|
21
|
+
it "tracks some estimates and efforts", :needs_valid_configuration => true do
|
22
|
+
without_logging do
|
23
|
+
tracker = TrelloTracker.new(tracker_username: config.tracker, developer_public_key: config.dev_key, access_token_key: config.token)
|
24
|
+
tracker.track(DateTime.parse("2013-01-28"))
|
25
|
+
end
|
26
|
+
|
27
|
+
# a card to do
|
28
|
+
todo_card = TrackedCard.find_by_trello_id(TODO_CARD_ID)
|
29
|
+
|
30
|
+
todo_card.estimates.should have(1).estimate
|
31
|
+
todo_card.estimates.first.amount.should == 4
|
32
|
+
todo_card.should_not be_done
|
33
|
+
|
34
|
+
# a card in progress
|
35
|
+
in_progress_card = TrackedCard.find_by_trello_id(IN_PROGRESS_CARD_ID)
|
36
|
+
|
37
|
+
in_progress_card.estimates.should have(1).estimate
|
38
|
+
in_progress_card.estimates.should have(1).effort
|
39
|
+
in_progress_card.estimates.first.amount.should == 5
|
40
|
+
in_progress_card.efforts.first.amount.should == 1
|
41
|
+
in_progress_card.should_not be_done
|
42
|
+
|
43
|
+
# a done card
|
44
|
+
done_card = TrackedCard.find_by_trello_id(DONE_CARD_ID)
|
45
|
+
done_card.total_effort.should == 2
|
46
|
+
done_card.should be_done
|
47
|
+
|
48
|
+
# another done card (this time for the DONE column)
|
49
|
+
another_done_card = TrackedCard.find_by_trello_id(ANOTHER_DONE_CARD_ID)
|
50
|
+
another_done_card.should be_done
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def without_logging(&block)
|
57
|
+
original_error_level = Trello.logger.level
|
58
|
+
|
59
|
+
begin
|
60
|
+
Trello.logger.level = Logger::WARN
|
61
|
+
block.call unless block.nil?
|
62
|
+
ensure
|
63
|
+
Trello.logger.level = original_error_level
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/member_spec.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mongoid-rspec'
|
3
|
+
|
4
|
+
describe Member do
|
5
|
+
|
6
|
+
it { should have_fields(:trello_id, :username, :full_name, :avatar_id, :bio, :url) }
|
7
|
+
it { should be_embedded_in(:effort) }
|
8
|
+
|
9
|
+
describe "validation" do
|
10
|
+
it { should validate_presence_of(:username) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "equality" do
|
14
|
+
it "is equal to another member with the same username" do
|
15
|
+
member = Member.new(username: "piero")
|
16
|
+
same_member = Member.new(username: "piero")
|
17
|
+
different_member = Member.new(username: "tommaso")
|
18
|
+
|
19
|
+
member.should == same_member
|
20
|
+
member.should_not == different_member
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ".build_from" do
|
25
|
+
it "builds a Member from a Trello Member" do
|
26
|
+
member = Member.build_from(Trello::Member.new("username" => "piero"))
|
27
|
+
|
28
|
+
member.username.should == "piero"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "takes the Trello Member id and set it as trello_id" do
|
32
|
+
member = Member.build_from(Trello::Member.new("username" => "piero", "id" => "1234567abc"))
|
33
|
+
|
34
|
+
member.id.should_not == "1234567abc"
|
35
|
+
member.trello_id.should == "1234567abc"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#effort_spent" do
|
40
|
+
%w{piero tommaso}.each do |username|
|
41
|
+
let(username.to_sym) { Member.new(username: username) }
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:card) { TrackedCard.create(name: "any", short_id: 1234, trello_id: "123123") }
|
45
|
+
let(:another_card) { TrackedCard.create(name: "any_other", short_id: 1235, trello_id: "123125") }
|
46
|
+
|
47
|
+
it "is zero when the member did not spent effort at all" do
|
48
|
+
piero.effort_spent.should == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
it "counts the effort spent on a card" do
|
52
|
+
card.efforts << Effort.new(amount: 4, date: Date.today, members: [piero, tommaso])
|
53
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
54
|
+
|
55
|
+
piero.effort_spent.should == 4 / 2
|
56
|
+
end
|
57
|
+
|
58
|
+
it "counts the effort spent on several cards" do
|
59
|
+
card.efforts << Effort.new(amount: 4, date: Date.today, members: [piero, tommaso])
|
60
|
+
another_card.efforts << Effort.new(amount: 5, date: Date.today, members: [piero])
|
61
|
+
|
62
|
+
piero.effort_spent.should == 2 + 5
|
63
|
+
end
|
64
|
+
|
65
|
+
it "counts the effort spent on a card from a given date" do
|
66
|
+
card.efforts << Effort.new(amount: 4, date: Date.yesterday, members: [piero])
|
67
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [piero])
|
68
|
+
|
69
|
+
piero.effort_spent(Date.today).should == 5
|
70
|
+
piero.effort_spent_since(Date.today).should == 5
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#avatar_url" do
|
76
|
+
it "points to the avatar thumbnail image" do
|
77
|
+
member = Member.new(avatar_id: "123xyz")
|
78
|
+
member.avatar_url.should == "https://trello-avatars.s3.amazonaws.com/123xyz/30.png"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Trello::Card do
|
4
|
+
|
5
|
+
describe "#in_done_column?" do
|
6
|
+
|
7
|
+
let(:trello_card) { Trello::Card.new("name" => "a name", "desc" => "any description") }
|
8
|
+
|
9
|
+
let(:todo_column) { Trello::List.new("name" => "ToDo") }
|
10
|
+
let(:done_column) { Trello::List.new("name" => "Done yesterday") }
|
11
|
+
|
12
|
+
it "is done when is in a DONE column" do
|
13
|
+
trello_card.stub(:list).and_return(done_column)
|
14
|
+
|
15
|
+
trello_card.in_done_column?.should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is still not done when is not in DONE column" do
|
19
|
+
trello_card.stub(:list).and_return(todo_column)
|
20
|
+
|
21
|
+
trello_card.in_done_column?.should be_false
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
# Set up gems listed in the Gemfile.
|
7
|
+
begin
|
8
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', File.dirname(__FILE__))
|
9
|
+
require 'bundler'
|
10
|
+
Bundler.setup
|
11
|
+
rescue Bundler::GemNotFound => e
|
12
|
+
STDERR.puts e.message
|
13
|
+
STDERR.puts "Try running `bundle install`."
|
14
|
+
exit!
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler.require(:spec)
|
18
|
+
|
19
|
+
require 'tracco'
|
20
|
+
|
21
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
22
|
+
# in spec/support/ and its subdirectories.
|
23
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
24
|
+
|
25
|
+
RSpec.configure do |configuration|
|
26
|
+
configuration.include Mongoid::Matchers
|
27
|
+
end
|
28
|
+
|
29
|
+
# force test env for the mongodb configuration
|
30
|
+
TrelloConfiguration::Database.load_env("test")
|
31
|
+
|
32
|
+
|
33
|
+
## Spec Helper Methods (TODO: should we move them in a separate file?)
|
34
|
+
|
35
|
+
def unrecognized_notification
|
36
|
+
create_notification(data: { 'text' => '@trackinguser hi there!' })
|
37
|
+
end
|
38
|
+
|
39
|
+
def notification_with_message(message)
|
40
|
+
create_notification(data: { 'text' => message })
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_estimate(time_measurement)
|
44
|
+
create_notification(data: { 'text' => "@trackinguser [1.5#{TIME_MEASUREMENTS[time_measurement]}]" })
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_effort(time_measurement)
|
48
|
+
create_notification(data: { 'text' => "@trackinguser +4.5#{TIME_MEASUREMENTS[time_measurement]}]" })
|
49
|
+
end
|
50
|
+
|
51
|
+
def with(notification)
|
52
|
+
tracking = TrackingFactory.build_from(notification)
|
53
|
+
yield(tracking)
|
54
|
+
end
|
55
|
+
|
56
|
+
def with_message(notification_message, &block)
|
57
|
+
with(notification_with_message(notification_message), &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_notification(custom_params)
|
61
|
+
params = { data: { 'text' => "@trackinguser +2h" }, date: "2012-10-28T21:06:14.801Z", member_creator: stub(username: "pietrodibello") }
|
62
|
+
params.merge!(custom_params)
|
63
|
+
|
64
|
+
stub(data: params[:data], date: params[:date], member_creator: params[:member_creator]).as_null_object
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,336 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TrackedCard do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Date.stub(:today).and_return(Date.parse("2012-11-05"))
|
7
|
+
end
|
8
|
+
|
9
|
+
subject(:card) { TrackedCard.new(name: "any", short_id: 1234, trello_id: "123123") }
|
10
|
+
|
11
|
+
%w{piero tommaso michele}.each do |username|
|
12
|
+
let(username.to_sym) { Member.new(username: username) }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "validations" do
|
16
|
+
it "is not valid when a name is not given" do
|
17
|
+
TrackedCard.new(trello_id: "123456789", short_id: 1234).should_not be_valid
|
18
|
+
end
|
19
|
+
|
20
|
+
it "is not valid when a short_id is not given" do
|
21
|
+
TrackedCard.new(name: "any", trello_id: "123456789").should_not be_valid
|
22
|
+
end
|
23
|
+
|
24
|
+
it "is not valid when a trello_id is not given" do
|
25
|
+
TrackedCard.new(name: "any", short_id: 1234).should_not be_valid
|
26
|
+
end
|
27
|
+
|
28
|
+
it "is valid when has a name, a short id and a trello id" do
|
29
|
+
TrackedCard.new(name: "any", trello_id: "123456789", short_id: 1234).should be_valid
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ".find_by_trello_id" do
|
34
|
+
it "find a card given its Trello id" do
|
35
|
+
card = TrackedCard.create(name: "any card", short_id: 1234, trello_id: "1")
|
36
|
+
another_card = TrackedCard.create(name: "another card", short_id: 3456, trello_id: "2")
|
37
|
+
|
38
|
+
TrackedCard.find_by_trello_id("1").should == card
|
39
|
+
TrackedCard.find_by_trello_id("3").should == nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe ".update_or_create_with" do
|
44
|
+
let(:trello_card) { Trello::Card.new("id" => "ABC123", "name" => "a name", "idShort" => 1, "desc" => "any description") }
|
45
|
+
|
46
|
+
before(:each) do
|
47
|
+
Trello::Card.any_instance.stub(:in_done_column?)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "creates a tracked card on a given trello card" do
|
51
|
+
tracked_card = TrackedCard.update_or_create_with(trello_card)
|
52
|
+
|
53
|
+
tracked_card.name.should == "a name"
|
54
|
+
tracked_card.trello_id == "ABC123"
|
55
|
+
tracked_card.short_id == 1
|
56
|
+
end
|
57
|
+
|
58
|
+
it "updates an existing tracked card on a given trello card" do
|
59
|
+
existing_card = TrackedCard.create(name: "an old name", short_id: 1234, trello_id: trello_card.id)
|
60
|
+
|
61
|
+
updated_card = TrackedCard.update_or_create_with(trello_card)
|
62
|
+
|
63
|
+
updated_card.should == existing_card
|
64
|
+
updated_card.name.should == "a name"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "is nil when the trello card is not valid" do
|
68
|
+
invalid_trello_card = Trello::Card.new("id" => nil, "name" => nil)
|
69
|
+
|
70
|
+
TrackedCard.update_or_create_with(invalid_trello_card).should be_nil
|
71
|
+
TrackedCard.all.should be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it "tracks the card as done when the original trello card is moved in a DONE column" do
|
75
|
+
trello_card.stub(:in_done_column?).and_return(true)
|
76
|
+
|
77
|
+
tracked_card = TrackedCard.update_or_create_with(trello_card)
|
78
|
+
|
79
|
+
tracked_card.should be_done
|
80
|
+
end
|
81
|
+
|
82
|
+
it "tracks the card as NOT done when the original trello card is moved in a column different from DONE" do
|
83
|
+
trello_card.stub(:in_done_column?).and_return(false)
|
84
|
+
|
85
|
+
tracked_card = TrackedCard.update_or_create_with(trello_card)
|
86
|
+
|
87
|
+
tracked_card.should_not be_done
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
describe ".build_from" do
|
93
|
+
it "builds a TrackedCard from a Trello Card" do
|
94
|
+
tracked_card = TrackedCard.build_from(Trello::Card.new("name" => "a name", "desc" => "any description"))
|
95
|
+
|
96
|
+
tracked_card.name.should == "a name"
|
97
|
+
tracked_card.description.should == "any description"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "takes the Trello Card id and set it as trello_id" do
|
101
|
+
tracked_card = TrackedCard.build_from(Trello::Card.new("id" => "abc123", "name" => "a name", "desc" => "any description"))
|
102
|
+
|
103
|
+
tracked_card.id.should_not == "abc123"
|
104
|
+
tracked_card.trello_id.should == "abc123"
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
it "has no estimates and efforts initially" do
|
110
|
+
card.estimates.should be_empty
|
111
|
+
card.efforts.should be_empty
|
112
|
+
end
|
113
|
+
|
114
|
+
it "is possible to add estimates" do
|
115
|
+
card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
|
116
|
+
card.estimates << Estimate.new(amount: 12, date: Date.today)
|
117
|
+
|
118
|
+
card.estimates.should have(2).estimates
|
119
|
+
end
|
120
|
+
|
121
|
+
it "is possible to add efforts" do
|
122
|
+
card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero, tommaso])
|
123
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
124
|
+
|
125
|
+
card.efforts.should have(2).efforts
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "equality" do
|
129
|
+
it "is equal to another TrelloCard when the trello id is the same" do
|
130
|
+
card = TrackedCard.new(name: "a name", trello_id: "123456789")
|
131
|
+
same_card = TrackedCard.new(name: "a name", trello_id: "123456789")
|
132
|
+
another_card = TrackedCard.new(name: "a name", trello_id: "987654321")
|
133
|
+
|
134
|
+
card.should == same_card
|
135
|
+
card.should_not == another_card
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#add" do
|
140
|
+
let(:card) { TrackedCard.new(name: "a name", trello_id: "123456789") }
|
141
|
+
let(:estimate_tracking) { Tracking::EstimateTracking.new(create_notification(data: { 'text' => "@trackinguser [1h]" })) }
|
142
|
+
|
143
|
+
it "adds an estimate from a tracking estimate notification" do
|
144
|
+
card.add(estimate_tracking)
|
145
|
+
card.estimates.should have(1).estimate
|
146
|
+
end
|
147
|
+
|
148
|
+
it "adds an estimate only once" do
|
149
|
+
card.add(estimate_tracking)
|
150
|
+
card.add(estimate_tracking)
|
151
|
+
|
152
|
+
card.estimates.should have(1).estimate
|
153
|
+
end
|
154
|
+
|
155
|
+
it "is done when has a DONE notification" do
|
156
|
+
card.should_not be_done
|
157
|
+
|
158
|
+
card.add(Tracking::CardDoneTracking.new(stub(:notification)))
|
159
|
+
card.should be_done
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#add!" do
|
165
|
+
let(:card) { TrackedCard.new(name: "a name", trello_id: "123456789", short_id: "123") }
|
166
|
+
|
167
|
+
it "saves the tracked card after adding the tracking" do
|
168
|
+
any_tracking = Tracking::EstimateTracking.new(create_notification(data: { 'text' => "@trackinguser [1h]" }))
|
169
|
+
|
170
|
+
card.add!(any_tracking)
|
171
|
+
card.reload.should_not be_nil
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#total_effort" do
|
176
|
+
it "is zero when there's no effort" do
|
177
|
+
card.total_effort.should == 0
|
178
|
+
end
|
179
|
+
|
180
|
+
it "computes the total effort on the card" do
|
181
|
+
card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero, tommaso])
|
182
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
183
|
+
|
184
|
+
card.total_effort.should == 3+5
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "#last_estimate_error" do
|
189
|
+
it "is nil when the card has no estimate" do
|
190
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
191
|
+
|
192
|
+
card.last_estimate_error.should be_nil
|
193
|
+
end
|
194
|
+
|
195
|
+
it "is nil when the card has no effort" do
|
196
|
+
card.efforts << Estimate.new(amount: 5, date: Date.today)
|
197
|
+
|
198
|
+
card.last_estimate_error.should be_nil
|
199
|
+
end
|
200
|
+
|
201
|
+
it "is zero when actual effort is equal to estimate" do
|
202
|
+
card.estimates << Estimate.new(amount: 5, date: Date.today)
|
203
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
204
|
+
|
205
|
+
card.last_estimate_error.should == 0.0
|
206
|
+
end
|
207
|
+
|
208
|
+
it "is 100 when the actual effort is twice the given estimate" do
|
209
|
+
card.estimates << Estimate.new(amount: 5, date: Date.today)
|
210
|
+
card.efforts << Effort.new(amount: 10, date: Date.today, members: [tommaso])
|
211
|
+
|
212
|
+
card.last_estimate_error.should == 100.0
|
213
|
+
end
|
214
|
+
|
215
|
+
it "is -50 when the actual effort is half of the given estimate" do
|
216
|
+
card.estimates << Estimate.new(amount: 10, date: Date.today)
|
217
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
218
|
+
|
219
|
+
card.last_estimate_error.should == -50.0
|
220
|
+
end
|
221
|
+
|
222
|
+
it "is rounded with two decimal digits" do
|
223
|
+
card.estimates << Estimate.new(amount: 3, date: Date.today)
|
224
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
225
|
+
|
226
|
+
card.last_estimate_error.should == 66.67
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#estimate_errors" do
|
230
|
+
|
231
|
+
it "collects all the estimate errors against the actual effort" do
|
232
|
+
card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
|
233
|
+
card.efforts << Effort.new(amount: 10, date: Date.yesterday, members: [tommaso])
|
234
|
+
|
235
|
+
card.estimates << Estimate.new(amount: 10, date: Date.today)
|
236
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
237
|
+
|
238
|
+
card.estimate_errors.should == [200.0, 50.0]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "#members" do
|
245
|
+
it "lists all the members which spent some effort on the card" do
|
246
|
+
card.efforts << Effort.new(amount: 3, date: Date.today, members: [piero, tommaso])
|
247
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
248
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso, michele])
|
249
|
+
|
250
|
+
card.members.should == [piero, tommaso, michele]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "#working_start_date" do
|
255
|
+
|
256
|
+
it "is the date of the first effort spent on the card" do
|
257
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
258
|
+
card.efforts << Effort.new(amount: 3, date: Date.yesterday, members: [tommaso])
|
259
|
+
card.efforts << Effort.new(amount: 5, date: Date.tomorrow, members: [tommaso])
|
260
|
+
|
261
|
+
card.working_start_date.should == Date.yesterday
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
describe "#first_activity_date" do
|
266
|
+
it "is the date of the first effort or estimate given on the card" do
|
267
|
+
card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
|
268
|
+
card.estimates << Estimate.new(amount: 12, date: Date.yesterday.prev_day)
|
269
|
+
card.estimates << Estimate.new(amount: 12, date: Date.tomorrow)
|
270
|
+
|
271
|
+
card.efforts << Effort.new(amount: 3, date: Date.yesterday, members: [tommaso])
|
272
|
+
card.efforts << Effort.new(amount: 5, date: Date.today, members: [tommaso])
|
273
|
+
|
274
|
+
card.first_activity_date.should == Date.yesterday.prev_day
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe "#first_estimate_date" do
|
279
|
+
it "is the date of the first estimate given on the card" do
|
280
|
+
card.estimates << Estimate.new(amount: 5, date: Date.tomorrow)
|
281
|
+
card.estimates << Estimate.new(amount: 12, date: Date.yesterday.prev_day)
|
282
|
+
card.estimates << Estimate.new(amount: 12, date: Date.today)
|
283
|
+
|
284
|
+
card.first_estimate_date.should == Date.yesterday.prev_day
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe "#last_estimate_date" do
|
289
|
+
it "is the date of the last estimate given on the card" do
|
290
|
+
card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
|
291
|
+
card.estimates << Estimate.new(amount: 12, date: Date.tomorrow)
|
292
|
+
card.estimates << Estimate.new(amount: 12, date: Date.today)
|
293
|
+
|
294
|
+
card.last_estimate_date.should == Date.tomorrow
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "#no_tracking?" do
|
299
|
+
it "is false when there's no effort or estimate tracked on the card" do
|
300
|
+
card.no_tracking?.should be_true
|
301
|
+
|
302
|
+
card.estimates << Estimate.new(amount: 5, date: Date.yesterday)
|
303
|
+
card.no_tracking?.should be_false
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe "#to_s" do
|
308
|
+
it "describes the card as a string" do
|
309
|
+
card = TrackedCard.new(name: "A Story Name")
|
310
|
+
card.estimates << Estimate.new(amount: 5, date: Date.today)
|
311
|
+
card.efforts << Effort.new(amount: 3, date: Date.today, members: [Member.new(username: "piero"), Member.new(username: "tommaso")])
|
312
|
+
card.efforts << Effort.new(amount: 6, date: Date.today, members: [Member.new(username: "piero"), Member.new(username: "tommaso")])
|
313
|
+
|
314
|
+
card.to_s.should == %Q{[A Story Name]. Total effort: 9.0h. Estimates ["[2012-11-05] estimated 5.0 hours"]. Efforts: ["[2012-11-05] spent 3.0 hours by @piero, @tommaso", "[2012-11-05] spent 6.0 hours by @piero, @tommaso"]}
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe "#status" do
|
319
|
+
it "is done when is done" do
|
320
|
+
done_card = TrackedCard.new(done:true)
|
321
|
+
done_card.status.should == :done
|
322
|
+
|
323
|
+
done_card.efforts << Effort.new(amount: 3, date: Date.today, members: [Member.new(username: "any")])
|
324
|
+
done_card.status.should == :done
|
325
|
+
end
|
326
|
+
|
327
|
+
it "is todo when no effort has been spent on the card" do
|
328
|
+
card = TrackedCard.new
|
329
|
+
card.status.should == :todo
|
330
|
+
|
331
|
+
card.efforts << Effort.new(amount: 3, date: Date.today, members: [Member.new(username: "any")])
|
332
|
+
card.status.should == :in_progress
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|