px_github_changelog_generator 0.0.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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +9 -0
  3. data/README.md +357 -0
  4. data/Rakefile +19 -0
  5. data/bin/git-generate-changelog +5 -0
  6. data/bin/github_changelog_generator +5 -0
  7. data/lib/github_changelog_generator/argv_parser.rb +225 -0
  8. data/lib/github_changelog_generator/file_parser_chooser.rb +27 -0
  9. data/lib/github_changelog_generator/generator/entry.rb +218 -0
  10. data/lib/github_changelog_generator/generator/generator.rb +177 -0
  11. data/lib/github_changelog_generator/generator/generator_fetcher.rb +202 -0
  12. data/lib/github_changelog_generator/generator/generator_processor.rb +237 -0
  13. data/lib/github_changelog_generator/generator/generator_tags.rb +210 -0
  14. data/lib/github_changelog_generator/generator/section.rb +129 -0
  15. data/lib/github_changelog_generator/helper.rb +37 -0
  16. data/lib/github_changelog_generator/octo_fetcher.rb +535 -0
  17. data/lib/github_changelog_generator/options.rb +159 -0
  18. data/lib/github_changelog_generator/parser.rb +89 -0
  19. data/lib/github_changelog_generator/parser_file.rb +101 -0
  20. data/lib/github_changelog_generator/reader.rb +88 -0
  21. data/lib/github_changelog_generator/ssl_certs/cacert.pem +3138 -0
  22. data/lib/github_changelog_generator/task.rb +68 -0
  23. data/lib/github_changelog_generator/version.rb +5 -0
  24. data/lib/github_changelog_generator.rb +49 -0
  25. data/man/git-generate-changelog.1 +393 -0
  26. data/man/git-generate-changelog.1.html +359 -0
  27. data/man/git-generate-changelog.html +270 -0
  28. data/man/git-generate-changelog.md +274 -0
  29. data/spec/files/angular.js.md +9395 -0
  30. data/spec/files/bundler.md +1911 -0
  31. data/spec/files/config_example +5 -0
  32. data/spec/files/github-changelog-generator.md +305 -0
  33. data/spec/github_changelog_generator_spec.rb +32 -0
  34. data/spec/install_gem_in_bundler.gemfile +5 -0
  35. data/spec/spec_helper.rb +74 -0
  36. data/spec/unit/generator/entry_spec.rb +766 -0
  37. data/spec/unit/generator/generator_processor_spec.rb +203 -0
  38. data/spec/unit/generator/generator_spec.rb +47 -0
  39. data/spec/unit/generator/generator_tags_spec.rb +337 -0
  40. data/spec/unit/generator/section_spec.rb +43 -0
  41. data/spec/unit/octo_fetcher_spec.rb +590 -0
  42. data/spec/unit/options_spec.rb +67 -0
  43. data/spec/unit/parser_file_spec.rb +94 -0
  44. data/spec/unit/parser_spec.rb +54 -0
  45. data/spec/unit/reader_spec.rb +120 -0
  46. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_commits/when_API_is_valid/returns_commits.json +1 -0
  47. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_commits_before/when_API_is_valid/returns_commits.json +1 -0
  48. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issue_with_proper_key/values.json +1 -0
  49. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issues.json +1 -0
  50. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issues_with_labels.json +1 -0
  51. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_pull_request_with_proper_key/values.json +1 -0
  52. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_pull_requests_with_labels.json +1 -0
  53. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid.json +1 -0
  54. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid/returns_correct_pull_request_keys.json +1 -0
  55. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid/returns_pull_requests.json +1 -0
  56. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid.json +1 -0
  57. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_commit/when_API_call_is_valid/returns_commit.json +1 -0
  58. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_commit/when_API_call_is_valid.json +1 -0
  59. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_date_of_tag/when_API_call_is_valid/returns_date.json +1 -0
  60. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_date_of_tag/when_API_call_is_valid.json +1 -0
  61. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_events_async/when_API_call_is_valid/populates_issues.json +1 -0
  62. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_events_async/when_API_call_is_valid.json +1 -0
  63. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid/should_return_tags.json +1 -0
  64. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid/should_return_tags_count.json +1 -0
  65. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid.json +1 -0
  66. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_wrong_token_provided/should_raise_Unauthorized_error.json +1 -0
  67. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_wrong_token_provided.json +1 -0
  68. metadata +250 -0
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHubChangelogGenerator
4
+ describe Generator do
5
+ let(:default_options) { GitHubChangelogGenerator::Parser.default_options.merge(verbose: false) }
6
+ let(:options) { {} }
7
+ let(:generator) { described_class.new(default_options.merge(options)) }
8
+ let(:bad_label) { { "name" => "BAD" } }
9
+ let(:good_label) { { "name" => "GOOD" } }
10
+
11
+ describe "pull requests" do
12
+ let(:bad_pull_request) { { "pull_request" => {}, "labels" => [bad_label] } }
13
+ let(:good_pull_request) { { "pull_request" => {}, "labels" => [good_label] } }
14
+ let(:unlabeled_pull_request) { { "pull_request" => {}, "labels" => [] } }
15
+ let(:pull_requests) { [bad_pull_request, good_pull_request, unlabeled_pull_request] }
16
+
17
+ describe "#filter_wo_labels" do
18
+ subject do
19
+ generator.filter_wo_labels(pull_requests)
20
+ end
21
+
22
+ let(:expected_pull_requests) { pull_requests }
23
+
24
+ it { is_expected.to eq(expected_pull_requests) }
25
+
26
+ context "when 'add_pr_wo_labels' is false" do
27
+ let(:options) { { add_pr_wo_labels: false } }
28
+ let(:expected_pull_requests) { [bad_pull_request, good_pull_request] }
29
+
30
+ it { is_expected.to eq(expected_pull_requests) }
31
+ end
32
+
33
+ context "when 'add_pr_wo_labels' is true" do
34
+ let(:options) { { add_pr_wo_labels: true } }
35
+
36
+ it { is_expected.to eq(expected_pull_requests) }
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "issues" do
42
+ let(:bad_issue) { { "labels" => [bad_label] } }
43
+ let(:good_issue) { { "labels" => [good_label] } }
44
+ let(:unlabeled_issue) { { "labels" => [] } }
45
+ let(:issues) { [bad_issue, good_issue, unlabeled_issue] }
46
+
47
+ describe "#filter_wo_labels" do
48
+ subject do
49
+ generator.filter_wo_labels(issues)
50
+ end
51
+
52
+ let(:expected_issues) { issues }
53
+
54
+ it { is_expected.to eq(expected_issues) }
55
+
56
+ context "when 'add_issues_wo_labels' is false" do
57
+ let(:options) { { add_issues_wo_labels: false } }
58
+ let(:expected_issues) { [bad_issue, good_issue] }
59
+
60
+ it { is_expected.to eq(expected_issues) }
61
+ end
62
+
63
+ context "when 'add_issues_wo_labels' is true" do
64
+ let(:options) { { add_issues_wo_labels: true } }
65
+
66
+ it { is_expected.to eq(expected_issues) }
67
+ end
68
+ end
69
+
70
+ describe "#exclude_issues_by_labels" do
71
+ subject do
72
+ generator.exclude_issues_by_labels(issues)
73
+ end
74
+
75
+ let(:expected_issues) { issues }
76
+
77
+ it { is_expected.to eq(expected_issues) }
78
+
79
+ context "when 'exclude_labels' is provided" do
80
+ let(:options) { { exclude_labels: %w[BAD BOO] } }
81
+ let(:expected_issues) { [good_issue, unlabeled_issue] }
82
+
83
+ it { is_expected.to eq(expected_issues) }
84
+ end
85
+
86
+ context "with no option given" do
87
+ subject(:generator) { described_class.new }
88
+ it "passes everything through when no option given" do
89
+ result = generator.exclude_issues_by_labels(issues)
90
+
91
+ expect(result).to eq(issues)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "#get_filtered_issues" do
97
+ subject do
98
+ generator.get_filtered_issues(issues)
99
+ end
100
+
101
+ let(:expected_issues) { issues }
102
+
103
+ it { is_expected.to eq(expected_issues) }
104
+
105
+ context "when 'exclude_labels' is provided" do
106
+ let(:options) { { exclude_labels: %w[BAD BOO] } }
107
+ let(:expected_issues) { [good_issue, unlabeled_issue] }
108
+
109
+ it { is_expected.to eq(expected_issues) }
110
+ end
111
+
112
+ context "when 'add_issues_wo_labels' is false" do
113
+ let(:options) { { add_issues_wo_labels: false } }
114
+ let(:expected_issues) { [bad_issue, good_issue] }
115
+
116
+ it { is_expected.to eq(expected_issues) }
117
+
118
+ context "with 'exclude_labels'" do
119
+ let(:options) { { add_issues_wo_labels: false, exclude_labels: %w[GOOD] } }
120
+ let(:expected_issues) { [bad_issue] }
121
+
122
+ it { is_expected.to eq(expected_issues) }
123
+ end
124
+
125
+ context "with 'include_labels'" do
126
+ let(:options) { { add_issues_wo_labels: false, include_labels: %w[GOOD] } }
127
+ let(:expected_issues) { [good_issue] }
128
+
129
+ it { is_expected.to eq(expected_issues) }
130
+ end
131
+ end
132
+
133
+ context "when 'include_labels' is specified" do
134
+ let(:options) { { include_labels: %w[GOOD] } }
135
+ let(:expected_issues) { [good_issue, unlabeled_issue] }
136
+
137
+ it { is_expected.to eq(expected_issues) }
138
+ end
139
+ end
140
+
141
+ describe "#find_issues_to_add" do
142
+ let(:issues) { [issue] }
143
+ let(:tag_name) { nil }
144
+ let(:filtered_tags) { [] }
145
+ before { generator.instance_variable_set(:@filtered_tags, filtered_tags) }
146
+
147
+ subject { generator.find_issues_to_add(issues, tag_name) }
148
+
149
+ context "issue without milestone" do
150
+ let(:issue) { { "labels" => [] } }
151
+
152
+ it { is_expected.to be_empty }
153
+ end
154
+
155
+ context "milestone title not in the filtered tags" do
156
+ let(:issue) { { "milestone" => { "title" => "a title" } } }
157
+ let(:filtered_tags) { [{ "name" => "another name" }] }
158
+
159
+ it { is_expected.to be_empty }
160
+ end
161
+
162
+ context "milestone title matches the tag name" do
163
+ let(:tag_name) { "tag name" }
164
+ let(:issue) { { "milestone" => { "title" => tag_name } } }
165
+ let(:filtered_tags) { [{ "name" => tag_name }] }
166
+
167
+ it { is_expected.to contain_exactly(issue) }
168
+ end
169
+ end
170
+
171
+ describe "#remove_issues_in_milestones" do
172
+ let(:issues) { [issue] }
173
+
174
+ context "issue without milestone" do
175
+ let(:issue) { { "labels" => [] } }
176
+
177
+ it "is not filtered" do
178
+ expect { generator.remove_issues_in_milestones(issues) }.to_not(change { issues })
179
+ end
180
+ end
181
+
182
+ context "remove issues of open milestones if option is set" do
183
+ let(:issue) { { "milestone" => { "state" => "open" } } }
184
+ let(:options) { { issues_of_open_milestones: false } }
185
+
186
+ it "is filtered" do
187
+ expect { generator.remove_issues_in_milestones(issues) }.to change { issues }.to([])
188
+ end
189
+ end
190
+
191
+ context "milestone in the tag list" do
192
+ let(:milestone_name) { "milestone name" }
193
+ let(:issue) { { "milestone" => { "title" => milestone_name } } }
194
+
195
+ it "is filtered" do
196
+ generator.instance_variable_set(:@filtered_tags, [{ "name" => milestone_name }])
197
+ expect { generator.remove_issues_in_milestones(issues) }.to change { issues }.to([])
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "github_changelog_generator/generator/generator"
4
+
5
+ RSpec.describe GitHubChangelogGenerator::Generator do
6
+ let(:header) { "# Changelog" }
7
+ let(:generator) { described_class.new({ header: header }) }
8
+ let(:content) do
9
+ <<~'BASE'
10
+ ## [1.3.10](https://github.com/xxx/yyy/tree/1.3.10) (2015-03-18)
11
+
12
+ [Full Changelog](https://github.com/xxx/yyy/compare/1.3.9...1.3.10)
13
+
14
+ **Fixed bugs:**
15
+
16
+
17
+ BASE
18
+ end
19
+ let(:footer) do
20
+ <<~CREDIT
21
+ \\* *This Changelog was automatically generated \
22
+ by [github_changelog_generator]\
23
+ (https://github.com/github-changelog-generator/github-changelog-generator)*
24
+ CREDIT
25
+ end
26
+
27
+ context "when the given base file has previously appended template messages" do
28
+ describe "#remove_old_fixed_string" do
29
+ it "removes old template headers and footers" do
30
+ log = +"#{header}\n\n#{header}\n#{header}#{content}\n\n#{footer}\n#{footer}#{footer}"
31
+
32
+ expect(generator.send(:remove_old_fixed_string, log)).to eq content
33
+ end
34
+ end
35
+ end
36
+
37
+ context "when plain contents string was given" do
38
+ describe "#insert_fixed_string" do
39
+ it "append template messages at header and footer" do
40
+ log = String.new(content)
41
+ ans = "#{header}\n\n#{content}\n\n#{footer}"
42
+
43
+ expect(generator.send(:insert_fixed_string, log)).to eq ans
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GitHubChangelogGenerator::Generator do
4
+ def tag_with_name(tag)
5
+ {
6
+ "name" => tag
7
+ }
8
+ end
9
+
10
+ def tags_from_strings(tags_strings)
11
+ tags_strings.map do |tag|
12
+ tag_with_name(tag)
13
+ end
14
+ end
15
+
16
+ describe "#detect_link_tag_time" do
17
+ let(:newer_tag) { nil }
18
+
19
+ let(:default_options) { GitHubChangelogGenerator::Parser.default_options.merge(verbose: false) }
20
+ let(:options) do
21
+ {
22
+ future_release: "2.0.0"
23
+ }
24
+ end
25
+ let(:generator) { described_class.new(default_options.merge(options)) }
26
+
27
+ subject do
28
+ generator.detect_link_tag_time(newer_tag)
29
+ end
30
+
31
+ context "When the local date is not the same as the UTC date" do
32
+ before do
33
+ # 2020-12-27T17:00:00-10:00 is 2020-12-28T03:00:00Z.
34
+ # GitHub API date & time use UTC, so this instant when converted as a
35
+ # date should be 2020-12-28.
36
+ expect(Time).to receive(:new).and_return(Time.new(2020, 12, 27, 17, 0, 0, "-10:00"))
37
+ end
38
+
39
+ it { is_expected.to eq(["2.0.0", "2.0.0", Time.gm(2020, 12, 28, 3)]) }
40
+ end
41
+ end
42
+
43
+ describe "#tag_section_mapping" do
44
+ let(:all_tags) { tags_from_strings(%w[8 7 6 5 4 3 2 1]) }
45
+ let(:sorted_tags) { all_tags }
46
+
47
+ let(:default_options) { GitHubChangelogGenerator::Parser.default_options.merge(verbose: false) }
48
+ let(:options) { {} }
49
+ let(:generator) { described_class.new(default_options.merge(options)) }
50
+
51
+ before do
52
+ allow_any_instance_of(GitHubChangelogGenerator::OctoFetcher).to receive(:fetch_all_tags).and_return(all_tags)
53
+ allow(generator).to receive(:fetch_tags_dates).with(all_tags)
54
+ allow(generator).to receive(:sort_tags_by_date).with(all_tags).and_return(sorted_tags)
55
+ generator.fetch_and_filter_tags
56
+ end
57
+
58
+ subject do
59
+ generator.tag_section_mapping
60
+ end
61
+
62
+ shared_examples_for "a section mapping" do
63
+ it { is_expected.to be_a(Hash) }
64
+ it { is_expected.to eq(expected_mapping) }
65
+ end
66
+
67
+ shared_examples_for "a full changelog" do
68
+ let(:expected_mapping) do
69
+ {
70
+ tag_with_name("8") => [tag_with_name("7"), tag_with_name("8")],
71
+ tag_with_name("7") => [tag_with_name("6"), tag_with_name("7")],
72
+ tag_with_name("6") => [tag_with_name("5"), tag_with_name("6")],
73
+ tag_with_name("5") => [tag_with_name("4"), tag_with_name("5")],
74
+ tag_with_name("4") => [tag_with_name("3"), tag_with_name("4")],
75
+ tag_with_name("3") => [tag_with_name("2"), tag_with_name("3")],
76
+ tag_with_name("2") => [tag_with_name("1"), tag_with_name("2")],
77
+ tag_with_name("1") => [nil, tag_with_name("1")]
78
+ }
79
+ end
80
+
81
+ it_behaves_like "a section mapping"
82
+ end
83
+
84
+ shared_examples_for "a changelog with some exclusions" do
85
+ let(:expected_mapping) do
86
+ {
87
+ tag_with_name("8") => [tag_with_name("6"), tag_with_name("8")],
88
+ tag_with_name("6") => [tag_with_name("4"), tag_with_name("6")],
89
+ tag_with_name("4") => [tag_with_name("3"), tag_with_name("4")],
90
+ tag_with_name("3") => [tag_with_name("1"), tag_with_name("3")],
91
+ tag_with_name("1") => [nil, tag_with_name("1")]
92
+ }
93
+ end
94
+
95
+ it_behaves_like "a section mapping"
96
+ end
97
+
98
+ context "with no constraints" do
99
+ it_behaves_like "a full changelog"
100
+ end
101
+
102
+ context "with since only" do
103
+ let(:options) { { since_tag: "6" } }
104
+ let(:expected_mapping) do
105
+ {
106
+ tag_with_name("8") => [tag_with_name("7"), tag_with_name("8")],
107
+ tag_with_name("7") => [tag_with_name("6"), tag_with_name("7")]
108
+ }
109
+ end
110
+
111
+ it_behaves_like "a section mapping"
112
+ end
113
+
114
+ context "with due only" do
115
+ let(:options) { { due_tag: "4" } }
116
+ let(:expected_mapping) do
117
+ {
118
+ tag_with_name("3") => [tag_with_name("2"), tag_with_name("3")],
119
+ tag_with_name("2") => [tag_with_name("1"), tag_with_name("2")],
120
+ tag_with_name("1") => [nil, tag_with_name("1")]
121
+ }
122
+ end
123
+
124
+ it_behaves_like "a section mapping"
125
+ end
126
+
127
+ context "with since and due" do
128
+ let(:options) { { since_tag: "2", due_tag: "5" } }
129
+ let(:expected_mapping) do
130
+ {
131
+ tag_with_name("4") => [tag_with_name("3"), tag_with_name("4")],
132
+ tag_with_name("3") => [tag_with_name("2"), tag_with_name("3")]
133
+ }
134
+ end
135
+
136
+ it_behaves_like "a section mapping"
137
+ end
138
+
139
+ context "with excluded tags" do
140
+ context "as a list of strings" do
141
+ let(:options) { { exclude_tags: %w[2 5 7] } }
142
+
143
+ it_behaves_like "a changelog with some exclusions"
144
+ end
145
+
146
+ context "as a regex" do
147
+ let(:options) { { exclude_tags: /[257]/ } }
148
+
149
+ it_behaves_like "a changelog with some exclusions"
150
+ end
151
+
152
+ context "as a regex string" do
153
+ let(:options) { { exclude_tags_regex: "[257]" } }
154
+
155
+ it_behaves_like "a changelog with some exclusions"
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "#filter_included_tags_regex" do
161
+ subject { generator.filter_included_tags(tags_from_strings(%w[1 2 3])) }
162
+
163
+ context "with matching regex" do
164
+ let(:generator) { GitHubChangelogGenerator::Generator.new(include_tags_regex: "[23]") }
165
+ it { is_expected.to be_a Array }
166
+ it { is_expected.to match_array(tags_from_strings(%w[2 3])) }
167
+ end
168
+
169
+ context "with non-matching regex" do
170
+ let(:generator) { GitHubChangelogGenerator::Generator.new(include_tags_regex: "[45]") }
171
+ it { is_expected.to be_a Array }
172
+ it { is_expected.to match_array(tags_from_strings(%w[])) }
173
+ end
174
+ end
175
+
176
+ describe "#filter_excluded_tags" do
177
+ subject { generator.filter_excluded_tags(tags_from_strings(%w[1 2 3])) }
178
+
179
+ context "with matching string" do
180
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w[3]) }
181
+ it { is_expected.to be_a Array }
182
+ it { is_expected.to match_array(tags_from_strings(%w[1 2])) }
183
+ end
184
+
185
+ context "with non-matching string" do
186
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w[invalid tags]) }
187
+ it { is_expected.to be_a Array }
188
+ it { is_expected.to match_array(tags_from_strings(%w[1 2 3])) }
189
+ end
190
+
191
+ context "with matching regex" do
192
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[23]/) }
193
+ it { is_expected.to be_a Array }
194
+ it { is_expected.to match_array(tags_from_strings(%w[1])) }
195
+ end
196
+
197
+ context "with non-matching regex" do
198
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[abc]/) }
199
+ it { is_expected.to be_a Array }
200
+ it { is_expected.to match_array(tags_from_strings(%w[1 2 3])) }
201
+ end
202
+ end
203
+
204
+ describe "#filter_excluded_tags_regex" do
205
+ subject { generator.filter_excluded_tags(tags_from_strings(%w[1 2 3])) }
206
+
207
+ context "with matching regex" do
208
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[23]") }
209
+ it { is_expected.to be_a Array }
210
+ it { is_expected.to match_array(tags_from_strings(%w[1])) }
211
+ end
212
+
213
+ context "with non-matching regex" do
214
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[45]") }
215
+ it { is_expected.to be_a Array }
216
+ it { is_expected.to match_array(tags_from_strings(%w[1 2 3])) }
217
+ end
218
+ end
219
+
220
+ describe "#filter_since_tag" do
221
+ context "with filled array" do
222
+ subject { generator.filter_since_tag(tags_from_strings(%w[1 2 3])) }
223
+
224
+ context "with valid since tag" do
225
+ let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
226
+ it { is_expected.to be_a Array }
227
+ it { is_expected.to match_array(tags_from_strings(%w[1 2])) }
228
+
229
+ context "with since tag set to the most recent tag" do
230
+ let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "1") }
231
+ it { is_expected.to match_array(tags_from_strings(%w[1])) }
232
+ end
233
+ end
234
+
235
+ context "with invalid since tag" do
236
+ let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
237
+ it { expect { subject }.to raise_error(GitHubChangelogGenerator::ChangelogGeneratorError) }
238
+ end
239
+ end
240
+
241
+ context "with empty array" do
242
+ subject { generator.filter_since_tag(tags_from_strings(%w[])) }
243
+
244
+ context "with invalid since tag" do
245
+ let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
246
+ it { expect { subject }.to raise_error(GitHubChangelogGenerator::ChangelogGeneratorError) }
247
+ end
248
+ end
249
+ end
250
+
251
+ describe "#filter_due_tag" do
252
+ context "with filled array" do
253
+ subject { generator.filter_due_tag(tags_from_strings(%w[1 2 3])) }
254
+
255
+ context "with valid due tag" do
256
+ let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
257
+ it { is_expected.to be_a Array }
258
+ it { is_expected.to match_array(tags_from_strings(%w[3])) }
259
+ end
260
+
261
+ context "with invalid due tag" do
262
+ let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
263
+ it { expect { subject }.to raise_error(GitHubChangelogGenerator::ChangelogGeneratorError) }
264
+ end
265
+ end
266
+
267
+ context "with empty array" do
268
+ subject { generator.filter_due_tag(tags_from_strings(%w[])) }
269
+
270
+ context "with invalid due tag" do
271
+ let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
272
+ it { expect { subject }.to raise_error(GitHubChangelogGenerator::ChangelogGeneratorError) }
273
+ end
274
+ end
275
+ end
276
+
277
+ describe "#get_time_of_tag" do
278
+ current_time = Time.now
279
+ before(:all) { @generator = GitHubChangelogGenerator::Generator.new }
280
+ context "run with nil parameter" do
281
+ it "should raise ChangelogGeneratorError" do
282
+ expect { @generator.get_time_of_tag nil }.to raise_error(GitHubChangelogGenerator::ChangelogGeneratorError)
283
+ end
284
+ end
285
+ context "fetch already filled tag" do
286
+ before { @generator.instance_variable_set :@tag_times_hash, "valid_tag" => current_time }
287
+ subject { @generator.get_time_of_tag tag_with_name("valid_tag") }
288
+ it { is_expected.to be_a_kind_of(Time) }
289
+ it { is_expected.to eq(current_time) }
290
+ end
291
+ context "fetch not filled tag" do
292
+ before do
293
+ mock = double("fake fetcher")
294
+ allow(mock).to receive_messages(fetch_date_of_tag: current_time)
295
+ @generator.instance_variable_set :@fetcher, mock
296
+ end
297
+ subject do
298
+ of_tag = @generator.get_time_of_tag(tag_with_name("valid_tag"))
299
+ of_tag
300
+ end
301
+ it { is_expected.to be_a_kind_of(Time) }
302
+ it { is_expected.to eq(current_time) }
303
+ end
304
+ end
305
+
306
+ describe "#sort_tags_by_date" do
307
+ let(:time1) { Time.now }
308
+ let(:time2) { Time.now }
309
+ let(:time3) { Time.now }
310
+
311
+ before(:all) do
312
+ @generator = GitHubChangelogGenerator::Generator.new
313
+ end
314
+
315
+ before do
316
+ @generator.instance_variable_set(:@tag_times_hash, "valid_tag1" => time1,
317
+ "valid_tag2" => time2,
318
+ "valid_tag3" => time3)
319
+ end
320
+
321
+ subject do
322
+ @generator.sort_tags_by_date(tags)
323
+ end
324
+ context "sort unsorted tags" do
325
+ let(:tags) { tags_from_strings %w[valid_tag1 valid_tag2 valid_tag3] }
326
+
327
+ it { is_expected.to be_a_kind_of(Array) }
328
+ it { is_expected.to match_array(tags.reverse!) }
329
+ end
330
+ context "sort sorted tags" do
331
+ let(:tags) { tags_from_strings %w[valid_tag3 valid_tag2 valid_tag1] }
332
+
333
+ it { is_expected.to be_a_kind_of(Array) }
334
+ it { is_expected.to match_array(tags) }
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHubChangelogGenerator
4
+ RSpec.describe Section do
5
+ let(:options) { {} }
6
+ subject(:section) { described_class.new(options) }
7
+
8
+ describe "#encapsulate_string" do
9
+ let(:string) { "" }
10
+
11
+ context "with the empty string" do
12
+ it "returns the string" do
13
+ expect(section.send(:encapsulate_string, string)).to eq string
14
+ end
15
+ end
16
+
17
+ context "with a string with an escape-needing character in it" do
18
+ let(:string) { "<Inside> and outside" }
19
+
20
+ it "returns the string escaped" do
21
+ expect(section.send(:encapsulate_string, string)).to eq '\\<Inside\\> and outside'
22
+ end
23
+ end
24
+
25
+ context "with a backticked string with an escape-needing character in it" do
26
+ let(:string) { 'back `\` slash' }
27
+
28
+ it "returns the string" do
29
+ expect(section.send(:encapsulate_string, string)).to eq 'back `\` slash'
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#normalize_body" do
35
+ context "it should remove CR" do
36
+ let(:body) { "Some content from GitHub\r\n\r\nUser is describing something" }
37
+ it "returns a cleaned body" do
38
+ expect(section.send(:normalize_body, body)).to eq "Some content from GitHub\n\nUser is describing something"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end