use anyhow::{Context, Result};
use dioxus_iconify::api::IconifyClient;
use dioxus_iconify::generator::Generator;
use dioxus_iconify::naming::IconIdentifier;
use rstest::rstest;
use std::fs;
use std::process::Command;
use tempfile::TempDir;
#[tokio::test]
#[ignore] async fn test_generated_code_compiles_without_warnings() -> Result<()> {
let temp_dir = TempDir::new()?;
let project_dir = temp_dir.path();
let icons_dir = project_dir.join("src/icons");
println!("Test project directory: {:?}", project_dir);
let generator = Generator::new(icons_dir.clone());
let client = IconifyClient::new()?;
let test_icons = vec!["mdi:home", "heroicons:arrow-left", "lucide:settings"];
let mut icons_to_add = Vec::new();
for icon_id in &test_icons {
let identifier = IconIdentifier::parse(icon_id)?;
let icon = client
.fetch_icon(&identifier.collection, &identifier.icon_name)
.await?;
icons_to_add.push((identifier, icon));
}
generator.add_icons(&icons_to_add)?;
let cargo_toml = r#"[package]
name = "icon-test"
version = "0.1.0"
edition = "2021"
[dependencies]
dioxus = "0.7"
"#;
fs::write(project_dir.join("Cargo.toml"), cargo_toml)?;
fs::create_dir_all(project_dir.join("src"))?;
let main_rs = r#"#![deny(warnings)]
mod icons;
use dioxus::prelude::*;
use icons::Icon;
use icons::{heroicons, lucide, mdi};
fn main() {
// Use the icons to avoid dead_code warnings
let _home = mdi::Home;
let _arrow = heroicons::ArrowLeft;
let _settings = lucide::Settings;
println!("Icons loaded successfully");
}
#[component]
fn App() -> Element {
rsx! {
div {
Icon { data: mdi::Home }
Icon { data: heroicons::ArrowLeft }
Icon { data: lucide::Settings, width: "32", height: "32" }
Icon { data: mdi::Home, size: "24" }
Icon { data: heroicons::ArrowLeft, size: 32.to_string() }
Icon { data: lucide::Settings, size: "2em" }
}
}
}
"#;
fs::write(project_dir.join("src/main.rs"), main_rs)?;
let output = Command::new("cargo")
.args(["build", "--quiet"])
.current_dir(project_dir)
.output()
.context("Failed to run cargo build")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
eprintln!("Build failed!");
eprintln!("STDOUT:\n{}", stdout);
eprintln!("STDERR:\n{}", stderr);
panic!("Build failed with exit code: {:?}", output.status.code());
}
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let full_output = format!("{}\n{}", stdout, stderr);
if full_output.contains("warning:") {
eprintln!("Build output contained warnings:");
eprintln!("{}", full_output);
panic!("Generated code produced compilation warnings");
}
println!("✓ Generated code compiles without warnings");
Ok(())
}
#[rstest]
#[case(
"test:sample-icon",
"SampleIcon",
r#"<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>"#
)]
#[case(
"my-collection:custom-icon",
"CustomIcon",
r#"<circle cx="12" cy="12" r="10"/>"#
)]
fn test_generated_icons_are_valid_rust(
#[case] icon_id: &str,
#[case] expected_const_name: &str,
#[case] body: &str,
) -> Result<()> {
let temp_dir = TempDir::new()?;
let icons_dir = temp_dir.path().join("icons");
let generator = Generator::new(icons_dir.clone());
use dioxus_iconify::api::IconifyIcon;
let test_icon = IconifyIcon {
body: body.to_string(),
width: Some(24),
height: Some(24),
view_box: Some("0 0 24 24".to_string()),
};
let identifier = IconIdentifier::parse(icon_id)?;
let collection = identifier.collection.clone().replace('-', "_");
generator.add_icons(&[(identifier, test_icon)])?;
let generated_file = icons_dir.join(format!("{}.rs", collection));
assert!(generated_file.exists(), "Generated file should exist");
let content = fs::read_to_string(&generated_file)?;
assert!(
content.contains("use super::IconData;"),
"Should import IconData"
);
assert!(
content.contains(&format!("pub const {}: IconData", expected_const_name)),
"Should define icon constant"
);
assert!(
content.contains(&format!(r#"name: "{}""#, icon_id)),
"Should include full icon name"
);
assert!(
content.contains(r#"view_box: "0 0 24 24""#),
"Should include viewBox"
);
let mod_file = icons_dir.join("mod.rs");
assert!(mod_file.exists(), "mod.rs should be created");
let mod_content = fs::read_to_string(&mod_file)?;
assert!(
mod_content.contains(&format!("pub mod {};", collection)),
"Should declare collection module"
);
assert!(
mod_content.contains("pub struct IconData"),
"Should define IconData struct"
);
assert!(
mod_content.contains("pub fn Icon("),
"Should define Icon component"
);
Ok(())
}
#[test]
fn test_list_icons_integration() -> Result<()> {
let temp_dir = TempDir::new()?;
let icons_dir = temp_dir.path().join("icons");
let generator = Generator::new(icons_dir.clone());
use dioxus_iconify::api::IconifyIcon;
let test_icon = IconifyIcon {
body: r#"<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>"#.to_string(),
width: Some(24),
height: Some(24),
view_box: Some("0 0 24 24".to_string()),
};
let icons_to_add = vec![
(IconIdentifier::parse("mdi:home")?, test_icon.clone()),
(IconIdentifier::parse("mdi:settings")?, test_icon.clone()),
(
IconIdentifier::parse("heroicons:arrow-left")?,
test_icon.clone(),
),
(IconIdentifier::parse("lucide:user")?, test_icon.clone()),
];
generator.add_icons(&icons_to_add)?;
let icons_by_collection = generator.list_icons()?;
assert_eq!(
icons_by_collection.len(),
3,
"Should have 3 collections: mdi, heroicons, lucide"
);
let mdi_icons = icons_by_collection
.get("mdi")
.expect("mdi collection should exist");
assert_eq!(mdi_icons.len(), 2, "mdi should have 2 icons");
assert!(
mdi_icons.contains(&"mdi:home".to_string()),
"Should contain mdi:home"
);
assert!(
mdi_icons.contains(&"mdi:settings".to_string()),
"Should contain mdi:settings"
);
let heroicons_icons = icons_by_collection
.get("heroicons")
.expect("heroicons collection should exist");
assert_eq!(heroicons_icons.len(), 1, "heroicons should have 1 icon");
assert!(
heroicons_icons.contains(&"heroicons:arrow-left".to_string()),
"Should contain heroicons:arrow-left"
);
let lucide_icons = icons_by_collection
.get("lucide")
.expect("lucide collection should exist");
assert_eq!(lucide_icons.len(), 1, "lucide should have 1 icon");
assert!(
lucide_icons.contains(&"lucide:user".to_string()),
"Should contain lucide:user"
);
for (collection, icons) in &icons_by_collection {
for icon in icons {
assert!(
icon.starts_with(&format!("{}:", collection)),
"Icon {} should start with collection prefix {}:",
icon,
collection
);
}
}
println!("✓ List icons integration test passed");
Ok(())
}