hoe-halostatue 2.1.2 → 3.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.
@@ -0,0 +1,527 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest_helper"
4
+ require "hoe/halostatue/markdown/linkify"
5
+
6
+ class TestHoeHalostatueMarkdownLinkify < Minitest::Test
7
+ BUG_TRACKER_URI = "https://github.com/owner/repo/issues"
8
+
9
+ private def linkify(source, **kwargs)
10
+ Hoe::Halostatue::Markdown::Linkify.linkify(
11
+ source,
12
+ bug_tracker_uri: BUG_TRACKER_URI, **kwargs
13
+ )
14
+ end
15
+
16
+ private def assert_unchanged(input, message: nil, **kwargs)
17
+ expected = {markdown: input, changed: false}
18
+ actual = linkify(input, **kwargs)
19
+
20
+ assert_equal expected, actual,
21
+ message(message || "Expected input and output to be the same", nil) { diff expected[:markdown], actual[:markdown] }
22
+ end
23
+
24
+ private def assert_changed(expected, input, message: nil, **kwargs)
25
+ expected = {markdown: expected, changed: true}
26
+ actual = linkify(input, **kwargs)
27
+
28
+ assert_equal expected, actual,
29
+ message(message || "Expected output to be different than input", nil) { diff expected[:markdown], actual[:markdown] }
30
+ end
31
+
32
+ def test_linkifies_usernames_with_reference_style
33
+ input = "Thanks @example for the help!"
34
+ expected = <<~MD
35
+ Thanks [@example][gh-user-example] for the help!
36
+
37
+ [gh-user-example]: https://github.com/example
38
+ MD
39
+
40
+ assert_changed expected, input
41
+ end
42
+
43
+ def test_linkifies_usernames_with_inline_style
44
+ input = "Thanks @example for the help!"
45
+ expected = "Thanks [@example](https://github.com/example) for the help!"
46
+
47
+ assert_changed expected, input, style: :inline
48
+ end
49
+
50
+ def test_does_not_linkify_email_addresses
51
+ input = "Email is user@example.com"
52
+ assert_unchanged input
53
+ end
54
+
55
+ def test_linkifies_usernames_with_hyphens
56
+ input = "Thanks @foo-bar-baz for the help!"
57
+ expected = <<~MD
58
+ Thanks [@foo-bar-baz][gh-user-foo-bar-baz] for the help!
59
+
60
+ [gh-user-foo-bar-baz]: https://github.com/foo-bar-baz
61
+ MD
62
+
63
+ assert_changed expected, input
64
+ end
65
+
66
+ def test_linkifies_usernames_at_end_of_sentence
67
+ input = "Hello @example."
68
+ expected = <<~MD
69
+ Hello [@example][gh-user-example].
70
+
71
+ [gh-user-example]: https://github.com/example
72
+ MD
73
+
74
+ assert_changed expected, input
75
+ end
76
+
77
+ def test_linkifies_usernames_with_punctuation
78
+ input = "See @alice, @bob; and @charlie!"
79
+ expected = <<~MD
80
+ See [@alice][gh-user-alice], [@bob][gh-user-bob]; and [@charlie][gh-user-charlie]!
81
+
82
+ [gh-user-alice]: https://github.com/alice
83
+ [gh-user-bob]: https://github.com/bob
84
+ [gh-user-charlie]: https://github.com/charlie
85
+ MD
86
+
87
+ assert_changed expected, input
88
+ end
89
+
90
+ def test_does_not_linkify_username_with_trailing_hyphen
91
+ input = "Not a user: @invalid-"
92
+ assert_unchanged input
93
+ end
94
+
95
+ def test_does_not_linkify_username_with_leading_hyphen
96
+ input = "Not a user: @-invalid"
97
+ assert_unchanged input
98
+ end
99
+
100
+ def test_does_not_linkify_mastodon_handles
101
+ input = "Follow me at @exemplar@example.com"
102
+ assert_unchanged input
103
+ end
104
+
105
+ def test_does_not_linkify_username_with_consecutive_hyphens
106
+ input = "Not valid: @foo--bar"
107
+ assert_unchanged input
108
+ end
109
+
110
+ def test_linkifies_username_at_max_length
111
+ username = "a" * 39
112
+ input = "User @#{username} here"
113
+ expected = <<~MD
114
+ User [@#{username}][gh-user-#{username}] here
115
+
116
+ [gh-user-#{username}]: https://github.com/#{username}
117
+ MD
118
+
119
+ assert_changed expected, input
120
+ end
121
+
122
+ def test_does_not_linkify_username_over_max_length
123
+ username = "a" * 40
124
+ input = "User @#{username} here"
125
+ assert_unchanged input
126
+ end
127
+
128
+ def test_does_not_linkify_patterns_in_code_blocks_and_spans
129
+ input = <<~MD
130
+ See `@user` and `#123` inline.
131
+
132
+ ```
133
+ @another and #456 in block
134
+ ```
135
+ MD
136
+
137
+ assert_unchanged input
138
+ end
139
+
140
+ def test_preserves_non_comment_fragments_in_github_urls
141
+ input = "See https://github.com/owner/repo/issues/123#heading"
142
+ expected = <<~MD
143
+ See [#123][gh-issue-123]
144
+
145
+ [gh-issue-123]: https://github.com/owner/repo/issues/123#heading
146
+ MD
147
+
148
+ assert_changed expected, input
149
+ end
150
+
151
+ def test_linkifies_same_pattern_in_different_contexts
152
+ input = <<~MD
153
+ See @alice and #123.
154
+
155
+ But not `@alice` or `#123` in code.
156
+ MD
157
+ expected = <<~MD
158
+ See [@alice][gh-user-alice] and [#123][gh-issue-123].
159
+
160
+ But not `@alice` or `#123` in code.
161
+
162
+ [gh-user-alice]: https://github.com/alice
163
+ [gh-issue-123]: https://github.com/owner/repo/issues/123
164
+ MD
165
+
166
+ assert_changed expected, input
167
+ end
168
+
169
+ def test_handles_reference_id_collision
170
+ input = <<~MD
171
+ See @alice
172
+
173
+ [gh-user-alice]: https://example.com/different-alice
174
+ MD
175
+ expected = <<~MD
176
+ See [@alice][gh-user-alice-2]
177
+
178
+ [gh-user-alice]: https://example.com/different-alice
179
+ [gh-user-alice-2]: https://github.com/alice
180
+ MD
181
+
182
+ assert_changed expected, input
183
+ end
184
+
185
+ def test_does_not_linkify_github_urls_in_inline_code
186
+ input = "See `https://github.com/owner/repo/issues/123` for details"
187
+ assert_unchanged input
188
+ end
189
+
190
+ def test_does_not_linkify_autolinked_urls
191
+ input = "See <https://github.com/owner/repo/issues/123>"
192
+ assert_unchanged input
193
+ end
194
+
195
+ def test_does_not_linkify_patterns_in_square_brackets
196
+ input = "Reference [@user] and [#123] here"
197
+ assert_unchanged input
198
+ end
199
+
200
+ def test_linkifies_full_github_uris_without_bug_tracker_uri
201
+ input = "See https://github.com/owner/repo/issues/123"
202
+ expected = <<~MD
203
+ See [owner/repo#123][gh-owner-repo-123]
204
+
205
+ [gh-owner-repo-123]: https://github.com/owner/repo/issues/123
206
+ MD
207
+
208
+ assert_changed expected, input, bug_tracker_uri: nil
209
+ end
210
+
211
+ def test_does_not_linkify_issue_mentions_without_bug_tracker_uri
212
+ assert_unchanged "Fixed in #123", bug_tracker_uri: nil
213
+ end
214
+
215
+ def test_does_not_linkify_issue_mentions_with_non_github_bug_tracker
216
+ assert_unchanged "Fixed in #123", bug_tracker_uri: "https://bugs.example.com/issues"
217
+ end
218
+
219
+ def test_linkifies_bare_username_urls
220
+ input = "See https://github.com/halostatue for details"
221
+ expected = <<~MD
222
+ See [@halostatue][gh-user-halostatue] for details
223
+
224
+ [gh-user-halostatue]: https://github.com/halostatue
225
+ MD
226
+
227
+ assert_changed expected, input
228
+ end
229
+
230
+ def test_linkifies_repo_urls
231
+ input = "Check out https://github.com/owner/repo"
232
+ expected = <<~MD
233
+ Check out [owner/repo][gh-owner-repo]
234
+
235
+ [gh-owner-repo]: https://github.com/owner/repo
236
+ MD
237
+
238
+ assert_changed expected, input
239
+ end
240
+
241
+ def test_linkifies_owner_repo_issue_pattern
242
+ input = "See owner/repo#123 for details"
243
+ expected = <<~MD
244
+ See [owner/repo#123][gh-owner-repo-123] for details
245
+
246
+ [gh-owner-repo-123]: https://github.com/owner/repo/issues/123
247
+ MD
248
+
249
+ assert_changed expected, input
250
+ end
251
+
252
+ def test_preserves_query_parameters_in_github_urls
253
+ input = "See https://github.com/owner/repo/issues/123?foo=bar"
254
+ expected = <<~MD
255
+ See [#123][gh-issue-123]
256
+
257
+ [gh-issue-123]: https://github.com/owner/repo/issues/123?foo=bar
258
+ MD
259
+
260
+ assert_changed expected, input
261
+ end
262
+
263
+ def test_does_not_linkify_usernames_in_code_blocks
264
+ input = <<~MD
265
+ Check out this code:
266
+
267
+ ```ruby
268
+ # @example wrote this
269
+ def foo
270
+ end
271
+ ```
272
+ MD
273
+ assert_unchanged input
274
+ end
275
+
276
+ def test_does_not_linkify_usernames_in_inline_code
277
+ input = "Use `@example` as the username"
278
+ assert_unchanged input
279
+ end
280
+
281
+ def test_does_not_linkify_usernames_already_in_links
282
+ input = "See [@example](https://example.com)"
283
+ assert_unchanged input
284
+ end
285
+
286
+ def test_reuses_existing_reference_for_same_username
287
+ input = "Thanks @example and @example again!"
288
+ expected = <<~MD
289
+ Thanks [@example][gh-user-example] and [@example][gh-user-example] again!
290
+
291
+ [gh-user-example]: https://github.com/example
292
+ MD
293
+
294
+ assert_changed expected, input
295
+ end
296
+
297
+ def test_reuses_existing_reference_definition_from_source
298
+ input = <<~MD
299
+ Thanks @example!
300
+
301
+ [gh-user-example]: https://github.com/example
302
+ MD
303
+ expected = <<~MD
304
+ Thanks [@example][gh-user-example]!
305
+
306
+ [gh-user-example]: https://github.com/example
307
+ MD
308
+
309
+ assert_changed expected, input
310
+ end
311
+
312
+ def test_linkifies_issue_mentions_with_reference_style
313
+ input = "Fixed in #123"
314
+ expected = <<~MD
315
+ Fixed in [#123][gh-issue-123]
316
+
317
+ [gh-issue-123]: https://github.com/owner/repo/issues/123
318
+ MD
319
+
320
+ assert_changed expected, input
321
+ end
322
+
323
+ def test_linkifies_issue_mentions_with_inline_style
324
+ input = "Fixed in #123"
325
+ expected = "Fixed in [#123](https://github.com/owner/repo/issues/123)"
326
+
327
+ assert_changed expected, input, style: :inline
328
+ end
329
+
330
+ def test_does_not_linkify_issue_mentions_in_code_blocks
331
+ input = <<~MD
332
+ ```ruby
333
+ # Issue #123
334
+ ```
335
+ MD
336
+ assert_unchanged input
337
+ end
338
+
339
+ def test_does_not_linkify_issue_mentions_in_inline_code
340
+ input = "Use `#123` as reference"
341
+ assert_unchanged input
342
+ end
343
+
344
+ def test_does_not_linkify_issue_mentions_already_in_links
345
+ input = "See [#123](https://example.com)"
346
+ assert_unchanged input
347
+ end
348
+
349
+ def test_reuses_existing_reference_for_same_issue
350
+ input = "See #123 and #123 again"
351
+ expected = <<~MD
352
+ See [#123][gh-issue-123] and [#123][gh-issue-123] again
353
+
354
+ [gh-issue-123]: https://github.com/owner/repo/issues/123
355
+ MD
356
+
357
+ assert_changed expected, input
358
+ end
359
+
360
+ def test_shortens_same_repo_issue_uris
361
+ input = "See https://github.com/owner/repo/issues/123"
362
+ expected = <<~MD
363
+ See [#123][gh-issue-123]
364
+
365
+ [gh-issue-123]: https://github.com/owner/repo/issues/123
366
+ MD
367
+
368
+ assert_changed expected, input
369
+ end
370
+
371
+ def test_shortens_same_repo_pr_uris
372
+ input = "See https://github.com/owner/repo/pull/456"
373
+ expected = <<~MD
374
+ See [#456][gh-issue-456]
375
+
376
+ [gh-issue-456]: https://github.com/owner/repo/pull/456
377
+ MD
378
+
379
+ assert_changed expected, input
380
+ end
381
+
382
+ def test_shortens_other_repo_uris_with_owner_repo_prefix
383
+ input = "See https://github.com/other/project/issues/789"
384
+ expected = <<~MD
385
+ See [other/project#789][gh-other-project-789]
386
+
387
+ [gh-other-project-789]: https://github.com/other/project/issues/789
388
+ MD
389
+
390
+ assert_changed expected, input
391
+ end
392
+
393
+ def test_handles_comment_fragments
394
+ input = "See https://github.com/owner/repo/issues/123#issuecomment-456"
395
+ expected = <<~MD
396
+ See [#123 (comment)][gh-issue-123-456]
397
+
398
+ [gh-issue-123-456]: https://github.com/owner/repo/issues/123#issuecomment-456
399
+ MD
400
+
401
+ assert_changed expected, input
402
+ end
403
+
404
+ def test_handles_discussion_uris
405
+ input = "See https://github.com/owner/repo/discussions/999"
406
+ expected = <<~MD
407
+ See [#999][gh-issue-999]
408
+
409
+ [gh-issue-999]: https://github.com/owner/repo/discussions/999
410
+ MD
411
+
412
+ assert_changed expected, input
413
+ end
414
+
415
+ def test_does_not_linkify_uris_already_in_links
416
+ input = "[issue](https://github.com/owner/repo/issues/123)"
417
+ assert_unchanged input
418
+ end
419
+
420
+ def test_does_not_linkify_uris_in_code_blocks
421
+ input = <<~MD
422
+ ```
423
+ https://github.com/owner/repo/issues/123
424
+ ```
425
+ MD
426
+ assert_unchanged input
427
+ end
428
+
429
+ def test_adds_prefixes_when_configured
430
+ input = "See https://github.com/owner/repo/issues/123"
431
+ expected = <<~MD
432
+ See [issue #123][gh-issue-123]
433
+
434
+ [gh-issue-123]: https://github.com/owner/repo/issues/123
435
+ MD
436
+
437
+ assert_changed expected, input, uri_prefixes: {issue: "issue", pull: "pull"}
438
+ end
439
+
440
+ def test_uses_default_prefixes_when_true
441
+ input = "See https://github.com/owner/repo/pull/456"
442
+ expected = <<~MD
443
+ See [pull request #456][gh-issue-456]
444
+
445
+ [gh-issue-456]: https://github.com/owner/repo/pull/456
446
+ MD
447
+
448
+ assert_changed expected, input, uri_prefixes: true
449
+ end
450
+
451
+ def test_does_not_linkify_text_in_reference_definitions
452
+ input = <<~MD
453
+ [#123]: https://github.com/owner/repo/issues/123
454
+ MD
455
+ assert_unchanged input
456
+ end
457
+
458
+ def test_appends_new_references_after_existing_content
459
+ input = <<~MD
460
+ See @example [existing][existing].
461
+
462
+ [existing]: https://example.com
463
+ MD
464
+ expected = <<~MD
465
+ See [@example][gh-user-example] [existing][existing].
466
+
467
+ [existing]: https://example.com
468
+ [gh-user-example]: https://github.com/example
469
+ MD
470
+
471
+ assert_changed expected, input
472
+ end
473
+
474
+ def test_handles_multiple_patterns_in_one_line
475
+ input = "Thanks @example for fixing #123!"
476
+ expected = <<~MD
477
+ Thanks [@example][gh-user-example] for fixing [#123][gh-issue-123]!
478
+
479
+ [gh-user-example]: https://github.com/example
480
+ [gh-issue-123]: https://github.com/owner/repo/issues/123
481
+ MD
482
+
483
+ assert_changed expected, input
484
+ end
485
+
486
+ def test_handles_empty_input
487
+ assert_unchanged ""
488
+ end
489
+
490
+ def test_handles_input_with_no_matches
491
+ input = "Just some regular text"
492
+ assert_unchanged input
493
+ end
494
+
495
+ def test_preserves_existing_markdown_formatting
496
+ input = <<~MD
497
+ # Heading
498
+
499
+ **Bold** and *italic* text with @example mention.
500
+
501
+ - List item
502
+ - Another item
503
+ MD
504
+ expected = <<~MD
505
+ # Heading
506
+
507
+ **Bold** and *italic* text with [@example][gh-user-example] mention.
508
+
509
+ - List item
510
+ - Another item
511
+
512
+ [gh-user-example]: https://github.com/example
513
+ MD
514
+
515
+ assert_changed expected, input
516
+ end
517
+
518
+ Dir["*.md"].each do |filename|
519
+ testname = "test_golden_master_#{File.basename(filename, ".md").downcase}"
520
+
521
+ define_method testname do
522
+ input = File.read(filename)
523
+
524
+ assert_unchanged input, message: "#{filename} should not change after linkification"
525
+ end
526
+ end
527
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "minitest"
4
+ require "minitest/autorun"
5
+ require "minitest/focus"
6
+
7
+ if ENV["STRICT"]
8
+ $VERBOSE = true
9
+ Warning[:deprecated] = true
10
+ require "minitest/error_on_warning"
11
+ end