use super::TypeScriptValidator;
use crate::snippets::types::{Language, Snippet, SnippetMetadata, SnippetStatus, SourceOrigin, ValidationLevel};
const TOOLCHAIN_TEST_TIMEOUT_SECS: u64 = 30;
fn snippet(code: &str) -> Snippet {
Snippet {
id: None,
path: "snippet.ts".into(),
language: Language::TypeScript,
title: None,
code: code.into(),
start_line: 1,
block_index: 0,
annotation: None,
metadata: SnippetMetadata::default(),
source_origin: SourceOrigin {
path: "snippet.ts".into(),
line: 1,
block_index: 0,
},
}
}
fn check(code: &str) -> (SnippetStatus, Option<String>) {
TypeScriptValidator::validate_with_context(
&snippet(code),
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("validation runs")
}
#[test]
fn node_fs_promises_expression_form_typechecks_without_a_types_node_dependency() {
if which::which("tsc").is_err() {
return;
}
let (status, diagnostics) = check(
"async function main() {
const bytes = await (await import(\"node:fs/promises\")).readFile(\"fixture.bin\");
console.log(bytes.length);
}
void main();
",
);
assert_eq!(status, SnippetStatus::Pass, "diagnostics: {diagnostics:?}");
}
#[test]
fn node_fs_promises_assignment_form_typechecks_without_a_types_node_dependency() {
if which::which("tsc").is_err() {
return;
}
let (status, diagnostics) = check(
"async function main() {
const target: { bytes: Uint8Array } = { bytes: new Uint8Array() };
target.bytes = await (await import(\"node:fs/promises\")).readFile(\"fixture.bin\");
console.log(target.bytes.length);
}
void main();
",
);
assert_eq!(status, SnippetStatus::Pass, "diagnostics: {diagnostics:?}");
}
#[test]
fn node_fs_promises_typechecks_in_a_batch_without_a_types_node_dependency() {
if which::which("tsc").is_err() {
return;
}
let uses_node_import = snippet(
"async function main() {
const bytes = await (await import(\"node:fs/promises\")).readFile(\"fixture.bin\");
console.log(bytes.length);
}
void main();
",
);
let results = TypeScriptValidator::validate_batch_with_context(
&[&uses_node_import],
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("batch validation runs");
assert_eq!(results, vec![(SnippetStatus::Pass, None)], "{results:?}");
}
#[test]
fn a_snippet_without_a_node_import_is_unaffected_by_the_ambient_declaration() {
if which::which("tsc").is_err() {
return;
}
let (status, diagnostics) = check(
"function add(a: number, b: number): number {
return a + b;
}
console.log(add(1, 2));
",
);
assert_eq!(status, SnippetStatus::Pass, "diagnostics: {diagnostics:?}");
}
#[test]
fn an_unrelated_type_error_still_fails_with_the_ambient_declaration_present() {
if which::which("tsc").is_err() {
return;
}
let (status, diagnostics) = check("const value: number = \"not a number\";\nconsole.log(value);\n");
assert_eq!(status, SnippetStatus::Fail);
let diagnostics = diagnostics.expect("a failed check carries diagnostics");
assert!(
diagnostics.contains("TS2322"),
"must fail on the real type error, not something related to the ambient declaration: {diagnostics}"
);
}