use super::TypeScriptValidator;
use crate::snippets::types::{Language, Snippet, SnippetMetadata, SnippetStatus, SourceOrigin, ValidationLevel};
const TOOLCHAIN_TEST_TIMEOUT_SECS: u64 = 30;
const AMBIENT_TYPES: &str = "\
interface DataNode {
kind: string;
children: DataNode[];
}
interface ProcessResult {
data?: DataNode;
}
declare function process(source: string, config: unknown): ProcessResult;
";
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(body: &str) -> (SnippetStatus, Option<String>) {
let code = format!("{AMBIENT_TYPES}\nfunction main() {{\n{body}\n}}\n\nvoid main();\n");
TypeScriptValidator::validate_with_context(
&snippet(&code),
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("validation runs")
}
#[test]
fn the_guarded_accessor_shape_typechecks() {
if which::which("tsc").is_err() {
return;
}
let (status, diagnostics) = check(
" const result = process(\"source\", {});\n\
\x20 console.log(result.data);\n\
\x20 console.log(result.data?.kind);\n\
\x20 console.log(result.data?.children);",
);
assert_eq!(status, SnippetStatus::Pass, "diagnostics: {diagnostics:?}");
}
#[test]
fn the_unguarded_accessor_shape_fails_ts18048() {
if which::which("tsc").is_err() {
return;
}
let (status, diagnostics) = check(
" const result = process(\"source\", {});\n\
\x20 console.log(result.data);\n\
\x20 console.log(result.data.kind);\n\
\x20 console.log(result.data.children);",
);
assert_eq!(status, SnippetStatus::Fail);
let diagnostics = diagnostics.expect("a failed check carries diagnostics");
assert!(
diagnostics.contains("TS18048") || diagnostics.contains("TS2532"),
"must fail on the possibly-undefined access, not some unrelated error: {diagnostics}"
);
}
#[test]
fn a_required_field_typechecks_without_optional_chaining() {
if which::which("tsc").is_err() {
return;
}
let code = "\
interface DataNode {
kind: string;
}
interface ProcessResult {
data: DataNode;
}
declare function process(source: string, config: unknown): ProcessResult;
function main() {
const result = process(\"source\", {});
console.log(result.data.kind);
}
void main();
";
let (status, diagnostics) = TypeScriptValidator::validate_with_context(
&snippet(code),
ValidationLevel::TypeCheck,
TOOLCHAIN_TEST_TIMEOUT_SECS,
None,
)
.expect("validation runs");
assert_eq!(status, SnippetStatus::Pass, "diagnostics: {diagnostics:?}");
}