indieweb 0.10.0

A collection of utilities for working with the IndieWeb.
Documentation
/// Integration tests for the IndieWeb authorship algorithm.
///
/// These tests verify the full authorship discovery algorithm
/// following the test cases from <https://authorship.rocks/>
mod helpers;
use helpers::authorship::*;

use indieweb::algorithms::authorship::resolve_author;
use url::Url;

#[tokio::test]
async fn test_01_p_author_string() {
    let html = load_fixture("test_01_p_author_string.html");
    let base_url = "https://example.com/post/1";

    let document = parse_html_to_document(&html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");

    let client = MockClient::default();
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_name("William Shakespeare");
    let author = result.unwrap();
    assert!(author.url.is_none(), "String author should not have URL");
    assert!(
        author.photo.is_none(),
        "String author should not have photo"
    );
}

#[tokio::test]
async fn test_02_p_author_embedded_hcard() {
    let html = load_fixture("test_02_p_author_hcard.html");
    let base_url = "https://example.com/post/2";

    let document = parse_html_to_document(&html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");

    let client = MockClient::default();
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_author_matches(
        &ExpectedAuthor::new("Homer")
            .with_url("https://example.com/homer")
            .with_photo("https://example.com/images/homer.jpg"),
    );
}

#[tokio::test]
async fn test_06_hfeed_parent_author() {
    let html = load_fixture("h_feed_with_author.html");
    let base_url = "https://example.com/feed";

    let document = parse_html_to_document(&html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");

    let client = MockClient::default();
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_author_matches(
        &ExpectedAuthor::new("Feed Author").with_url("https://example.com/author"),
    );
}

#[tokio::test]
async fn test_07_no_author_found() {
    let html = load_fixture("h_entry_no_author.html");
    let base_url = "https://example.com/post/no-author";

    let document = parse_html_to_document(&html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");

    let client = MockClient::default();
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_no_author();
}

#[tokio::test]
async fn test_08_permalink_rel_author_followed() {
    let html = r#"<!DOCTYPE html>
<html>
<body>
  <div class="h-entry">
    <a class="u-url" href="https://example.com/post/1">Permalink</a>
    <p class="p-name e-content">Post content</p>
  </div>
  <a href="https://example.com/author-page" rel="author">Author</a>
</body>
</html>"#;

    let author_page_html = r#"<!DOCTYPE html>
<html>
<body class="h-card">
  <a href="https://example.com/author-page" class="u-url u-uid">
    <div class="p-name">Page Author</div>
  </a>
</body>
</html>"#;

    let base_url = "https://example.com/post/1";

    let mut client = MockClient::default();
    client.add_response("https://example.com/post/1".to_string(), html.to_string());
    client.add_response(
        "https://example.com/author-page".to_string(),
        author_page_html.to_string(),
    );

    let document = parse_html_to_document(html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_name("Page Author");
}

#[tokio::test]
async fn test_09_feed_page_rel_author_ignored() {
    let html = r#"<!DOCTYPE html>
<html>
<body>
  <div class="h-entry">
    <p class="p-name e-content">Post content without u-url</p>
  </div>
  <a href="https://example.com/author-page" rel="author">Author</a>
</body>
</html>"#;

    let base_url = "https://example.com/feed";

    let client = MockClient::default();

    let document = parse_html_to_document(html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_no_author();
}

#[tokio::test]
async fn test_10_author_as_u_author_url() {
    let html = r#"<!DOCTYPE html>
<html>
<body>
  <div class="h-entry">
    <a class="u-author" href="https://example.com/some-author">author page</a>
    <p class="p-name e-content">Post with URL author</p>
  </div>
</body>
</html>"#;

    let author_page_html = r#"<!DOCTYPE html>
<html>
<body class="h-card">
  <a href="https://example.com/some-author" class="u-url u-uid">
    <div class="p-name">URL Author</div>
  </a>
</body>
</html>"#;

    let base_url = "https://example.com/post/url-author";

    let mut client = MockClient::default();
    client.add_response(
        "https://example.com/some-author".to_string(),
        author_page_html.to_string(),
    );

    let document = parse_html_to_document(html, base_url);
    let entry = find_h_entry(&document).expect("h-entry should exist");
    let page_url = Url::parse(base_url).unwrap();

    let result = resolve_author(&client, entry, &document, &page_url).await;

    result.assert_name("URL Author");
}