trollolo 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +5 -1
- data/CHANGELOG.md +29 -0
- data/Gemfile +7 -2
- data/README.md +19 -0
- data/bin/trollolo +1 -1
- data/lib/array.rb +6 -0
- data/lib/backup.rb +67 -0
- data/lib/burndown_chart.rb +96 -67
- data/lib/burndown_data.rb +62 -123
- data/lib/card.rb +74 -30
- data/lib/cli.rb +131 -9
- data/lib/column.rb +61 -0
- data/lib/result.rb +0 -0
- data/lib/scrum_board.rb +104 -0
- data/lib/settings.rb +9 -4
- data/lib/trello_wrapper.rb +62 -0
- data/lib/trollolo.rb +10 -7
- data/lib/version.rb +1 -1
- data/scripts/.gitignore +1 -0
- data/scripts/burndowndata.py +113 -0
- data/scripts/create_burndown.py +111 -146
- data/scripts/graph.py +116 -0
- data/scripts/plot.py +131 -0
- data/spec/data/board.json +63 -0
- data/spec/data/burndown-data.yaml +3 -0
- data/spec/data/burndown_dir/burndown-data-01.yaml +1 -1
- data/spec/data/burndown_dir/burndown-data-02.yaml +1 -1
- data/spec/data/card.json +61 -0
- data/spec/data/full-board.json +1626 -0
- data/spec/data/lists.json +25 -25
- data/spec/data/trollolorc +5 -0
- data/spec/{command_line_spec.rb → integration/command_line_spec.rb} +1 -4
- data/spec/integration/create_burndown_spec.rb +57 -0
- data/spec/integration/integration_spec_helper.rb +10 -0
- data/spec/integration/support/aruba_hook.rb +11 -0
- data/spec/integration/support/custom_matchers.rb +13 -0
- data/spec/{wrapper → integration/wrapper}/credentials_input_wrapper +2 -2
- data/spec/{wrapper → integration/wrapper}/empty_config_trollolo_wrapper +2 -2
- data/spec/integration/wrapper/trollolo_wrapper +10 -0
- data/spec/unit/backup_spec.rb +107 -0
- data/spec/unit/burndown_chart_spec.rb +396 -0
- data/spec/unit/burndown_data_spec.rb +118 -0
- data/spec/unit/card_spec.rb +79 -0
- data/spec/unit/cli_spec.rb +38 -0
- data/spec/unit/retrieve_data_spec.rb +54 -0
- data/spec/unit/scrum_board_spec.rb +18 -0
- data/spec/{settings_spec.rb → unit/settings_spec.rb} +1 -1
- data/spec/{spec_helper.rb → unit/spec_helper.rb} +4 -12
- data/spec/unit/support/test_data_operations.rb +7 -0
- data/spec/unit/support/update_webmock_data +17 -0
- data/spec/unit/support/webmocks.rb +52 -0
- data/spec/unit/trello_wrapper_spec.rb +47 -0
- data/trollolo.gemspec +10 -11
- metadata +54 -37
- data/lib/trello.rb +0 -66
- data/spec/burndown_chart_spec.rb +0 -307
- data/spec/burndown_data_spec.rb +0 -125
- data/spec/card_spec.rb +0 -15
- data/spec/cli_spec.rb +0 -18
- data/spec/data/cards.json +0 -1002
- data/spec/trello_spec.rb +0 -32
- data/spec/wrapper/trollolo_wrapper +0 -11
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include GivenFilesystemSpecHelpers
|
4
|
+
|
5
|
+
describe BurndownData do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@burndown = BurndownData.new(dummy_settings)
|
9
|
+
@burndown.board_id = "53186e8391ef8671265eba9d"
|
10
|
+
full_board_mock
|
11
|
+
end
|
12
|
+
|
13
|
+
describe BurndownData::Result do
|
14
|
+
it "calculates total" do
|
15
|
+
r = described_class.new
|
16
|
+
r.open = 7
|
17
|
+
r.done = 4
|
18
|
+
|
19
|
+
expect(r.total).to eq 11
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "setters" do
|
24
|
+
it "sets open story points" do
|
25
|
+
@burndown.story_points.open = 13
|
26
|
+
expect(@burndown.story_points.open).to eq 13
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets open tasks" do
|
30
|
+
@burndown.tasks.open = 42
|
31
|
+
expect(@burndown.tasks.open).to eq 42
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#fetch" do
|
36
|
+
|
37
|
+
before do
|
38
|
+
@burndown.fetch
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns story points" do
|
42
|
+
expect( @burndown.story_points.total ).to eq 16
|
43
|
+
expect( @burndown.story_points.open ).to eq 13
|
44
|
+
expect( @burndown.story_points.done ).to eq 3
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns extra story points" do
|
48
|
+
expect( @burndown.extra_story_points.total ).to eq 8
|
49
|
+
expect( @burndown.extra_story_points.open ).to eq 8
|
50
|
+
expect( @burndown.extra_story_points.done ).to eq 0
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns tasks" do
|
54
|
+
expect( @burndown.tasks.total ).to eq 13
|
55
|
+
expect( @burndown.tasks.open ).to eq 9
|
56
|
+
expect( @burndown.tasks.done ).to eq 4
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns extra tasks" do
|
60
|
+
expect( @burndown.extra_tasks.total ).to eq 1
|
61
|
+
expect( @burndown.extra_tasks.open ).to eq 1
|
62
|
+
expect( @burndown.extra_tasks.done ).to eq 0
|
63
|
+
end
|
64
|
+
|
65
|
+
it "returns meta data" do
|
66
|
+
expect( @burndown.meta ).to eq({
|
67
|
+
"sprint" => 10,
|
68
|
+
"total_days" => 18,
|
69
|
+
"weekend_lines" => [1.5, 6.5, 11.5, 16.5]
|
70
|
+
})
|
71
|
+
end
|
72
|
+
|
73
|
+
it "saves date and time" do
|
74
|
+
expected_date_time = DateTime.parse("2015-01-12T13:57:16+01:00")
|
75
|
+
allow(DateTime).to receive(:now).and_return(expected_date_time)
|
76
|
+
@burndown.fetch
|
77
|
+
expect(@burndown.date_time).to eq(expected_date_time)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#to_hash' do
|
82
|
+
|
83
|
+
it 'converts to hash' do
|
84
|
+
@burndown.story_points.open = 1
|
85
|
+
@burndown.story_points.done = 2
|
86
|
+
@burndown.tasks.open = 3
|
87
|
+
@burndown.tasks.done = 4
|
88
|
+
@burndown.extra_story_points.open = 5
|
89
|
+
@burndown.extra_story_points.done = 6
|
90
|
+
@burndown.extra_tasks.open = 7
|
91
|
+
@burndown.extra_tasks.done = 8
|
92
|
+
@burndown.date_time = DateTime.parse('20150115')
|
93
|
+
|
94
|
+
expected_hash = {
|
95
|
+
'date' => '2015-01-15',
|
96
|
+
'updated_at' => '2015-01-15T00:00:00+00:00',
|
97
|
+
'story_points' => {
|
98
|
+
'total' => 3,
|
99
|
+
'open' => 1
|
100
|
+
},
|
101
|
+
'tasks' => {
|
102
|
+
'total' => 7,
|
103
|
+
'open' => 3
|
104
|
+
},
|
105
|
+
'story_points_extra' => {
|
106
|
+
'done' => 6
|
107
|
+
},
|
108
|
+
'tasks_extra' => {
|
109
|
+
'done' => 8
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
expect(@burndown.to_hash).to eq(expected_hash)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Card do
|
4
|
+
|
5
|
+
describe "parses name" do
|
6
|
+
before(:each) do
|
7
|
+
allow_any_instance_of(Card).to receive(:init_data)
|
8
|
+
@card = Card.new(double, double)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "extracts single digit story point value from card name" do
|
12
|
+
allow(@card).to receive(:name).and_return("(3) P1: Refactor cards")
|
13
|
+
expect(@card.story_points).to eq(3)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "extracts double digit story point value from card name" do
|
17
|
+
allow(@card).to receive(:name).and_return "(13) P1: Refactor cards"
|
18
|
+
expect(@card.story_points).to eq(13)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "extracts fractional story point value from card name" do
|
22
|
+
allow(@card).to receive(:name).and_return "(0.5) P1: Refactor cards"
|
23
|
+
expect(@card.story_points).to eq(0.5)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "extracts story points when value is not at beginning of card name" do
|
27
|
+
allow(@card).to receive(:name).and_return "P01: (3) Refactor cards"
|
28
|
+
expect(@card.story_points).to eq(3)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#parse_yaml_from_description" do
|
33
|
+
it "parses description only having YAML" do
|
34
|
+
description = <<EOT
|
35
|
+
```yaml
|
36
|
+
total_days: 18
|
37
|
+
weekend_lines:
|
38
|
+
- 1.5
|
39
|
+
- 6.5
|
40
|
+
```
|
41
|
+
EOT
|
42
|
+
meta = Card.parse_yaml_from_description(description)
|
43
|
+
expect(meta["total_days"]).to eq(18)
|
44
|
+
expect(meta["weekend_lines"]).to eq([1.5, 6.5])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "parses description only having unmarked YAML" do
|
48
|
+
description = <<EOT
|
49
|
+
```
|
50
|
+
total_days: 18
|
51
|
+
weekend_lines:
|
52
|
+
- 1.5
|
53
|
+
- 6.5
|
54
|
+
```
|
55
|
+
EOT
|
56
|
+
meta = Card.parse_yaml_from_description(description)
|
57
|
+
expect(meta["total_days"]).to eq(18)
|
58
|
+
expect(meta["weekend_lines"]).to eq([1.5, 6.5])
|
59
|
+
end
|
60
|
+
|
61
|
+
it "parses description having YAML and text" do
|
62
|
+
description = <<EOT
|
63
|
+
This is some text
|
64
|
+
|
65
|
+
```yaml
|
66
|
+
total_days: 18
|
67
|
+
weekend_lines:
|
68
|
+
- 1.5
|
69
|
+
- 6.5
|
70
|
+
```
|
71
|
+
|
72
|
+
And more text.
|
73
|
+
EOT
|
74
|
+
meta = Card.parse_yaml_from_description(description)
|
75
|
+
expect(meta["total_days"]).to eq(18)
|
76
|
+
expect(meta["weekend_lines"]).to eq([1.5, 6.5])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include GivenFilesystemSpecHelpers
|
4
|
+
|
5
|
+
describe Cli do
|
6
|
+
|
7
|
+
use_given_filesystem
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
Cli.settings = dummy_settings
|
11
|
+
@cli = Cli.new
|
12
|
+
|
13
|
+
allow(STDOUT).to receive(:puts)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "fetches burndown data" do
|
17
|
+
expect_any_instance_of(BurndownData).to receive(:fetch)
|
18
|
+
|
19
|
+
@cli.fetch_burndown_data
|
20
|
+
end
|
21
|
+
|
22
|
+
it "fetches burndown data from board-list" do
|
23
|
+
full_board_mock
|
24
|
+
dir = given_directory
|
25
|
+
@cli.options = {"board-list" => "spec/data/board-list.yaml",
|
26
|
+
"output" => dir}
|
27
|
+
@cli.burndowns
|
28
|
+
expect(File.exist?(File.join(dir,"orange/burndown-data-01.yaml")))
|
29
|
+
expect(File.exist?(File.join(dir,"blue/burndown-data-01.yaml")))
|
30
|
+
end
|
31
|
+
|
32
|
+
it "backups board" do
|
33
|
+
expect_any_instance_of(Backup).to receive(:backup)
|
34
|
+
@cli.options = {"board-id" => "1234"}
|
35
|
+
@cli.backup
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "retrieve data through Trello API" do
|
4
|
+
before(:each) do
|
5
|
+
full_board_mock
|
6
|
+
trello_wrapper = TrelloWrapper.new(dummy_settings)
|
7
|
+
@board = trello_wrapper.board("53186e8391ef8671265eba9d")
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "board" do
|
11
|
+
it "gets id" do
|
12
|
+
expect(@board.id).to eq("53186e8391ef8671265eba9d")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gets columns" do
|
16
|
+
columns = @board.columns
|
17
|
+
expect(columns.count).to eq(6)
|
18
|
+
expect(columns[0].name).to eq("Sprint Backlog")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "gets cards" do
|
22
|
+
cards = @board.columns[0].cards
|
23
|
+
expect(cards.count).to eq(6)
|
24
|
+
expect(cards[0].name).to eq("Sprint 3")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "gets checklist item counts" do
|
28
|
+
card = @board.columns[1].cards[0]
|
29
|
+
expect(card.tasks).to eq(2)
|
30
|
+
expect(card.done_tasks).to eq(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "gets card labels" do
|
34
|
+
card = @board.columns[0].cards[5]
|
35
|
+
expect(card.card_labels.count).to eq(1)
|
36
|
+
expect(card.card_labels[0]["name"]).to eq("Under waterline")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "gets card description" do
|
40
|
+
card = @board.columns[2].cards[1]
|
41
|
+
expected_desc = <<EOT
|
42
|
+
```yaml
|
43
|
+
total_days: 18
|
44
|
+
weekend_lines:
|
45
|
+
- 1.5
|
46
|
+
- 6.5
|
47
|
+
- 11.5
|
48
|
+
- 16.5
|
49
|
+
```
|
50
|
+
EOT
|
51
|
+
expect(card.desc).to eq(expected_desc.chomp)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe ScrumBoard do
|
4
|
+
describe '#done_column' do
|
5
|
+
before(:each) do
|
6
|
+
@settings = dummy_settings
|
7
|
+
|
8
|
+
board_data = JSON.parse(load_test_file("full-board.json"))
|
9
|
+
@scrum_board = ScrumBoard.new(board_data, @settings)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raises error when done column cannot be found' do
|
13
|
+
@settings.done_column_name_regex = /thiscolumndoesntexist/
|
14
|
+
|
15
|
+
expect{@scrum_board.done_column}.to raise_error ScrumBoard::DoneColumnNotFoundError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,7 +6,7 @@ describe Settings do
|
|
6
6
|
|
7
7
|
context "given config file" do
|
8
8
|
before(:each) do
|
9
|
-
@settings = Settings.new( File.expand_path('
|
9
|
+
@settings = Settings.new( File.expand_path('../../data/trollolorc',__FILE__) )
|
10
10
|
end
|
11
11
|
|
12
12
|
it "is not verbose by default" do
|
@@ -1,23 +1,15 @@
|
|
1
1
|
require "codeclimate-test-reporter"
|
2
2
|
CodeClimate::TestReporter.start
|
3
|
-
|
4
|
-
require_relative '../lib/trollolo'
|
5
|
-
|
3
|
+
require_relative '../../lib/trollolo'
|
6
4
|
require 'given_filesystem/spec_helpers'
|
7
5
|
require 'webmock/rspec'
|
8
|
-
|
6
|
+
require 'byebug'
|
9
7
|
WebMock.disable_net_connect!(:allow => "codeclimate.com")
|
10
8
|
|
11
|
-
bin_path = File.expand_path( "
|
9
|
+
bin_path = File.expand_path( "../../../bin/", __FILE__ )
|
12
10
|
|
13
11
|
if ENV['PATH'] !~ /#{bin_path}/
|
14
12
|
ENV['PATH'] = bin_path + File::PATH_SEPARATOR + ENV['PATH']
|
15
13
|
end
|
16
14
|
|
17
|
-
|
18
|
-
File.read(File.expand_path('../data/' + filename,__FILE__))
|
19
|
-
end
|
20
|
-
|
21
|
-
def dummy_settings
|
22
|
-
Settings.new( File.expand_path('../data/trollolorc',__FILE__) )
|
23
|
-
end
|
15
|
+
Dir.glob(::File.expand_path('../support/*.rb', __FILE__)).each { |f| require_relative f }
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "webmocks"
|
4
|
+
|
5
|
+
trollolo_bin = File.expand_path("../../../bin/trollolo", __FILE__)
|
6
|
+
spec_data_dir = File.expand_path("../../data", __FILE__)
|
7
|
+
|
8
|
+
STDERR.puts "Updating web mock data"
|
9
|
+
|
10
|
+
webmock_mapping.each do |mapping|
|
11
|
+
url = mapping[:path] + parameters_as_string(mapping)
|
12
|
+
file = mapping[:file]
|
13
|
+
|
14
|
+
cmd = "#{trollolo_bin} get-raw '#{url}' >#{spec_data_dir}/#{file}"
|
15
|
+
puts cmd
|
16
|
+
system cmd
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
def webmock_mapping
|
2
|
+
[
|
3
|
+
{
|
4
|
+
path: 'boards/53186e8391ef8671265eba9d',
|
5
|
+
file: 'board.json'
|
6
|
+
},
|
7
|
+
{
|
8
|
+
path: 'boards/53186e8391ef8671265eba9d/lists',
|
9
|
+
parameters: {
|
10
|
+
"filter" => "open"
|
11
|
+
},
|
12
|
+
file: 'lists.json'
|
13
|
+
},
|
14
|
+
{
|
15
|
+
path: 'boards/53186e8391ef8671265eba9d',
|
16
|
+
parameters: {
|
17
|
+
"cards" => "open",
|
18
|
+
"lists" => "open",
|
19
|
+
"card_checklists" => "all"
|
20
|
+
},
|
21
|
+
file: 'full-board.json'
|
22
|
+
}
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def parameters_as_string(mapping, parameters = nil)
|
27
|
+
parameters ||= []
|
28
|
+
if mapping[:parameters]
|
29
|
+
mapping[:parameters].each do |key, value|
|
30
|
+
parameters.push("#{key}=#{value}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
if !parameters.empty?
|
34
|
+
parameters_string = "?" + parameters.join("&")
|
35
|
+
else
|
36
|
+
parameters_string = ""
|
37
|
+
end
|
38
|
+
parameters_string
|
39
|
+
end
|
40
|
+
|
41
|
+
def mapping_url(mapping, parameters = nil)
|
42
|
+
url = "https://api.trello.com/1/" + mapping[:path]
|
43
|
+
url += parameters_as_string(mapping, parameters)
|
44
|
+
end
|
45
|
+
|
46
|
+
def full_board_mock
|
47
|
+
webmock_mapping.each do |mapping|
|
48
|
+
url = mapping_url(mapping, [ "key=mykey", "token=mytoken" ])
|
49
|
+
stub_request(:get, url)
|
50
|
+
.to_return(:status => 200, :body => load_test_file(mapping[:file]))
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe TrelloWrapper do
|
4
|
+
|
5
|
+
let!(:settings){ double('settings', developer_public_key: "mykey", member_token: "mytoken") }
|
6
|
+
subject { described_class.new(settings) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_request(:get, "https://api.trello.com/1/boards/myboard?cards=open&key=mykey&lists=open&token=mytoken").
|
10
|
+
to_return(:status => 200, :body => load_test_file("board.json"), :headers => {})
|
11
|
+
full_board_mock
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.new' do
|
15
|
+
it 'populates settings' do
|
16
|
+
expect(subject.instance_variable_get(:@settings)).to be settings
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'init trello configuration' do
|
20
|
+
expect_any_instance_of(described_class).to receive(:init_trello)
|
21
|
+
described_class.new(settings)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#board' do
|
26
|
+
before(:each) do
|
27
|
+
expect(subject).to receive(:retrieve_board_data).with('myboard').and_return(:board)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'finds board via Trello' do
|
31
|
+
subject.board("myboard")
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'instantiate ScrumBoard with trello board and settings' do
|
35
|
+
expect(ScrumBoard).to receive(:new).with(:board, subject.instance_variable_get(:@settings))
|
36
|
+
subject.board("myboard")
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns instance of a ScrumBoard' do
|
40
|
+
expect(subject.board("myboard")).to be_instance_of(ScrumBoard)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'memoize board object' do
|
44
|
+
expect(subject.board("myboard")).to be subject.board("myboard")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|