use crate::docs::Check;
use crate::docs::tests::{fires_once_at, of_kind, silent};
#[test]
fn a_bare_url_reports_at_its_first_character() {
fires_once_at("see https://example.com/x for more", Check::BareUrl, 1, 5);
fires_once_at("http://example.com", Check::BareUrl, 1, 1);
}
#[test]
fn a_url_inside_a_well_formed_link_is_not_bare() {
silent("[example](https://example.com)", Check::BareUrl);
silent(
"see [the docs](http://example.com/a/b) here",
Check::BareUrl,
);
}
#[test]
fn blanking_a_code_span_leaves_the_text_before_it_alone() {
fires_once_at("see https://example.com and `code`", Check::BareUrl, 1, 5);
}
#[test]
fn a_url_inside_backticks_is_not_bare() {
silent("run `curl https://example.com`", Check::BareUrl);
}
#[test]
fn one_link_does_not_excuse_a_bare_url_beside_it() {
fires_once_at(
"[docs](https://example.com) and https://bare.example.com",
Check::BareUrl,
1,
33,
);
}
#[test]
fn a_scheme_with_nothing_after_it_is_not_a_url() {
silent("the prefix https:// is required", Check::BareUrl);
silent("https://", Check::BareUrl);
}
#[test]
fn the_url_ends_at_whitespace() {
let found = of_kind("go to https://example.com/a now", Check::BareUrl);
assert!(
found[0].message.ends_with("https://example.com/a"),
"{}",
found[0].message
);
}
#[test]
fn a_very_long_url_is_excerpted_rather_than_printed_whole() {
let url = format!("https://example.com/{}", "a".repeat(500));
let found = of_kind(&url, Check::BareUrl);
assert!(
found[0].message.chars().count() < 90,
"{}",
found[0].message
);
assert!(found[0].message.ends_with('…'), "{}", found[0].message);
let found = of_kind("https://example.com", Check::BareUrl);
assert_eq!(found[0].message, "bare URL: https://example.com");
}
#[test]
fn a_url_carrying_an_escape_sequence_cannot_reach_the_terminal_intact() {
let found = of_kind("https://example.com/\u{1b}[31mred", Check::BareUrl);
assert!(
!found[0].message.chars().any(char::is_control),
"{:?}",
found[0].message
);
}
#[test]
fn only_the_first_bare_url_on_a_line_is_reported() {
assert_eq!(
of_kind("https://a.example and https://b.example", Check::BareUrl).len(),
1
);
}
#[test]
fn a_well_formed_link_is_silent() {
silent("[text](url)", Check::LinkSyntaxInvalid);
silent("a [one](x) and [two](y) b", Check::LinkSyntaxInvalid);
}
#[test]
fn a_badge_is_one_link_not_two_broken_ones() {
silent(
"[](https://ci.example)",
Check::LinkSyntaxInvalid,
);
}
#[test]
fn a_prose_parenthetical_is_not_broken_link_syntax() {
silent("a sentence (that wraps onto", Check::LinkSyntaxInvalid);
silent("continued) here", Check::LinkSyntaxInvalid);
silent("a smiley :-) alone", Check::LinkSyntaxInvalid);
}
#[test]
fn an_unclosed_link_target_is_reported() {
fires_once_at("[text](url", Check::LinkSyntaxInvalid, 1, 1);
}
#[test]
fn an_unbalanced_bracket_is_reported_in_either_direction() {
fires_once_at("[text without a close", Check::LinkSyntaxInvalid, 1, 1);
fires_once_at("text] without an open", Check::LinkSyntaxInvalid, 1, 1);
}
#[test]
fn brackets_inside_backticks_do_not_count() {
silent("use `items[0]` here", Check::LinkSyntaxInvalid);
silent("write `[` to open", Check::LinkSyntaxInvalid);
}
#[test]
fn a_reference_style_link_is_balanced() {
silent("[text][ref]", Check::LinkSyntaxInvalid);
}
#[test]
fn a_link_reference_definition_declares_a_url_rather_than_leaving_one_bare() {
silent(
"[2.5.0]: https://github.com/slb350/drep/compare/v2.4.0...v2.5.0",
Check::BareUrl,
);
silent("[2.5.0]: https://example.com", Check::LinkSyntaxInvalid);
silent(" [ref]: https://example.com", Check::BareUrl);
}
#[test]
fn a_definition_only_exempts_its_own_destination() {
fires_once_at(
"[not a definition] https://example.com",
Check::BareUrl,
1,
20,
);
silent(
"[ref]: https://a.example \"see https://b.example\"",
Check::BareUrl,
);
}
#[test]
fn a_link_target_containing_parentheses_matches_to_the_first_close() {
silent("[a](b(c)", Check::LinkSyntaxInvalid);
}
#[test]
fn an_unterminated_code_span_does_not_swallow_the_rest_of_the_line() {
fires_once_at(
"a ` stray tick then [broken](",
Check::LinkSyntaxInvalid,
1,
1,
);
}
#[test]
fn two_nested_brackets_in_a_row_terminate() {
let findings = of_kind("[x[a][b](u)", Check::LinkSyntaxInvalid);
assert_eq!(findings.len(), 1, "{findings:?}");
assert!(
findings[0].message.contains("2 `[` against 1 `]`"),
"{}",
findings[0].message
);
}
#[test]
fn the_message_names_the_counts_that_disagree() {
let found = of_kind("[text without a close", Check::LinkSyntaxInvalid);
assert!(
found[0].message.contains("1 `[` against 0 `]`"),
"{}",
found[0].message
);
let found = of_kind("text] without an open", Check::LinkSyntaxInvalid);
assert!(
found[0].message.contains("0 `[` against 1 `]`"),
"{}",
found[0].message
);
let found = of_kind("[text](url", Check::LinkSyntaxInvalid);
assert!(
found[0].message.contains("1 `(` against 0 `)`"),
"{}",
found[0].message
);
}
#[test]
fn a_definition_with_no_space_after_the_colon_still_declares_its_url() {
silent("[ref]:https://example.com", Check::BareUrl);
}
#[test]
fn a_long_label_does_not_move_where_the_destination_starts() {
silent("[unreleased-notes]: https://example.com/x", Check::BareUrl);
}
#[test]
fn link_checks_report_the_right_line_number() {
let content = "clean\n\n[broken](\n";
fires_once_at(content, Check::LinkSyntaxInvalid, 3, 1);
}