use rust_scraper::{
create_http_client, save_results, scrape_with_readability, DownloadedAsset, ScrapedContent,
ValidUrl,
};
use tempfile::TempDir;
#[tokio::test]
async fn test_scraper_can_fetch_simple_page() {
let url = url::Url::parse("https://example.com").expect("Valid URL");
let client = create_http_client().expect("HTTP client");
let result: Result<Vec<_>, _> = scrape_with_readability(&client, &url).await;
if let Ok(contents) = result {
if !contents.is_empty() {
assert!(!contents[0].title.is_empty());
}
}
}
#[test]
fn test_output_format_display() {
use rust_scraper::OutputFormat;
let markdown = OutputFormat::Markdown;
let text = OutputFormat::Text;
let json = OutputFormat::Json;
let _ = format!("{:?}", markdown);
let _ = format!("{:?}", text);
let _ = format!("{:?}", json);
}
#[test]
fn test_args_has_required_fields() {
use rust_scraper::Args;
use rust_scraper::ExportFormat;
let args = Args {
url: "https://example.com".to_string(),
selector: "article".to_string(),
output: std::path::PathBuf::from("custom_output"),
export_format: ExportFormat::Text,
delay_ms: 500,
max_pages: 5,
download_images: false,
download_documents: false,
verbose: 2,
concurrency: rust_scraper::ConcurrencyConfig::default(),
use_sitemap: false,
sitemap_url: None,
interactive: false,
resume: false,
state_dir: None,
};
assert_eq!(args.url, "https://example.com");
assert_eq!(args.selector, "article");
assert_eq!(args.export_format, ExportFormat::Text);
assert_eq!(args.delay_ms, 500);
assert_eq!(args.max_pages, 5);
assert_eq!(args.verbose, 2);
}
#[tokio::test]
async fn test_scrape_handles_404_gracefully() {
let url =
url::Url::parse("https://example.com/this-does-not-exist-404-test").expect("Valid URL");
let client = create_http_client().expect("HTTP client");
let result: Result<Vec<_>, _> = scrape_with_readability(&client, &url).await;
assert!(result.is_err());
let err = result.unwrap_err();
let error_msg = err.to_string();
assert!(
error_msg.contains("404")
|| error_msg.contains("HTTP")
|| error_msg.contains("retries")
|| error_msg.contains("middleware"),
"Error should contain 404/HTTP/retries/middleware, got: {}",
error_msg
);
}
#[tokio::test]
async fn test_scrape_handles_invalid_url_gracefully() {
let result = create_http_client();
assert!(result.is_ok());
}
#[test]
fn test_save_results_to_nested_directory() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let output_dir = temp_dir.path().join("level1").join("level2").join("output");
let results = vec![ScrapedContent {
title: "Test".to_string(),
content: "Content".to_string(),
url: ValidUrl::parse("https://example.com").unwrap(),
excerpt: None,
author: None,
date: None,
html: None,
assets: Vec::new(),
}];
let result = save_results(&results, &output_dir, &rust_scraper::OutputFormat::Text);
assert!(result.is_ok());
assert!(output_dir.exists());
let files: Vec<_> = std::fs::read_dir(&output_dir)
.unwrap()
.filter_map(|e| e.ok())
.collect();
assert!(!files.is_empty());
}
#[test]
fn test_save_results_json_with_special_characters() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let output_dir = temp_dir.path().to_path_buf();
let results = vec![ScrapedContent {
title: "Test with \"quotes\" and 'apostrophes'".to_string(),
content: "Content with\nnewlines\tand\ttabs".to_string(),
url: ValidUrl::parse("https://example.com?param=value&other=test").unwrap(),
excerpt: Some("Excerpt with <html> & \"special\" chars".to_string()),
author: Some("Author Name".to_string()),
date: Some("2024-01-01".to_string()),
html: None,
assets: vec![DownloadedAsset {
url: "https://example.com/img.png".to_string(),
local_path: "images/img.png".to_string(),
asset_type: "image".to_string(),
size: 100,
}],
}];
let result = save_results(&results, &output_dir, &rust_scraper::OutputFormat::Json);
assert!(result.is_ok());
let json_path = output_dir.join("results.json");
let content = std::fs::read_to_string(&json_path).unwrap();
let parsed: Vec<ScrapedContent> = serde_json::from_str(&content).expect("Valid JSON");
assert_eq!(parsed.len(), 1);
}
#[test]
fn test_save_results_markdown_with_markdown_syntax() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let output_dir = temp_dir.path().to_path_buf();
let results = vec![ScrapedContent {
title: "# Heading 1".to_string(),
content: "**Bold** and *italic* and `code`".to_string(),
url: ValidUrl::parse("https://example.com").unwrap(),
excerpt: None,
author: None,
date: None,
html: None,
assets: Vec::new(),
}];
let result = save_results(&results, &output_dir, &rust_scraper::OutputFormat::Markdown);
assert!(result.is_ok());
use walkdir::WalkDir;
let files: Vec<_> = WalkDir::new(&output_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.collect();
let content = std::fs::read_to_string(files[0].path()).unwrap();
assert!(content.contains("# Heading 1"));
assert!(content.contains("**Bold**"));
}
#[cfg(feature = "images")]
#[tokio::test]
async fn test_download_images_from_website() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let output_dir = temp_dir.path().to_path_buf();
let url = url::Url::parse("https://webscraper.io/test-sites").expect("Valid URL");
let client = create_http_client().expect("HTTP client");
let config = rust_scraper::ScraperConfig {
download_images: true,
download_documents: false,
output_dir: output_dir.clone(),
max_file_size: Some(10 * 1024 * 1024), };
let result: Result<Vec<_>, _> = scrape_with_config(&client, &url, &config).await;
if let Ok(contents) = result {
if !contents.is_empty() {
let content = &contents[0];
assert!(
!content.assets.is_empty(),
"Should have downloaded some images"
);
let images_dir = output_dir.join("images");
assert!(images_dir.exists(), "Images directory should exist");
let image_files: Vec<_> = WalkDir::new(&images_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.collect();
assert!(
!image_files.is_empty(),
"Should have downloaded image files"
);
eprintln!(
"✅ Downloaded {} images: {:?}",
content.assets.len(),
content
.assets
.iter()
.map(|a| &a.local_path)
.collect::<Vec<_>>()
);
}
}
}
#[cfg(feature = "documents")]
#[tokio::test]
async fn test_download_documents_from_website() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let output_dir = temp_dir.path().to_path_buf();
let url = url::Url::parse("https://toscrape.com").expect("Valid URL");
let client = create_http_client().expect("HTTP client");
let config = rust_scraper::ScraperConfig {
download_images: false,
download_documents: true,
output_dir: output_dir.clone(),
max_file_size: Some(50 * 1024 * 1024), };
let result: Result<Vec<_>, _> = scrape_with_config(&client, &url, &config).await;
if let Ok(contents) = result {
eprintln!(
"✅ Document extraction completed, found {} items",
contents.len()
);
}
}