trollolo 0.0.3 → 0.0.4
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/.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
|