use super::TypeScriptValidator;
use crate::snippets::types::{Language, Snippet, SnippetMetadata, SnippetStatus, SourceOrigin, ValidationLevel};
use std::path::{Path, PathBuf};
const TOOLCHAIN_TEST_TIMEOUT_SECS: u64 = 30;
fn hand_written_node_path_snippet(markdown_path: PathBuf) -> Snippet {
Snippet {
id: None,
path: markdown_path.clone(),
language: Language::TypeScript,
title: None,
code: "import { join } from \"node:path\";\n\
const result: string = join(\"a\", \"b\");\n\
console.log(result);\n"
.to_string(),
start_line: 1,
block_index: 0,
annotation: None,
metadata: SnippetMetadata::default(),
source_origin: SourceOrigin {
path: markdown_path,
line: 1,
block_index: 0,
},
}
}
fn write_fake_types_node(root: &Path) {
let package = root.join("node_modules/@types/node");
std::fs::create_dir_all(&package).expect("fake @types/node directory");
std::fs::write(
package.join("package.json"),
r#"{"name":"@types/node","types":"index.d.ts"}"#,
)
.expect("fake @types/node package.json");
std::fs::write(
package.join("index.d.ts"),
"declare module \"node:path\" {\n function join(...paths: string[]): string;\n}\n",
)
.expect("fake @types/node index.d.ts");
}
fn write_real_markdown_snippet_file(root: &Path) -> PathBuf {
let markdown = root.join("docs/example.md");
std::fs::create_dir_all(markdown.parent().unwrap()).expect("docs directory");
std::fs::write(&markdown, "example doc source").expect("markdown source file");
markdown
}
#[test]
fn an_uncovered_node_builtin_fails_loudly_with_no_resolvable_project() {
if !super::tsc_is_runnable() {
return;
}
let project = tempfile::tempdir().expect("project root");
let markdown = write_real_markdown_snippet_file(project.path());
let snippet = hand_written_node_path_snippet(markdown);
let (status, diagnostics) = TypeScriptValidator::validate_with_context(
&snippet,
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("validation runs");
assert_eq!(status, SnippetStatus::Fail);
let diagnostics = diagnostics.expect("a failed check carries diagnostics");
assert!(
!diagnostics.trim().is_empty(),
"a failure must name what tsc actually reported, not an empty message"
);
}
#[test]
fn an_uncovered_node_builtin_typechecks_when_the_real_project_has_types_node_installed() {
if !super::tsc_is_runnable() {
return;
}
let project = tempfile::tempdir().expect("project root");
write_fake_types_node(project.path());
let markdown = write_real_markdown_snippet_file(project.path());
let snippet = hand_written_node_path_snippet(markdown);
let (status, diagnostics) = TypeScriptValidator::validate_with_context(
&snippet,
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("validation runs");
assert_eq!(status, SnippetStatus::Pass, "diagnostics: {diagnostics:?}");
}
#[test]
fn an_uncovered_node_builtin_typechecks_in_a_batch_when_the_real_project_has_types_node_installed() {
if !super::tsc_is_runnable() {
return;
}
let project = tempfile::tempdir().expect("project root");
write_fake_types_node(project.path());
let markdown = write_real_markdown_snippet_file(project.path());
let snippet = hand_written_node_path_snippet(markdown);
let results = TypeScriptValidator::validate_batch_with_context(
&[&snippet],
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("batch validation runs");
assert_eq!(results, vec![(SnippetStatus::Pass, None)], "{results:?}");
}