jira-auto-tool 0.1.1
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +291 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Guardfile +105 -0
- data/LICENSE.txt +21 -0
- data/README.md +159 -0
- data/Rakefile +20 -0
- data/bin/jira-auto-tool +57 -0
- data/bin/jira-auto-tool.bat +2 -0
- data/bin/setup +8 -0
- data/bin/setup-dev-win.bat +3 -0
- data/cucumber.yml +7 -0
- data/features/align_sprint_time_in_dates.feature +33 -0
- data/features/assign_tickets_to_team_sprints.feature +73 -0
- data/features/cache_boards.feature +24 -0
- data/features/control_http_request_rate_limit.feature +17 -0
- data/features/create_sprints_using_existing_ones_as_reference.feature +79 -0
- data/features/list_boards.feature +12 -0
- data/features/list_project_fields.feature +89 -0
- data/features/list_sprint_prefixes.feature +77 -0
- data/features/quarterly_add_sprints_using_existing_ones_as_a_reference.feature +71 -0
- data/features/quarterly_create_sprints_until_specific_date.feature +75 -0
- data/features/quarterly_rename_sprints.feature +179 -0
- data/features/rename_sprints.feature +203 -0
- data/features/self_documented_command_line.feature +15 -0
- data/features/sprint_filtering.feature +111 -0
- data/features/step_definitions/execution_context_steps.rb +33 -0
- data/features/step_definitions/jira_board_steps.rb +102 -0
- data/features/step_definitions/jira_ticket_steps.rb +63 -0
- data/features/support/10.setup_cucumber.rb +10 -0
- data/features/support/env.rb +25 -0
- data/features/support/hooks.rb +25 -0
- data/features/support/setup_rspec.rb +14 -0
- data/features/support/setup_simplecov.rb +5 -0
- data/features/update_sprint_end_date_and_shift_following_ones.feature +52 -0
- data/lib/jira/auto/tool/board/cache.rb +67 -0
- data/lib/jira/auto/tool/board/unavailable_board.rb +36 -0
- data/lib/jira/auto/tool/board.rb +105 -0
- data/lib/jira/auto/tool/board_controller/options.rb +32 -0
- data/lib/jira/auto/tool/board_controller.rb +88 -0
- data/lib/jira/auto/tool/common_options.rb +37 -0
- data/lib/jira/auto/tool/config/options.rb +19 -0
- data/lib/jira/auto/tool/config.rb +64 -0
- data/lib/jira/auto/tool/fetch_custom_field_options.rb +47 -0
- data/lib/jira/auto/tool/field.rb +59 -0
- data/lib/jira/auto/tool/field_controller.rb +50 -0
- data/lib/jira/auto/tool/field_option.rb +35 -0
- data/lib/jira/auto/tool/get_createmeta_for_project.rb +24 -0
- data/lib/jira/auto/tool/helpers/environment_based_value.rb +96 -0
- data/lib/jira/auto/tool/helpers/option_parser.rb +16 -0
- data/lib/jira/auto/tool/helpers/overridable_time.rb +18 -0
- data/lib/jira/auto/tool/helpers/pagination.rb +50 -0
- data/lib/jira/auto/tool/jira_http_options.rb +20 -0
- data/lib/jira/auto/tool/next_sprint_creator.rb +60 -0
- data/lib/jira/auto/tool/performer/options.rb +76 -0
- data/lib/jira/auto/tool/performer/planning_increment_sprint_creator.rb +42 -0
- data/lib/jira/auto/tool/performer/prefix_sprint_updater.rb +42 -0
- data/lib/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator.rb +60 -0
- data/lib/jira/auto/tool/performer/quarterly_sprint_renamer.rb +19 -0
- data/lib/jira/auto/tool/performer/sprint_end_date_updater.rb +55 -0
- data/lib/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator.rb +19 -0
- data/lib/jira/auto/tool/performer/sprint_renamer/next_name_generator.rb +47 -0
- data/lib/jira/auto/tool/performer/sprint_renamer.rb +55 -0
- data/lib/jira/auto/tool/performer/sprint_time_in_dates_aligner.rb +52 -0
- data/lib/jira/auto/tool/project/options.rb +22 -0
- data/lib/jira/auto/tool/project/ticket_fields.rb +70 -0
- data/lib/jira/auto/tool/project.rb +40 -0
- data/lib/jira/auto/tool/rate_limited_jira_client.rb +50 -0
- data/lib/jira/auto/tool/request_builder/field_context_fetcher.rb +78 -0
- data/lib/jira/auto/tool/request_builder/field_option_fetcher.rb +54 -0
- data/lib/jira/auto/tool/request_builder/get.rb +29 -0
- data/lib/jira/auto/tool/request_builder/sprint_creator.rb +112 -0
- data/lib/jira/auto/tool/request_builder/sprint_state_updater.rb +60 -0
- data/lib/jira/auto/tool/request_builder.rb +89 -0
- data/lib/jira/auto/tool/setup_logging.rb +35 -0
- data/lib/jira/auto/tool/sprint/name.rb +105 -0
- data/lib/jira/auto/tool/sprint/prefix.rb +66 -0
- data/lib/jira/auto/tool/sprint.rb +183 -0
- data/lib/jira/auto/tool/sprint_controller/options.rb +61 -0
- data/lib/jira/auto/tool/sprint_controller.rb +152 -0
- data/lib/jira/auto/tool/sprint_state_controller.rb +58 -0
- data/lib/jira/auto/tool/team.rb +23 -0
- data/lib/jira/auto/tool/team_sprint_prefix_mapper/options.rb +27 -0
- data/lib/jira/auto/tool/team_sprint_prefix_mapper.rb +62 -0
- data/lib/jira/auto/tool/team_sprint_ticket_dispatcher.rb +76 -0
- data/lib/jira/auto/tool/ticket.rb +110 -0
- data/lib/jira/auto/tool/until_date.rb +68 -0
- data/lib/jira/auto/tool/version.rb +9 -0
- data/lib/jira/auto/tool.rb +216 -0
- data/sig/jira/sprint/tool.rbs +8 -0
- data/spec/jira/auto/tool/board/cache_spec.rb +179 -0
- data/spec/jira/auto/tool/board/unavailable_board_spec.rb +34 -0
- data/spec/jira/auto/tool/board_controller/options_spec.rb +52 -0
- data/spec/jira/auto/tool/board_controller_spec.rb +154 -0
- data/spec/jira/auto/tool/board_spec.rb +163 -0
- data/spec/jira/auto/tool/common_options_spec.rb +49 -0
- data/spec/jira/auto/tool/config_spec.rb +108 -0
- data/spec/jira/auto/tool/field_controller_spec.rb +121 -0
- data/spec/jira/auto/tool/field_option_spec.rb +42 -0
- data/spec/jira/auto/tool/field_spec.rb +99 -0
- data/spec/jira/auto/tool/helpers/environment_based_value_spec.rb +21 -0
- data/spec/jira/auto/tool/helpers/option_parser_spec.rb +21 -0
- data/spec/jira/auto/tool/helpers/overridable_time_spec.rb +43 -0
- data/spec/jira/auto/tool/helpers/pagination_spec.rb +72 -0
- data/spec/jira/auto/tool/jira_http_options_spec.rb +32 -0
- data/spec/jira/auto/tool/next_sprint_creator_spec.rb +85 -0
- data/spec/jira/auto/tool/performer/option_spec.rb +55 -0
- data/spec/jira/auto/tool/performer/planning_increment_sprint_creator_spec.rb +62 -0
- data/spec/jira/auto/tool/performer/prefix_sprint_updater_spec.rb +35 -0
- data/spec/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator_spec.rb +175 -0
- data/spec/jira/auto/tool/performer/quarterly_sprint_renamer_spec.rb +239 -0
- data/spec/jira/auto/tool/performer/sprint_end_date_updater_spec.rb +90 -0
- data/spec/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator_spec.rb +12 -0
- data/spec/jira/auto/tool/performer/sprint_renamer/next_name_generator_spec.rb +129 -0
- data/spec/jira/auto/tool/performer/sprint_renamer_spec.rb +240 -0
- data/spec/jira/auto/tool/performer/sprint_time_in_dates_aligner_spec.rb +132 -0
- data/spec/jira/auto/tool/project/ticket_fields_spec.rb +390 -0
- data/spec/jira/auto/tool/project_spec.rb +31 -0
- data/spec/jira/auto/tool/rate_limited_jira_client_spec.rb +82 -0
- data/spec/jira/auto/tool/request_builder/field_context_fetcher_spec.rb +54 -0
- data/spec/jira/auto/tool/request_builder/field_option_fetcher_spec.rb +64 -0
- data/spec/jira/auto/tool/request_builder/get_spec.rb +40 -0
- data/spec/jira/auto/tool/request_builder/sprint_creator_spec.rb +179 -0
- data/spec/jira/auto/tool/request_builder/sprint_state_updater_spec.rb +31 -0
- data/spec/jira/auto/tool/request_builder_spec.rb +73 -0
- data/spec/jira/auto/tool/sprint/name_spec.rb +101 -0
- data/spec/jira/auto/tool/sprint/prefix_spec.rb +207 -0
- data/spec/jira/auto/tool/sprint_controller_spec.rb +406 -0
- data/spec/jira/auto/tool/sprint_spec.rb +309 -0
- data/spec/jira/auto/tool/team_spec.rb +21 -0
- data/spec/jira/auto/tool/team_sprint_prefix_mapper_spec.rb +97 -0
- data/spec/jira/auto/tool/team_sprint_ticket_dispatcher_spec.rb +232 -0
- data/spec/jira/auto/tool/ticket_spec.rb +116 -0
- data/spec/jira/auto/tool/until_date_spec.rb +80 -0
- data/spec/jira/auto/tool_spec.rb +458 -0
- data/spec/spec_helper.rb +42 -0
- metadata +368 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/board_controller"
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
module Auto
|
7
|
+
class Tool
|
8
|
+
class BoardController
|
9
|
+
def self.filtered_boards
|
10
|
+
board_controller.send(:filtered_boards)
|
11
|
+
end
|
12
|
+
|
13
|
+
# rubocop:disable RSpec/MultipleMemoizedHelpers
|
14
|
+
RSpec.describe BoardController do
|
15
|
+
let(:jira_client) { instance_double(JIRA::Client) }
|
16
|
+
let(:jira_boards) do
|
17
|
+
board_info.collect do |name, project_key, url|
|
18
|
+
jira_resource_double(JIRA::Resource::Board, name:, project_key:, url:)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:expected_boards) do
|
23
|
+
board_info.collect do |name, project_key, url, with_project_information|
|
24
|
+
build_board(name, project_key, url, with_project_information: with_project_information)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:tool) { instance_double(Tool, jira_client: jira_client) }
|
29
|
+
let(:board_controller) { described_class.new(tool) }
|
30
|
+
|
31
|
+
let(:board_info) do
|
32
|
+
[
|
33
|
+
["Board 1", "ART", "https://jira.example.com/projects/ART/boards/1", true],
|
34
|
+
["Board 2", "ART", "https://jira.example.com/projects/ART/boards/2", true],
|
35
|
+
["Board 3", "TOOL", "https://jira.example.com/projects/TOOL/boards/3", true],
|
36
|
+
["Board 4", "N/A", "https://jira.example.com/projects/TOOL/boards/4", false]
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_board(name, project_key, ui_url, with_project_information: true)
|
41
|
+
instance_double(Board, name:, project_key:, ui_url:, with_project_information?: with_project_information,
|
42
|
+
inspect: name)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#list_boards" do
|
46
|
+
let(:expected_board_list) do
|
47
|
+
<<~EOBL
|
48
|
+
+-------------------------------------------------------------------------+
|
49
|
+
| Boards |
|
50
|
+
+-------------+---------+-------------------------------------------------+
|
51
|
+
| Project Key | Name | Board UI URL |
|
52
|
+
+-------------+---------+-------------------------------------------------+
|
53
|
+
| ART | Board 1 | https://jira.example.com/projects/ART/boards/1 |
|
54
|
+
| ART | Board 2 | https://jira.example.com/projects/ART/boards/2 |
|
55
|
+
| TOOL | Board 3 | https://jira.example.com/projects/TOOL/boards/3 |
|
56
|
+
| N/A | Board 4 | https://jira.example.com/projects/TOOL/boards/4 |
|
57
|
+
+-------------+---------+-------------------------------------------------+
|
58
|
+
EOBL
|
59
|
+
end
|
60
|
+
|
61
|
+
before do
|
62
|
+
allow(board_controller).to receive_messages(boards: expected_boards)
|
63
|
+
end
|
64
|
+
|
65
|
+
it { expect { board_controller.list_boards }.to output(expected_board_list).to_stdout }
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#boards" do
|
69
|
+
context "when the cache is invalid" do
|
70
|
+
let(:boards) { %i[a_board another_board] }
|
71
|
+
|
72
|
+
it "uses the API to fetch the boards and stores them in the cache" do
|
73
|
+
allow(board_controller)
|
74
|
+
.to receive_messages(filtered_boards: boards, valid_cache?: false)
|
75
|
+
|
76
|
+
allow(board_controller).to receive_messages(cache_boards: boards)
|
77
|
+
|
78
|
+
expect(board_controller.boards).to eq(boards)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when the cache is valid" do
|
83
|
+
it "uses the API to fetch the boards and stores them in the cache" do
|
84
|
+
allow(board_controller)
|
85
|
+
.to receive_messages(filtered_boards: %i[a_board another_board], valid_cache?: true)
|
86
|
+
|
87
|
+
allow(board_controller).to receive_messages(cached_boards: %i[a_board another_board])
|
88
|
+
|
89
|
+
expect(board_controller.boards).to eq(%i[a_board another_board])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#filtered_boards" do
|
95
|
+
let(:project_key) { nil }
|
96
|
+
let(:board_name_regex) { nil }
|
97
|
+
let(:filtered_boards) { board_controller.send(:filtered_boards) }
|
98
|
+
|
99
|
+
before do
|
100
|
+
allow(board_controller).to receive_messages(unfiltered_boards: expected_boards)
|
101
|
+
|
102
|
+
allow(tool).to receive_messages(
|
103
|
+
jira_board_name_regex: board_name_regex,
|
104
|
+
jira_board_name_regex_defined?: board_name_regex,
|
105
|
+
jira_project_key: project_key,
|
106
|
+
jira_project_key_defined?: project_key
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when using board name filtering" do
|
111
|
+
context "when a board name regex is specified" do
|
112
|
+
let(:board_name_regex) { "1|3" }
|
113
|
+
|
114
|
+
it { expect(filtered_boards).to eq(expected_boards.find_all { |b| b.name !~ /Board (2|4)/ }) }
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when no board name regex is specified" do
|
118
|
+
it { expect(filtered_boards).to eq(expected_boards) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "when using project key filtering" do
|
123
|
+
context "when a project key is specified" do
|
124
|
+
let(:project_key) { "ART" }
|
125
|
+
let(:board_expected_to_be_excluded) do
|
126
|
+
build_board("Board 3", "ART", "https://jira.example.com/projects/ART/boards/3")
|
127
|
+
end
|
128
|
+
|
129
|
+
it { expect(filtered_boards).to eq(expected_boards.find_all { |b| b.name != "Board 3" }) }
|
130
|
+
it { expect(filtered_boards).not_to include(board_expected_to_be_excluded) }
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when no project key is specified" do
|
134
|
+
it { expect(filtered_boards).to eq(expected_boards) }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#unfiltered_boards" do
|
140
|
+
let(:query) { jira_resource_double(all: jira_boards) }
|
141
|
+
|
142
|
+
before do
|
143
|
+
allow(jira_client).to receive_messages(Board: query)
|
144
|
+
end
|
145
|
+
|
146
|
+
it("returns boards") { expect(board_controller.send(:unfiltered_boards)).to all be_a(Board) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# rubocop:enable RSpec/MultipleMemoizedHelpers
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/board"
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
module Auto
|
7
|
+
class Tool
|
8
|
+
class Board
|
9
|
+
RSpec.describe Board do
|
10
|
+
let(:jira_client) { instance_double(JIRA::Client) }
|
11
|
+
let(:tool) { instance_double(Tool, jira_client: jira_client) }
|
12
|
+
|
13
|
+
let(:jira_board) do
|
14
|
+
jira_resource_double(JIRA::Resource::Board, name: "board name", url: "BOARD_URL", id: 4)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:board) { described_class.new(tool, jira_board) }
|
18
|
+
|
19
|
+
it { expect(board.name).to eq("board name") }
|
20
|
+
|
21
|
+
it { expect(board.url).to eq("BOARD_URL") }
|
22
|
+
|
23
|
+
it { expect(board.id).to eq(4) }
|
24
|
+
|
25
|
+
describe "#<=>" do
|
26
|
+
def new_board(id)
|
27
|
+
described_class.new(tool, instance_double(JIRA::Resource::Board, id: id))
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:board_with_same_id) { new_board(4) }
|
31
|
+
|
32
|
+
it { expect(board <=> board_with_same_id).to eq(0) }
|
33
|
+
it { expect(new_board(1) <=> board).to eq(-1) }
|
34
|
+
it { expect(new_board(5) <=> board).to eq(1) }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".find_by_id" do
|
38
|
+
before do
|
39
|
+
allow(JIRA::Resource::Board).to receive(:find).with(jira_client, 4).and_return(jira_board)
|
40
|
+
end
|
41
|
+
|
42
|
+
it { expect(described_class.find_by_id(tool, 4)).to eq(board) }
|
43
|
+
|
44
|
+
it "deals with non available boards" do
|
45
|
+
allow(JIRA::Resource::Board)
|
46
|
+
.to receive(:find).with(jira_client, 16)
|
47
|
+
.and_raise(JIRA::HTTPError.new(instance_double(Net::HTTPResponse, code: "404")))
|
48
|
+
|
49
|
+
expect(described_class.find_by_id(tool, 16)).to eq(Board::UnavailableBoard.new(tool, 16))
|
50
|
+
end
|
51
|
+
|
52
|
+
it "caches the boards" do
|
53
|
+
allow(JIRA::Resource::Board).to receive(:find).with(jira_client, 4).and_return(jira_board).once
|
54
|
+
|
55
|
+
board_on_1st_call = described_class.find_by_id(tool, 4)
|
56
|
+
|
57
|
+
expect(described_class.find_by_id(tool, 4)).to be(board_on_1st_call)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#unavailable?" do
|
62
|
+
it { expect(board).not_to be_unavailable }
|
63
|
+
it { expect(UnavailableBoard.new(tool, 512)).to be_unavailable }
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".to_table_row_field_names" do
|
67
|
+
it { expect(described_class.to_table_row_field_names).to eq(%i[name ui_url project_key]) }
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".to_table_row_header" do
|
71
|
+
it { expect(described_class.to_table_row_header).to eq(["Name", "UI URL", "Project Key"]) }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#to_table_row" do
|
75
|
+
before do
|
76
|
+
allow(board).to receive_messages(project_key: "PROJECT_KEY", ui_url: "UI_URL")
|
77
|
+
end
|
78
|
+
|
79
|
+
it { expect(board.to_table_row).to eq(["board name", "UI_URL", "PROJECT_KEY"]) }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#sprint_compatible?" do
|
83
|
+
context "when a scrum board" do
|
84
|
+
let(:jira_board) { jira_resource_double(JIRA::Resource::Board, type: "scrum") }
|
85
|
+
|
86
|
+
it { expect(board).to be_sprint_compatible }
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when sprints are not available" do
|
90
|
+
let(:jira_board) { jira_resource_double(JIRA::Resource::Board, type: "non scrum") }
|
91
|
+
|
92
|
+
it { expect(board).not_to be_sprint_compatible }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#ui_url" do
|
97
|
+
context "when location information is not available" do
|
98
|
+
before do
|
99
|
+
allow(board).to receive_messages(with_project_information?: false)
|
100
|
+
|
101
|
+
allow(tool).to receive(:jira_url)
|
102
|
+
.with("/secure/RapidBoard.jspa?rapidView=4")
|
103
|
+
.and_return("https://a-jira-site.example.com/jira/secure/RapidBoard.jspa?rapidView=4")
|
104
|
+
end
|
105
|
+
|
106
|
+
it do
|
107
|
+
expect(board.ui_url)
|
108
|
+
.to eq("https://a-jira-site.example.com/jira/secure/RapidBoard.jspa?rapidView=4")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "when location information is available" do
|
113
|
+
# TODO: - fix the URL
|
114
|
+
before do
|
115
|
+
allow(board).to receive_messages(with_project_information?: true, project_key: "PROJECT_KEY")
|
116
|
+
|
117
|
+
allow(tool)
|
118
|
+
.to receive(:jira_url)
|
119
|
+
.with("/jira/software/c/projects/PROJECT_KEY/boards/4")
|
120
|
+
.and_return("https://a-jira-site.example.com/jira/software/c/projects/PROJECT_KEY/boards/4")
|
121
|
+
end
|
122
|
+
|
123
|
+
it do
|
124
|
+
expect(board.ui_url)
|
125
|
+
.to eq("https://a-jira-site.example.com/jira/software/c/projects/PROJECT_KEY/boards/4")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "#project_key" do
|
131
|
+
context "when location information is available" do
|
132
|
+
before do
|
133
|
+
allow(jira_board)
|
134
|
+
.to receive_messages(with_project_information?: true,
|
135
|
+
location: { "projectKey" => "PROJECT_KEY" })
|
136
|
+
end
|
137
|
+
|
138
|
+
it { expect(board.project_key).to eq("PROJECT_KEY") }
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when location information is not available" do
|
142
|
+
it { expect(board.project_key).to eq("N/A") }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "#with_project_information?" do
|
147
|
+
context "when location information is available" do
|
148
|
+
before do
|
149
|
+
allow(jira_board).to receive_messages(location: :a_location)
|
150
|
+
end
|
151
|
+
|
152
|
+
it { expect(board).to be_with_project_information }
|
153
|
+
end
|
154
|
+
|
155
|
+
context "when location information is not available" do
|
156
|
+
it { expect(board).not_to be_with_project_information }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
require "jira/auto/tool/common_options"
|
5
|
+
|
6
|
+
module Jira
|
7
|
+
module Auto
|
8
|
+
class Tool
|
9
|
+
module CommonOptions
|
10
|
+
RSpec.describe CommonOptions do
|
11
|
+
describe ".add" do
|
12
|
+
let(:tool) { Tool.new }
|
13
|
+
let(:parser) { OptionParser.new }
|
14
|
+
|
15
|
+
before { described_class.add(tool, parser) }
|
16
|
+
|
17
|
+
context "when using --help" do
|
18
|
+
it do
|
19
|
+
expect(Kernel).to receive(:puts).with(parser)
|
20
|
+
expect(Kernel).to receive(:exit).with(1)
|
21
|
+
|
22
|
+
parser.parse(["--help"])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when using --version" do
|
27
|
+
it do
|
28
|
+
expect(Kernel).to receive(:puts).with(tool.class::VERSION)
|
29
|
+
expect(Kernel).to receive(:exit).with(1)
|
30
|
+
|
31
|
+
parser.parse(["--version"])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: define proper tests
|
36
|
+
def expect_option_use_to_be_valid(option_use_with_args)
|
37
|
+
expect do
|
38
|
+
parser.parse([option_use_with_args])
|
39
|
+
end.not_to raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it { expect_option_use_to_be_valid(["--help"]) }
|
43
|
+
it { expect_option_use_to_be_valid(["-h"]) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
require "jira/auto/tool/config"
|
5
|
+
|
6
|
+
module Jira
|
7
|
+
module Auto
|
8
|
+
class Tool
|
9
|
+
class Config
|
10
|
+
RSpec.describe Config do
|
11
|
+
let(:config) { described_class.new(tool) }
|
12
|
+
let(:tool) { instance_double(Tool) }
|
13
|
+
|
14
|
+
describe "#[]" do
|
15
|
+
it "returns the value of the key" do
|
16
|
+
allow(config).to receive_messages(value_store: { "key" => "value" })
|
17
|
+
|
18
|
+
expect(config[:key]).to eq("value")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#[]=" do
|
23
|
+
before do
|
24
|
+
allow(config).to receive_messages(value_store: { "one_key" => "a_value" }, save: nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "updates the value and saves the configuration" do
|
28
|
+
config[:key] = "value"
|
29
|
+
|
30
|
+
expect(config[:key]).to eq("value")
|
31
|
+
expect(config).to have_received(:save)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#key?" do
|
36
|
+
before do
|
37
|
+
allow(config).to receive_messages(value_store: { "one_key" => "a_value" }, save: nil)
|
38
|
+
end
|
39
|
+
|
40
|
+
it { expect(config).to be_key(:one_key) }
|
41
|
+
it { expect(config).not_to be_key(:inexistent_key) }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#load" do
|
45
|
+
let(:config_path) { "config-path" }
|
46
|
+
|
47
|
+
before do
|
48
|
+
allow(config).to receive_messages(path: config_path)
|
49
|
+
allow(File).to receive(:exist?).with(config_path).and_return(config_path_exists?)
|
50
|
+
allow(YAML).to receive(:safe_load_file).with(config_path).and_return(:config_values)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when the file exists" do
|
54
|
+
let(:config_path_exists?) { true }
|
55
|
+
|
56
|
+
it "loads the configuration from the file" do
|
57
|
+
config.send(:load)
|
58
|
+
|
59
|
+
expect(config).to eq(:config_values)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when the file does not exists" do
|
64
|
+
let(:config_path_exists?) { false }
|
65
|
+
|
66
|
+
it { expect(config).to eq({}) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#save" do
|
71
|
+
it "writes the configuration as yaml to the file" do
|
72
|
+
allow(config).to receive_messages(path: "config.yml", value_store: { key: "value" })
|
73
|
+
allow(File).to receive_messages(write: nil)
|
74
|
+
|
75
|
+
config.send(:save)
|
76
|
+
|
77
|
+
expect(File).to have_received(:write).with("config.yml", "---\n:key: value\n")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#path" do
|
82
|
+
before do
|
83
|
+
allow(config).to receive_messages(dir: "path/to/config_dir")
|
84
|
+
end
|
85
|
+
|
86
|
+
it { expect(config.send(:path)).to eq("path/to/config_dir/jira-auto-tool.config.yml") }
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#dir" do
|
90
|
+
let(:expected_dir) { "~/.config/jira-auto-tool" }
|
91
|
+
|
92
|
+
before do
|
93
|
+
allow(Dir).to receive_messages(home: "~/")
|
94
|
+
end
|
95
|
+
|
96
|
+
it { expect(config.send(:dir)).to eq(expected_dir) }
|
97
|
+
|
98
|
+
it "creates the directory if it does not exist" do
|
99
|
+
expect(FileUtils).to receive(:makedirs).with(expected_dir)
|
100
|
+
|
101
|
+
config.send(:dir)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
require "jira/auto/tool/field_controller"
|
6
|
+
|
7
|
+
module Jira
|
8
|
+
module Auto
|
9
|
+
class Tool
|
10
|
+
class FieldController
|
11
|
+
RSpec.describe FieldController do
|
12
|
+
let(:jira_client) { instance_double(JIRA::Client) }
|
13
|
+
let(:controller) { described_class.new(jira_client) }
|
14
|
+
|
15
|
+
def build_field(name = "a field name #{rand}", type = "unexpected_type")
|
16
|
+
instance_double(Field, name: name, type: type)
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.shared_examples "a field fetcher" do
|
20
|
+
|method_under_test, expected_field_name, expected_field_type, actual_type = expected_field_type|
|
21
|
+
|
22
|
+
let(:expected_field) { build_field(expected_field_name, actual_type) }
|
23
|
+
let(:field_with_incorrect_type_name) { "non_#{expected_field_type}_field" }
|
24
|
+
|
25
|
+
before do
|
26
|
+
allow(controller)
|
27
|
+
.to receive(:ticket_fields)
|
28
|
+
.and_return([build_field, expected_field, build_field(field_with_incorrect_type_name), build_field])
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when the field is found" do
|
32
|
+
let(:actual_field) { controller.send(method_under_test, expected_field_name) }
|
33
|
+
|
34
|
+
it "returns the expected field" do
|
35
|
+
expect(actual_field).to eq(expected_field)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "has the expected type" do
|
39
|
+
expect(actual_field.type).to match_regex(expected_field_type)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "report an unexpected field type" do
|
44
|
+
expect { controller.send(method_under_test, field_with_incorrect_type_name) }
|
45
|
+
.to raise_error(
|
46
|
+
ExpectedFieldTypeError,
|
47
|
+
/Field\ '#{field_with_incorrect_type_name}'\ expected\ to\ have\ type\ '#{expected_field_type}',
|
48
|
+
\ but\ was \ 'unexpected_type'./x
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "raises and error for a non existing field" do
|
53
|
+
expect { controller.send(method_under_test, "non_existing_field") }
|
54
|
+
.to raise_error(FieldNotFoundError, /Field 'non_existing_field' not found!/)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#expected_start_date_field" do
|
59
|
+
it_behaves_like "a field fetcher", :expected_start_date_field, "Custom Start Date", "date"
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#implementation_team_field" do
|
63
|
+
context "when any field" do
|
64
|
+
it_behaves_like "a field fetcher", :implementation_team_field, "Custom Team", "option|any", "any"
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when option field" do
|
68
|
+
it_behaves_like "a field fetcher", :implementation_team_field, "Custom Team", "option|any", "option"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#sprint_field" do
|
73
|
+
it_behaves_like "a field fetcher", :sprint_field, "Custom Sprint", "array"
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#ticket_fields" do
|
77
|
+
let(:board_id) { 123 }
|
78
|
+
let(:jira_fields) do
|
79
|
+
[
|
80
|
+
%w[field1 date customfield_12345],
|
81
|
+
%w[field2 option customfield_12346]
|
82
|
+
].collect { |attributes| build_jira_field(*attributes) }
|
83
|
+
end
|
84
|
+
let(:expected_fields) { jira_fields.collect { |f| Field.new(jira_client, f) } }
|
85
|
+
|
86
|
+
def build_jira_field(name, type, id)
|
87
|
+
jira_resource_double(JIRA::Resource::Field, name:, schema: { "type" => type }, id:)
|
88
|
+
end
|
89
|
+
|
90
|
+
before do
|
91
|
+
# rubocop:disable RSpec/MessageChain
|
92
|
+
allow(jira_client).to receive_message_chain(:Field, :all).and_return(jira_fields)
|
93
|
+
# rubocop:enable RSpec/MessageChain
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when fields are successfully fetched" do
|
97
|
+
it "returns fields associated with the board" do
|
98
|
+
expect(controller.ticket_fields).to eq(expected_fields)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "when an error occurs" do
|
103
|
+
before do
|
104
|
+
# rubocop:disable RSpec/MessageChain
|
105
|
+
allow(jira_client).to receive_message_chain(:Field, :all).and_raise(StandardError,
|
106
|
+
"Something went wrong")
|
107
|
+
# rubocop:enable RSpec/MessageChain
|
108
|
+
end
|
109
|
+
|
110
|
+
it "raises an error with a descriptive message" do
|
111
|
+
expect do
|
112
|
+
controller.ticket_fields
|
113
|
+
end.to raise_error(RuntimeError, /Error fetching project ticket fields: Something went wrong/)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/field_option"
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
module Auto
|
7
|
+
class Tool
|
8
|
+
class FieldOption
|
9
|
+
RSpec.describe FieldOption do
|
10
|
+
let(:jira_client) { instance_double(JIRA::Client) }
|
11
|
+
let(:field_option) { described_class.new(jira_client, 123, "Test Option") }
|
12
|
+
|
13
|
+
describe "#initialize" do
|
14
|
+
it "assigns the id correctly" do
|
15
|
+
expect(field_option.id).to eq(123)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "assigns the value correctly" do
|
19
|
+
expect(field_option.value).to eq("Test Option")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#to_s" do
|
24
|
+
it "returns the correct string representation" do
|
25
|
+
expect(field_option.to_s).to eq("FieldOption(id: 123, value: 'Test Option')")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#<=>" do
|
30
|
+
let(:higher_field_option) { described_class.new(jira_client, 124, "Another Option") }
|
31
|
+
let(:lower_field_option) { described_class.new(jira_client, 122, "Previous Option") }
|
32
|
+
let(:equal_field_option) { described_class.new(jira_client, 123, "Test Option") }
|
33
|
+
|
34
|
+
it { expect(field_option).to be < higher_field_option }
|
35
|
+
it { expect(field_option).to be > lower_field_option }
|
36
|
+
it { expect(field_option).to eq(equal_field_option) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|