use dir_indexer::get_relative_file_paths_set; use expect_test::{expect, Expect}; use forc_doc::{self, generate_docs, Command, DocResult}; use std::{ collections::HashSet, path::{Path, PathBuf}, }; /// The path to the generated HTML of the type the traits are implemented on. const IMPL_FOR: &str = "bar/struct.Bar.html"; const DATA_DIR: &str = "tests/fixtures"; const JS_SEARCH_FILE_PATH: &str = "search.js"; #[test] fn builds_lib_std_docs() { let path = Path::new("./../../sway-lib-std"); let build_instructions = Command { path: Some(path.to_str().unwrap().to_string()), ..Default::default() }; println!("Building docs for {:?}", build_instructions.path); let res = generate_docs(&build_instructions); assert!(res.is_ok()); } #[test] fn test_impl_traits_default() { let doc_dir_name: &str = "impl_traits_default"; let project_name = "impl_traits"; let command = Command { path: Some(format!("{}/{}", DATA_DIR, project_name)), doc_path: Some(doc_dir_name.into()), ..Default::default() }; let (doc_path, _doc_result) = generate_docs(&command).unwrap(); assert_index_html( &doc_path, project_name, &expect![[r##" Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

pub fn foo()

something more about foo();

pub fn add(self, other: Self) -> Self

pub fn subtract(self, other: Self) -> Self

"##]], ); assert_search_js( &doc_path, &expect![[ r#"var SEARCH_INDEX={"impl_traits":[{"html_filename":"trait.Foo.html","module_info":["impl_traits","foo"],"name":"Foo","preview":"","type_name":"trait"},{"html_filename":"trait.Baz.html","module_info":["impl_traits","foo"],"name":"Baz","preview":"","type_name":"trait"},{"html_filename":"struct.Bar.html","module_info":["impl_traits","bar"],"name":"Bar","preview":"","type_name":"struct"},{"html_filename":"index.html","module_info":["impl_traits","bar"],"name":"bar","preview":"","type_name":"module"},{"html_filename":"index.html","module_info":["impl_traits","foo"],"name":"foo","preview":"","type_name":"module"}],"ops":[{"html_filename":"trait.Add.html","module_info":["ops"],"name":"Add","preview":"","type_name":"trait"},{"html_filename":"trait.Subtract.html","module_info":["ops"],"name":"Subtract","preview":"","type_name":"trait"},{"html_filename":"index.html","module_info":["ops"],"name":"ops","preview":"","type_name":"module"}]}; "object"==typeof exports&&"undefined"!=typeof module&&(module.exports=SEARCH_INDEX);"# ]], ); assert_file_tree( doc_dir_name, project_name, vec![ "impl_traits/foo/trait.Foo.html", "impl_traits/foo/index.html", "impl_traits/all.html", "ops/trait.Subtract.html", "ops/all.html", "impl_traits/bar/struct.Bar.html", "impl_traits/bar/index.html", "ops/trait.Add.html", "search.js", "impl_traits/index.html", "ops/index.html", "impl_traits/foo/trait.Baz.html", ], ); } #[test] fn test_workspace_docs() { let doc_dir_name: &str = "workspace_docs"; let workspace_name = "sample_workspace"; let command = Command { path: Some(format!("{}/{}", DATA_DIR, workspace_name)), doc_path: Some(doc_dir_name.into()), ..Default::default() }; let (doc_path, doc_result) = generate_docs(&command).unwrap(); // Verify that we got a workspace result match &doc_result { DocResult::Workspace { name, libraries } => { assert_eq!(name, workspace_name); assert_eq!( libraries.len(), 2, "Expected 2 libraries, found {}: {:?}", libraries.len(), libraries ); assert!(libraries.iter().any(|lib| lib.name == "lib_a")); assert!(libraries.iter().any(|lib| lib.name == "lib_b")); } DocResult::Package(_) => panic!("Expected workspace result, got package"), } // Check that workspace index.html was created let workspace_index_path = doc_path.join("index.html"); assert!( workspace_index_path.exists(), "Workspace index.html should exist" ); // Check that library-specific docs were created let lib_a_index = doc_path.join("lib_a").join("index.html"); let lib_b_index = doc_path.join("lib_b").join("index.html"); assert!(lib_a_index.exists(), "lib_a index.html should exist"); assert!(lib_b_index.exists(), "lib_b index.html should exist"); // Check that search.js was created let search_js = doc_path.join("search.js"); assert!(search_js.exists(), "search.js should exist"); // Read and verify the workspace index contains library links let workspace_content = std::fs::read_to_string(&workspace_index_path).unwrap(); assert!( workspace_content.contains("lib_a/index.html"), "Workspace index should link to lib_a" ); assert!( workspace_content.contains("lib_b/index.html"), "Workspace index should link to lib_b" ); assert!( workspace_content.contains("This workspace contains the following libraries"), "Should contain workspace description" ); } #[test] fn test_impl_traits_no_deps() { let doc_dir_name: &str = "impl_traits_no_deps"; let project_name: &str = "impl_traits_generic"; let command = Command { path: Some(format!("{}/{}", DATA_DIR, project_name)), doc_path: Some(doc_dir_name.into()), no_deps: true, ..Default::default() }; let (doc_path, _doc_result) = generate_docs(&command).unwrap(); assert_index_html( &doc_path, project_name, &expect![[r##" Bar in bar - Sway
pub struct Bar<T> {}

Trait Implementations

pub fn foo()

something more about foo();

fn foo_bar()

pub fn add(self, other: Self) -> Self

pub fn subtract(self, other: Self) -> Self

"##]], ); assert_search_js( &doc_path, &expect![[ r#"var SEARCH_INDEX={"impl_traits_generic":[{"html_filename":"trait.Foo.html","module_info":["impl_traits_generic","foo"],"name":"Foo","preview":"","type_name":"trait"},{"html_filename":"trait.Baz.html","module_info":["impl_traits_generic","foo"],"name":"Baz","preview":"","type_name":"trait"},{"html_filename":"struct.Bar.html","module_info":["impl_traits_generic","bar"],"name":"Bar","preview":"","type_name":"struct"},{"html_filename":"index.html","module_info":["impl_traits_generic","bar"],"name":"bar","preview":"","type_name":"module"},{"html_filename":"index.html","module_info":["impl_traits_generic","foo"],"name":"foo","preview":"","type_name":"module"}]}; "object"==typeof exports&&"undefined"!=typeof module&&(module.exports=SEARCH_INDEX);"# ]], ); assert_file_tree( doc_dir_name, project_name, vec![ "impl_traits_generic/index.html", "impl_traits_generic/all.html", "impl_traits_generic/foo/trait.Foo.html", "impl_traits_generic/bar/index.html", "impl_traits_generic/foo/index.html", "impl_traits_generic/foo/trait.Baz.html", "search.js", "impl_traits_generic/bar/struct.Bar.html", ], ); } fn assert_index_html(doc_path: &Path, project_name: &str, expect: &Expect) { let path_to_file = PathBuf::from(format!("{}/{}", project_name, IMPL_FOR)); check_file(doc_path, &path_to_file, expect); } fn assert_search_js(doc_path: &Path, expect: &Expect) { let path_to_file = PathBuf::from(JS_SEARCH_FILE_PATH); check_file(doc_path, &path_to_file, expect); } fn check_file(doc_path: &Path, path_to_file: &PathBuf, expect: &Expect) { let path = doc_path.join(path_to_file); let actual = std::fs::read_to_string(path.clone()) .unwrap_or_else(|_| panic!("failed to read file: {:?}", path)); expect.assert_eq(&actual) } fn assert_file_tree(doc_dir_name: &str, project_name: &str, expected_files: Vec<&str>) { let doc_root: PathBuf = format!("{}/{}/out/{}", DATA_DIR, project_name, doc_dir_name).into(); let expected = expected_files .iter() .map(PathBuf::from) .collect::>(); let files = get_relative_file_paths_set(doc_root.clone()); if files != expected { let diffs = files.symmetric_difference(&expected); assert_eq!( files, expected, "Symmetric Difference: {diffs:?} at {doc_root:?}" ); } }