Skip to content

Commit 36c5461

Browse files
authored
feat(test): add support for type checking documentation (denoland#10521)
This commit adds support for type checking codeblocks in the JS doc comments.
1 parent c44e53a commit 36c5461

File tree

7 files changed

+188
-15
lines changed

7 files changed

+188
-15
lines changed

cli/ast.rs

+16
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,22 @@ impl ParsedModule {
304304
self.leading_comments.clone()
305305
}
306306

307+
/// Get the module's comments.
308+
pub fn get_comments(&self) -> Vec<Comment> {
309+
let mut comments = Vec::new();
310+
let (leading_comments, trailing_comments) = self.comments.borrow_all();
311+
312+
for value in leading_comments.values() {
313+
comments.append(&mut value.clone());
314+
}
315+
316+
for value in trailing_comments.values() {
317+
comments.append(&mut value.clone());
318+
}
319+
320+
comments
321+
}
322+
307323
/// Get a location for a given span within the module.
308324
pub fn get_location(&self, span: &Span) -> Location {
309325
self.source_map.lookup_char_pos(span.lo).into()

cli/flags.rs

+10
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub enum DenoSubcommand {
9696
script: String,
9797
},
9898
Test {
99+
doc: bool,
99100
no_run: bool,
100101
fail_fast: bool,
101102
quiet: bool,
@@ -984,6 +985,12 @@ fn test_subcommand<'a, 'b>() -> App<'a, 'b> {
984985
.help("Cache test modules, but don't run tests")
985986
.takes_value(false),
986987
)
988+
.arg(
989+
Arg::with_name("doc")
990+
.long("doc")
991+
.help("UNSTABLE: type check code blocks")
992+
.takes_value(false),
993+
)
987994
.arg(
988995
Arg::with_name("fail-fast")
989996
.long("fail-fast")
@@ -1667,6 +1674,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
16671674
runtime_args_parse(flags, matches, true, true);
16681675

16691676
let no_run = matches.is_present("no-run");
1677+
let doc = matches.is_present("doc");
16701678
let fail_fast = matches.is_present("fail-fast");
16711679
let allow_none = matches.is_present("allow-none");
16721680
let quiet = matches.is_present("quiet");
@@ -1711,6 +1719,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
17111719
flags.coverage_dir = matches.value_of("coverage").map(String::from);
17121720
flags.subcommand = DenoSubcommand::Test {
17131721
no_run,
1722+
doc,
17141723
fail_fast,
17151724
quiet,
17161725
include,
@@ -3357,6 +3366,7 @@ mod tests {
33573366
Flags {
33583367
subcommand: DenoSubcommand::Test {
33593368
no_run: true,
3369+
doc: false,
33603370
fail_fast: false,
33613371
filter: Some("- foo".to_string()),
33623372
allow_none: true,

cli/main.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ async fn test_command(
914914
flags: Flags,
915915
include: Option<Vec<String>>,
916916
no_run: bool,
917+
doc: bool,
917918
fail_fast: bool,
918919
quiet: bool,
919920
allow_none: bool,
@@ -924,6 +925,8 @@ async fn test_command(
924925
env::set_var("DENO_UNSTABLE_COVERAGE_DIR", coverage_dir);
925926
}
926927

928+
// TODO(caspervonb) move this chunk into tools::test_runner.
929+
927930
let program_state = ProgramState::build(flags.clone()).await?;
928931

929932
let include = include.unwrap_or_else(|| vec![".".to_string()]);
@@ -944,16 +947,29 @@ async fn test_command(
944947

945948
let paths_to_watch: Vec<_> = include.iter().map(PathBuf::from).collect();
946949

950+
// TODO(caspervonb) clean this up.
947951
let resolver = |changed: Option<Vec<PathBuf>>| {
948-
let test_modules_result =
949-
test_runner::collect_test_module_specifiers(include.clone(), &cwd);
952+
let doc_modules_result = test_runner::collect_test_module_specifiers(
953+
include.clone(),
954+
&cwd,
955+
fs_util::is_supported_ext,
956+
);
957+
958+
let test_modules_result = test_runner::collect_test_module_specifiers(
959+
include.clone(),
960+
&cwd,
961+
tools::test_runner::is_supported,
962+
);
963+
950964
let paths_to_watch = paths_to_watch.clone();
951965
let paths_to_watch_clone = paths_to_watch.clone();
952966

953967
let handler = handler.clone();
954968
let program_state = program_state.clone();
955969
let files_changed = changed.is_some();
956970
async move {
971+
let doc_modules = if doc { doc_modules_result? } else { Vec::new() };
972+
957973
let test_modules = test_modules_result?;
958974

959975
let mut paths_to_watch = paths_to_watch_clone;
@@ -976,6 +992,12 @@ async fn test_command(
976992
}
977993
let graph = builder.get_graph();
978994

995+
for specifier in doc_modules {
996+
if let Ok(path) = specifier.to_file_path() {
997+
paths_to_watch.push(path);
998+
}
999+
}
1000+
9791001
for specifier in test_modules {
9801002
fn get_dependencies<'a>(
9811003
graph: &'a module_graph::Graph,
@@ -1070,6 +1092,7 @@ async fn test_command(
10701092
program_state.clone(),
10711093
permissions.clone(),
10721094
lib.clone(),
1095+
modules_to_reload.clone(),
10731096
modules_to_reload,
10741097
no_run,
10751098
fail_fast,
@@ -1084,13 +1107,27 @@ async fn test_command(
10841107
)
10851108
.await?;
10861109
} else {
1087-
let test_modules =
1088-
test_runner::collect_test_module_specifiers(include, &cwd)?;
1110+
let doc_modules = if doc {
1111+
test_runner::collect_test_module_specifiers(
1112+
include.clone(),
1113+
&cwd,
1114+
fs_util::is_supported_ext,
1115+
)?
1116+
} else {
1117+
Vec::new()
1118+
};
1119+
1120+
let test_modules = test_runner::collect_test_module_specifiers(
1121+
include.clone(),
1122+
&cwd,
1123+
tools::test_runner::is_supported,
1124+
)?;
10891125

10901126
let failed = test_runner::run_tests(
10911127
program_state.clone(),
10921128
permissions,
10931129
lib,
1130+
doc_modules,
10941131
test_modules,
10951132
no_run,
10961133
fail_fast,
@@ -1235,6 +1272,7 @@ fn get_subcommand(
12351272
DenoSubcommand::Run { script } => run_command(flags, script).boxed_local(),
12361273
DenoSubcommand::Test {
12371274
no_run,
1275+
doc,
12381276
fail_fast,
12391277
quiet,
12401278
include,
@@ -1245,6 +1283,7 @@ fn get_subcommand(
12451283
flags,
12461284
include,
12471285
no_run,
1286+
doc,
12481287
fail_fast,
12491288
quiet,
12501289
allow_none,

cli/tests/integration_tests.rs

+6
Original file line numberDiff line numberDiff line change
@@ -2580,6 +2580,12 @@ mod integration {
25802580
output: "test/deno_test.out",
25812581
});
25822582

2583+
itest!(doc {
2584+
args: "test --doc --allow-all test/doc.ts",
2585+
exit_code: 1,
2586+
output: "test/doc.out",
2587+
});
2588+
25832589
itest!(allow_all {
25842590
args: "test --unstable --allow-all test/allow_all.ts",
25852591
exit_code: 0,

cli/tests/test/doc.out

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Check [WILDCARD]/doc.ts:2-7
2+
error: TS2367 [ERROR]: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
3+
console.assert(example() == 42);
4+
~~~~~~~~~~~~~~~
5+
at [WILDCARD]/doc.ts:2-7.ts:3:16

cli/tests/test/doc.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* ```
3+
* import { example } from "./doc.ts";
4+
*
5+
* console.assert(example() == 42);
6+
* ```
7+
*/
8+
export function example(): string {
9+
return "example";
10+
}

0 commit comments

Comments
 (0)