summaryrefslogtreecommitdiff
path: root/cli/tests
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tests')
-rw-r--r--cli/tests/bench_tests.rs218
-rw-r--r--cli/tests/bundle_tests.rs478
-rw-r--r--cli/tests/cache_tests.rs102
-rw-r--r--cli/tests/cert_tests.rs321
-rw-r--r--cli/tests/check_tests.rs224
-rw-r--r--cli/tests/compile_tests.rs569
-rw-r--r--cli/tests/coverage_tests.rs382
-rw-r--r--cli/tests/doc_tests.rs79
-rw-r--r--cli/tests/eval_tests.rs86
-rw-r--r--cli/tests/flags_tests.rs50
-rw-r--r--cli/tests/fmt_tests.rs257
-rw-r--r--cli/tests/info_tests.rs135
-rw-r--r--cli/tests/init_tests.rs209
-rw-r--r--cli/tests/inspector_tests.rs1378
-rw-r--r--cli/tests/install_tests.rs247
-rw-r--r--cli/tests/integration/bench_tests.rs212
-rw-r--r--cli/tests/integration/bundle_tests.rs472
-rw-r--r--cli/tests/integration/cache_tests.rs97
-rw-r--r--cli/tests/integration/cert_tests.rs315
-rw-r--r--cli/tests/integration/check_tests.rs219
-rw-r--r--cli/tests/integration/compile_tests.rs566
-rw-r--r--cli/tests/integration/coverage_tests.rs376
-rw-r--r--cli/tests/integration/doc_tests.rs72
-rw-r--r--cli/tests/integration/eval_tests.rs79
-rw-r--r--cli/tests/integration/flags_tests.rs44
-rw-r--r--cli/tests/integration/fmt_tests.rs247
-rw-r--r--cli/tests/integration/info_tests.rs129
-rw-r--r--cli/tests/integration/init_tests.rs205
-rw-r--r--cli/tests/integration/inspector_tests.rs1372
-rw-r--r--cli/tests/integration/install_tests.rs243
-rw-r--r--cli/tests/integration/js_unit_tests.rs39
-rw-r--r--cli/tests/integration/lint_tests.rs132
-rw-r--r--cli/tests/integration/lsp_tests.rs6434
-rw-r--r--cli/tests/integration/mod.rs59
-rw-r--r--cli/tests/integration/npm_tests.rs1544
-rw-r--r--cli/tests/integration/repl_tests.rs959
-rw-r--r--cli/tests/integration/run_tests.rs3724
-rw-r--r--cli/tests/integration/task_tests.rs130
-rw-r--r--cli/tests/integration/test_tests.rs454
-rw-r--r--cli/tests/integration/upgrade_tests.rs193
-rw-r--r--cli/tests/integration/vendor_tests.rs574
-rw-r--r--cli/tests/integration/watcher_tests.rs1217
-rw-r--r--cli/tests/integration/worker_tests.rs110
-rw-r--r--cli/tests/integration_tests.rs6
-rw-r--r--cli/tests/js_unit_tests.rs45
-rw-r--r--cli/tests/lint_tests.rs139
-rw-r--r--cli/tests/lsp_tests.rs6445
-rw-r--r--cli/tests/npm_tests.rs1549
-rw-r--r--cli/tests/repl_tests.rs966
-rw-r--r--cli/tests/run_tests.rs3745
-rw-r--r--cli/tests/task_tests.rs136
-rw-r--r--cli/tests/test_tests.rs459
-rw-r--r--cli/tests/upgrade_tests.rs197
-rw-r--r--cli/tests/vendor_tests.rs583
-rw-r--r--cli/tests/watcher_tests.rs1230
-rw-r--r--cli/tests/worker_tests.rs116
56 files changed, 20223 insertions, 20345 deletions
diff --git a/cli/tests/bench_tests.rs b/cli/tests/bench_tests.rs
deleted file mode 100644
index 71cba171d..000000000
--- a/cli/tests/bench_tests.rs
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use deno_core::url::Url;
-use test_util as util;
-
-mod bench {
- use super::*;
-
- itest!(overloads {
- args: "bench bench/overloads.ts",
- exit_code: 0,
- output: "bench/overloads.out",
- });
-
- itest!(meta {
- args: "bench bench/meta.ts",
- exit_code: 0,
- output: "bench/meta.out",
- });
-
- itest!(pass {
- args: "bench bench/pass.ts",
- exit_code: 0,
- output: "bench/pass.out",
- });
-
- itest!(ignore {
- args: "bench bench/ignore.ts",
- exit_code: 0,
- output: "bench/ignore.out",
- });
-
- itest!(ignore_permissions {
- args: "bench bench/ignore_permissions.ts",
- exit_code: 0,
- output: "bench/ignore_permissions.out",
- });
-
- itest!(fail {
- args: "bench bench/fail.ts",
- exit_code: 1,
- output: "bench/fail.out",
- });
-
- itest!(collect {
- args: "bench --ignore=bench/collect/ignore bench/collect",
- exit_code: 0,
- output: "bench/collect.out",
- });
-
- itest!(load_unload {
- args: "bench bench/load_unload.ts",
- exit_code: 0,
- output: "bench/load_unload.out",
- });
-
- itest!(interval {
- args: "bench bench/interval.ts",
- exit_code: 0,
- output: "bench/interval.out",
- });
-
- itest!(quiet {
- args: "bench --quiet bench/quiet.ts",
- exit_code: 0,
- output: "bench/quiet.out",
- });
-
- itest!(only {
- args: "bench bench/only.ts",
- exit_code: 1,
- output: "bench/only.out",
- });
-
- itest!(multifile_summary {
- args: "bench bench/group_baseline.ts bench/pass.ts bench/group_baseline.ts",
- exit_code: 0,
- output: "bench/multifile_summary.out",
- });
-
- itest!(no_check {
- args: "bench --no-check bench/no_check.ts",
- exit_code: 1,
- output: "bench/no_check.out",
- });
-
- itest!(allow_all {
- args: "bench --allow-all bench/allow_all.ts",
- exit_code: 0,
- output: "bench/allow_all.out",
- });
-
- itest!(allow_none {
- args: "bench bench/allow_none.ts",
- exit_code: 1,
- output: "bench/allow_none.out",
- });
-
- itest!(exit_sanitizer {
- args: "bench bench/exit_sanitizer.ts",
- output: "bench/exit_sanitizer.out",
- exit_code: 1,
- });
-
- itest!(clear_timeout {
- args: "bench bench/clear_timeout.ts",
- exit_code: 0,
- output: "bench/clear_timeout.out",
- });
-
- itest!(finally_timeout {
- args: "bench bench/finally_timeout.ts",
- exit_code: 1,
- output: "bench/finally_timeout.out",
- });
-
- itest!(group_baseline {
- args: "bench bench/group_baseline.ts",
- exit_code: 0,
- output: "bench/group_baseline.out",
- });
-
- itest!(unresolved_promise {
- args: "bench bench/unresolved_promise.ts",
- exit_code: 1,
- output: "bench/unresolved_promise.out",
- });
-
- itest!(unhandled_rejection {
- args: "bench bench/unhandled_rejection.ts",
- exit_code: 1,
- output: "bench/unhandled_rejection.out",
- });
-
- itest!(filter {
- args: "bench --filter=foo bench/filter",
- exit_code: 0,
- output: "bench/filter.out",
- });
-
- itest!(no_prompt_by_default {
- args: "bench --quiet bench/no_prompt_by_default.ts",
- exit_code: 1,
- output: "bench/no_prompt_by_default.out",
- });
-
- itest!(no_prompt_with_denied_perms {
- args: "bench --quiet --allow-read bench/no_prompt_with_denied_perms.ts",
- exit_code: 1,
- output: "bench/no_prompt_with_denied_perms.out",
- });
-
- itest!(check_local_by_default {
- args: "bench --quiet bench/check_local_by_default.ts",
- output: "bench/check_local_by_default.out",
- http_server: true,
- });
-
- itest!(check_local_by_default2 {
- args: "bench --quiet bench/check_local_by_default2.ts",
- output: "bench/check_local_by_default2.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(bench_with_config {
- args: "bench --config bench/collect/deno.jsonc bench/collect",
- exit_code: 0,
- output: "bench/collect.out",
- });
-
- itest!(bench_with_config2 {
- args: "bench --config bench/collect/deno2.jsonc bench/collect",
- exit_code: 0,
- output: "bench/collect2.out",
- });
-
- itest!(bench_with_malformed_config {
- args: "bench --config bench/collect/deno.malformed.jsonc",
- exit_code: 1,
- output: "bench/collect_with_malformed_config.out",
- });
-
- #[test]
- fn recursive_permissions_pledge() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bench")
- .arg("bench/recursive_permissions_pledge.js")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert!(String::from_utf8(output.stderr).unwrap().contains(
- "pledge test permissions called before restoring previous pledge"
- ));
- }
-
- #[test]
- fn file_protocol() {
- let file_url =
- Url::from_file_path(util::testdata_path().join("bench/file_protocol.ts"))
- .unwrap()
- .to_string();
-
- (util::CheckOutputIntegrationTest {
- args_vec: vec!["bench", &file_url],
- exit_code: 0,
- output: "bench/file_protocol.out",
- ..Default::default()
- })
- .run();
- }
-}
diff --git a/cli/tests/bundle_tests.rs b/cli/tests/bundle_tests.rs
deleted file mode 100644
index 75b345a9f..000000000
--- a/cli/tests/bundle_tests.rs
+++ /dev/null
@@ -1,478 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-use test_util::assert_contains;
-use test_util::assert_ends_with;
-use test_util::TempDir;
-
-mod bundle {
- use super::*;
-
- #[test]
- fn bundle_exports() {
- // First we have to generate a bundle of some module that has exports.
- let mod1 = util::testdata_path().join("subdir/mod1.ts");
- assert!(mod1.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("mod1.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(mod1)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- // Now we try to use that bundle from another module.
- let test = t.path().join("test.js");
- std::fs::write(
- &test,
- "
- import { printHello3 } from \"./mod1.bundle.js\";
- printHello3(); ",
- )
- .unwrap();
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&test)
- .output()
- .unwrap();
- // check the output of the test.ts program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_exports_no_check() {
- // First we have to generate a bundle of some module that has exports.
- let mod1 = util::testdata_path().join("subdir/mod1.ts");
- assert!(mod1.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("mod1.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(mod1)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- // Now we try to use that bundle from another module.
- let test = t.path().join("test.js");
- std::fs::write(
- &test,
- "
- import { printHello3 } from \"./mod1.bundle.js\";
- printHello3(); ",
- )
- .unwrap();
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&test)
- .output()
- .unwrap();
- // check the output of the test.ts program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_circular() {
- // First we have to generate a bundle of some module that has exports.
- let circular1_path = util::testdata_path().join("subdir/circular1.ts");
- assert!(circular1_path.is_file());
- let t = TempDir::new();
- let bundle_path = t.path().join("circular1.bundle.js");
-
- // run this twice to ensure it works even when cached
- for _ in 0..2 {
- let mut deno = util::deno_cmd_with_deno_dir(&t)
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(&circular1_path)
- .arg(&bundle_path)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle_path.is_file());
- }
-
- let output = util::deno_cmd_with_deno_dir(&t)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&bundle_path)
- .output()
- .unwrap();
- // check the output of the the bundle program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "f2\nf1",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_single_module() {
- // First we have to generate a bundle of some module that has exports.
- let single_module = util::testdata_path().join("subdir/single_module.ts");
- assert!(single_module.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("single_module.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(single_module)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&bundle)
- .output()
- .unwrap();
- // check the output of the the bundle program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello world!",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_tla() {
- // First we have to generate a bundle of some module that has exports.
- let tla_import = util::testdata_path().join("subdir/tla.ts");
- assert!(tla_import.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("tla.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(tla_import)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- // Now we try to use that bundle from another module.
- let test = t.path().join("test.js");
- std::fs::write(
- &test,
- "
- import { foo } from \"./tla.bundle.js\";
- console.log(foo); ",
- )
- .unwrap();
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&test)
- .output()
- .unwrap();
- // check the output of the test.ts program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_js() {
- // First we have to generate a bundle of some module that has exports.
- let mod6 = util::testdata_path().join("subdir/mod6.js");
- assert!(mod6.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("mod6.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(mod6)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&bundle)
- .output()
- .unwrap();
- // check that nothing went to stderr
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_dynamic_import() {
- let _g = util::http_server();
- let dynamic_import = util::testdata_path().join("bundle/dynamic_import.ts");
- assert!(dynamic_import.is_file());
- let t = TempDir::new();
- let output_path = t.path().join("bundle_dynamic_import.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(dynamic_import)
- .arg(&output_path)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(output_path.is_file());
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-net")
- .arg("--quiet")
- .arg(&output_path)
- .output()
- .unwrap();
- // check the output of the test.ts program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_import_map() {
- let import = util::testdata_path().join("bundle/import_map/main.ts");
- let import_map_path =
- util::testdata_path().join("bundle/import_map/import_map.json");
- assert!(import.is_file());
- let t = TempDir::new();
- let output_path = t.path().join("import_map.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg("--import-map")
- .arg(import_map_path)
- .arg(import)
- .arg(&output_path)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(output_path.is_file());
-
- // Now we try to use that bundle from another module.
- let test = t.path().join("test.js");
- std::fs::write(
- &test,
- "
- import { printHello3 } from \"./import_map.bundle.js\";
- printHello3(); ",
- )
- .unwrap();
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg(&test)
- .output()
- .unwrap();
- // check the output of the test.ts program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_import_map_no_check() {
- let import = util::testdata_path().join("bundle/import_map/main.ts");
- let import_map_path =
- util::testdata_path().join("bundle/import_map/import_map.json");
- assert!(import.is_file());
- let t = TempDir::new();
- let output_path = t.path().join("import_map.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg("--import-map")
- .arg(import_map_path)
- .arg(import)
- .arg(&output_path)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(output_path.is_file());
-
- // Now we try to use that bundle from another module.
- let test = t.path().join("test.js");
- std::fs::write(
- &test,
- "
- import { printHello3 } from \"./import_map.bundle.js\";
- printHello3(); ",
- )
- .unwrap();
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&test)
- .output()
- .unwrap();
- // check the output of the test.ts program.
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "Hello",
- );
- assert_eq!(output.stderr, b"");
- }
-
- #[test]
- fn bundle_json_module() {
- // First we have to generate a bundle of some module that has exports.
- let mod7 = util::testdata_path().join("subdir/mod7.js");
- assert!(mod7.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("mod7.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(mod7)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&bundle)
- .output()
- .unwrap();
- // check that nothing went to stderr
- assert_eq!(output.stderr, b"");
- // ensure the output looks right
- assert_contains!(String::from_utf8(output.stdout).unwrap(), "with space",);
- }
-
- #[test]
- fn bundle_json_module_escape_sub() {
- // First we have to generate a bundle of some module that has exports.
- let mod8 = util::testdata_path().join("subdir/mod8.js");
- assert!(mod8.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("mod8.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(mod8)
- .arg(&bundle)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
- assert!(bundle.is_file());
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&bundle)
- .output()
- .unwrap();
- // check that nothing went to stderr
- assert_eq!(output.stderr, b"");
- // make sure the output looks right and the escapes were effective
- assert_contains!(
- String::from_utf8(output.stdout).unwrap(),
- "${globalThis}`and string literal`",
- );
- }
-
- itest!(lockfile_check_error {
- args: "bundle --lock=bundle/lockfile/check_error.json http://127.0.0.1:4545/subdir/mod1.ts",
- output: "bundle/lockfile/check_error.out",
- exit_code: 10,
- http_server: true,
-});
-
- itest!(bundle {
- args: "bundle subdir/mod1.ts",
- output: "bundle/bundle.test.out",
- });
-
- itest!(bundle_jsx {
- args: "bundle run/jsx_import_from_ts.ts",
- output: "bundle/jsx.out",
- });
-
- itest!(error_bundle_with_bare_import {
- args: "bundle bundle/bare_imports/error_with_bare_import.ts",
- output: "bundle/bare_imports/error_with_bare_import.ts.out",
- exit_code: 1,
- });
-
- itest!(ts_decorators_bundle {
- args: "bundle bundle/decorators/ts_decorators.ts",
- output: "bundle/decorators/ts_decorators.out",
- });
-
- itest!(bundle_export_specifier_with_alias {
- args: "bundle bundle/file_tests-fixture16.ts",
- output: "bundle/fixture16.out",
- });
-
- itest!(bundle_ignore_directives {
- args: "bundle subdir/mod1.ts",
- output: "bundle/ignore_directives.test.out",
- });
-
- itest!(check_local_by_default_no_errors {
- args: "bundle --quiet bundle/check_local_by_default/no_errors.ts",
- output: "bundle/check_local_by_default/no_errors.out",
- http_server: true,
- });
-
- itest!(check_local_by_default_type_error {
- args: "bundle --quiet bundle/check_local_by_default/type_error.ts",
- output: "bundle/check_local_by_default/type_error.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(bundle_shebang_file {
- args: "bundle subdir/shebang_file.js",
- output: "bundle/shebang_file.bundle.out",
- });
-}
diff --git a/cli/tests/cache_tests.rs b/cli/tests/cache_tests.rs
deleted file mode 100644
index 8e7f20fde..000000000
--- a/cli/tests/cache_tests.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-mod cache {
- use super::*;
- itest!(_036_import_map_fetch {
- args:
- "cache --quiet --reload --import-map=import_maps/import_map.json import_maps/test.ts",
- output: "cache/036_import_map_fetch.out",
- });
-
- itest!(_037_fetch_multiple {
- args: "cache --reload --check=all run/fetch/test.ts run/fetch/other.ts",
- http_server: true,
- output: "cache/037_fetch_multiple.out",
- });
-
- itest!(_095_cache_with_bare_import {
- args: "cache cache/095_cache_with_bare_import.ts",
- output: "cache/095_cache_with_bare_import.ts.out",
- exit_code: 1,
- });
-
- itest!(cache_extensionless {
- args: "cache --reload --check=all http://localhost:4545/subdir/no_js_ext",
- output: "cache/cache_extensionless.out",
- http_server: true,
- });
-
- itest!(cache_random_extension {
- args:
- "cache --reload --check=all http://localhost:4545/subdir/no_js_ext@1.0.0",
- output: "cache/cache_random_extension.out",
- http_server: true,
- });
-
- itest!(performance_stats {
- args: "cache --reload --check=all --log-level debug run/002_hello.ts",
- output: "cache/performance_stats.out",
- });
-
- itest!(redirect_cache {
- http_server: true,
- args:
- "cache --reload --check=all http://localhost:4548/subdir/redirects/a.ts",
- output: "cache/redirect_cache.out",
- });
-
- itest!(ignore_require {
- args: "cache --reload --no-check cache/ignore_require.js",
- output_str: Some(""),
- exit_code: 0,
- });
-
- // This test only runs on linux, because it hardcodes the XDG_CACHE_HOME env var
- // which is only used on linux.
- #[cfg(target_os = "linux")]
- #[test]
- fn relative_home_dir() {
- use test_util as util;
- use test_util::TempDir;
-
- let deno_dir = TempDir::new_in(&util::testdata_path());
- let path = deno_dir.path().strip_prefix(util::testdata_path()).unwrap();
-
- let mut deno_cmd = util::deno_cmd();
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .env("XDG_CACHE_HOME", path)
- .env_remove("HOME")
- .env_remove("DENO_DIR")
- .arg("cache")
- .arg("--reload")
- .arg("--no-check")
- .arg("run/002_hello.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"");
- }
-
- itest!(check_local_by_default {
- args: "cache --quiet cache/check_local_by_default.ts",
- output: "cache/check_local_by_default.out",
- http_server: true,
- });
-
- itest!(check_local_by_default2 {
- args: "cache --quiet cache/check_local_by_default2.ts",
- output: "cache/check_local_by_default2.out",
- http_server: true,
- });
-
- itest!(json_import {
- // should not error
- args: "cache --quiet cache/json_import/main.ts",
- });
-}
diff --git a/cli/tests/cert_tests.rs b/cli/tests/cert_tests.rs
deleted file mode 100644
index c06c1afb9..000000000
--- a/cli/tests/cert_tests.rs
+++ /dev/null
@@ -1,321 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use deno_runtime::deno_net::ops_tls::TlsStream;
-use deno_runtime::deno_tls::rustls;
-use deno_runtime::deno_tls::rustls_pemfile;
-use std::io::BufReader;
-use std::io::Cursor;
-use std::io::Read;
-use std::process::Command;
-use std::sync::Arc;
-use test_util as util;
-use test_util::TempDir;
-use tokio::task::LocalSet;
-
-mod cert {
- use super::*;
- itest_flaky!(cafile_url_imports {
- args:
- "run --quiet --reload --cert tls/RootCA.pem cert/cafile_url_imports.ts",
- output: "cert/cafile_url_imports.ts.out",
- http_server: true,
- });
-
- itest_flaky!(cafile_ts_fetch {
- args:
- "run --quiet --reload --allow-net --cert tls/RootCA.pem cert/cafile_ts_fetch.ts",
- output: "cert/cafile_ts_fetch.ts.out",
- http_server: true,
- });
-
- itest_flaky!(cafile_eval {
- args: "eval --cert tls/RootCA.pem fetch('https://localhost:5545/cert/cafile_ts_fetch.ts.out').then(r=>r.text()).then(t=>console.log(t.trimEnd()))",
- output: "cert/cafile_ts_fetch.ts.out",
- http_server: true,
- });
-
- itest_flaky!(cafile_info {
- args:
- "info --quiet --cert tls/RootCA.pem https://localhost:5545/cert/cafile_info.ts",
- output: "cert/cafile_info.ts.out",
- http_server: true,
- });
-
- itest_flaky!(cafile_url_imports_unsafe_ssl {
- args: "run --quiet --reload --unsafely-ignore-certificate-errors=localhost cert/cafile_url_imports.ts",
- output: "cert/cafile_url_imports_unsafe_ssl.ts.out",
- http_server: true,
- });
-
- itest_flaky!(cafile_ts_fetch_unsafe_ssl {
- args:
- "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors cert/cafile_ts_fetch.ts",
- output: "cert/cafile_ts_fetch_unsafe_ssl.ts.out",
- http_server: true,
- });
-
- itest!(deno_land_unsafe_ssl {
- args:
- "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=deno.land cert/deno_land_unsafe_ssl.ts",
- output: "cert/deno_land_unsafe_ssl.ts.out",
- });
-
- itest!(ip_address_unsafe_ssl {
- args:
- "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=1.1.1.1 cert/ip_address_unsafe_ssl.ts",
- output: "cert/ip_address_unsafe_ssl.ts.out",
- });
-
- itest!(localhost_unsafe_ssl {
- args:
- "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=deno.land cert/cafile_url_imports.ts",
- output: "cert/localhost_unsafe_ssl.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- #[flaky_test::flaky_test]
- fn cafile_env_fetch() {
- use deno_core::url::Url;
- let _g = util::http_server();
- let deno_dir = TempDir::new();
- let module_url =
- Url::parse("https://localhost:5545/cert/cafile_url_imports.ts").unwrap();
- let cafile = util::testdata_path().join("tls/RootCA.pem");
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .env("DENO_CERT", cafile)
- .current_dir(util::testdata_path())
- .arg("cache")
- .arg(module_url.to_string())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
- }
-
- #[flaky_test::flaky_test]
- fn cafile_fetch() {
- use deno_core::url::Url;
- let _g = util::http_server();
- let deno_dir = TempDir::new();
- let module_url =
- Url::parse("http://localhost:4545/cert/cafile_url_imports.ts").unwrap();
- let cafile = util::testdata_path().join("tls/RootCA.pem");
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("cache")
- .arg("--cert")
- .arg(cafile)
- .arg(module_url.to_string())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
- let out = std::str::from_utf8(&output.stdout).unwrap();
- assert_eq!(out, "");
- }
-
- #[flaky_test::flaky_test]
- fn cafile_install_remote_module() {
- let _g = util::http_server();
- let temp_dir = TempDir::new();
- let bin_dir = temp_dir.path().join("bin");
- std::fs::create_dir(&bin_dir).unwrap();
- let deno_dir = TempDir::new();
- let cafile = util::testdata_path().join("tls/RootCA.pem");
-
- let install_output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("install")
- .arg("--cert")
- .arg(cafile)
- .arg("--root")
- .arg(temp_dir.path())
- .arg("-n")
- .arg("echo_test")
- .arg("https://localhost:5545/echo.ts")
- .output()
- .expect("Failed to spawn script");
- println!("{}", std::str::from_utf8(&install_output.stdout).unwrap());
- eprintln!("{}", std::str::from_utf8(&install_output.stderr).unwrap());
- assert!(install_output.status.success());
-
- let mut echo_test_path = bin_dir.join("echo_test");
- if cfg!(windows) {
- echo_test_path = echo_test_path.with_extension("cmd");
- }
- assert!(echo_test_path.exists());
-
- let output = Command::new(echo_test_path)
- .current_dir(temp_dir.path())
- .arg("foo")
- .env("PATH", util::target_dir())
- .output()
- .expect("failed to spawn script");
- let stdout = std::str::from_utf8(&output.stdout).unwrap().trim();
- assert!(stdout.ends_with("foo"));
- }
-
- #[flaky_test::flaky_test]
- fn cafile_bundle_remote_exports() {
- let _g = util::http_server();
-
- // First we have to generate a bundle of some remote module that has exports.
- let mod1 = "https://localhost:5545/subdir/mod1.ts";
- let cafile = util::testdata_path().join("tls/RootCA.pem");
- let t = TempDir::new();
- let bundle = t.path().join("mod1.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg("--cert")
- .arg(cafile)
- .arg(mod1)
- .arg(&bundle)
- .spawn()
- .expect("failed to spawn script");
- let status = deno.wait().expect("failed to wait for the child process");
- assert!(status.success());
- assert!(bundle.is_file());
-
- // Now we try to use that bundle from another module.
- let test = t.path().join("test.js");
- std::fs::write(
- &test,
- "
- import { printHello3 } from \"./mod1.bundle.js\";
- printHello3(); ",
- )
- .expect("error writing file");
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg(&test)
- .output()
- .expect("failed to spawn script");
- // check the output of the test.ts program.
- assert!(std::str::from_utf8(&output.stdout)
- .unwrap()
- .trim()
- .ends_with("Hello"));
- assert_eq!(output.stderr, b"");
- }
-
- #[tokio::test]
- async fn listen_tls_alpn() {
- // TLS streams require the presence of an ambient local task set to gracefully
- // close dropped connections in the background.
- LocalSet::new()
- .run_until(async {
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--unstable")
- .arg("--quiet")
- .arg("--allow-net")
- .arg("--allow-read")
- .arg("./cert/listen_tls_alpn.ts")
- .arg("4504")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let stdout = child.stdout.as_mut().unwrap();
- let mut msg = [0; 5];
- let read = stdout.read(&mut msg).unwrap();
- assert_eq!(read, 5);
- assert_eq!(&msg, b"READY");
-
- let mut reader = &mut BufReader::new(Cursor::new(include_bytes!(
- "./testdata/tls/RootCA.crt"
- )));
- let certs = rustls_pemfile::certs(&mut reader).unwrap();
- let mut root_store = rustls::RootCertStore::empty();
- root_store.add_parsable_certificates(&certs);
- let mut cfg = rustls::ClientConfig::builder()
- .with_safe_defaults()
- .with_root_certificates(root_store)
- .with_no_client_auth();
- cfg.alpn_protocols.push(b"foobar".to_vec());
- let cfg = Arc::new(cfg);
-
- let hostname = rustls::ServerName::try_from("localhost").unwrap();
-
- let tcp_stream = tokio::net::TcpStream::connect("localhost:4504")
- .await
- .unwrap();
- let mut tls_stream =
- TlsStream::new_client_side(tcp_stream, cfg, hostname);
-
- tls_stream.handshake().await.unwrap();
-
- let (_, rustls_connection) = tls_stream.get_ref();
- let alpn = rustls_connection.alpn_protocol().unwrap();
- assert_eq!(alpn, b"foobar");
-
- let status = child.wait().unwrap();
- assert!(status.success());
- })
- .await;
- }
-
- #[tokio::test]
- async fn listen_tls_alpn_fail() {
- // TLS streams require the presence of an ambient local task set to gracefully
- // close dropped connections in the background.
- LocalSet::new()
- .run_until(async {
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--unstable")
- .arg("--quiet")
- .arg("--allow-net")
- .arg("--allow-read")
- .arg("./cert/listen_tls_alpn_fail.ts")
- .arg("4505")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let stdout = child.stdout.as_mut().unwrap();
- let mut msg = [0; 5];
- let read = stdout.read(&mut msg).unwrap();
- assert_eq!(read, 5);
- assert_eq!(&msg, b"READY");
-
- let mut reader = &mut BufReader::new(Cursor::new(include_bytes!(
- "./testdata/tls/RootCA.crt"
- )));
- let certs = rustls_pemfile::certs(&mut reader).unwrap();
- let mut root_store = rustls::RootCertStore::empty();
- root_store.add_parsable_certificates(&certs);
- let mut cfg = rustls::ClientConfig::builder()
- .with_safe_defaults()
- .with_root_certificates(root_store)
- .with_no_client_auth();
- cfg.alpn_protocols.push(b"boofar".to_vec());
- let cfg = Arc::new(cfg);
-
- let hostname = rustls::ServerName::try_from("localhost").unwrap();
-
- let tcp_stream = tokio::net::TcpStream::connect("localhost:4505")
- .await
- .unwrap();
- let mut tls_stream =
- TlsStream::new_client_side(tcp_stream, cfg, hostname);
-
- tls_stream.handshake().await.unwrap_err();
-
- let (_, rustls_connection) = tls_stream.get_ref();
- assert!(rustls_connection.alpn_protocol().is_none());
-
- let status = child.wait().unwrap();
- assert!(status.success());
- })
- .await;
- }
-}
diff --git a/cli/tests/check_tests.rs b/cli/tests/check_tests.rs
deleted file mode 100644
index 8cbccf8aa..000000000
--- a/cli/tests/check_tests.rs
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use std::process::Command;
-use std::process::Stdio;
-use test_util as util;
-use util::TempDir;
-
-mod check {
- use super::*;
- itest!(_095_check_with_bare_import {
- args: "check cache/095_cache_with_bare_import.ts",
- output: "cache/095_cache_with_bare_import.ts.out",
- exit_code: 1,
- });
-
- itest!(check_extensionless {
- args: "check --reload http://localhost:4545/subdir/no_js_ext",
- output: "cache/cache_extensionless.out",
- http_server: true,
- });
-
- itest!(check_random_extension {
- args: "check --reload http://localhost:4545/subdir/no_js_ext@1.0.0",
- output: "cache/cache_random_extension.out",
- http_server: true,
- });
-
- itest!(check_all {
- args: "check --quiet --all check/check_all.ts",
- output: "check/check_all.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(check_all_local {
- args: "check --quiet check/check_all.ts",
- output_str: Some(""),
- http_server: true,
- });
-
- itest!(module_detection_force {
- args: "check --quiet check/module_detection_force/main.ts",
- output_str: Some(""),
- });
-
- // Regression test for https://github.com/denoland/deno/issues/14937.
- itest!(declaration_header_file_with_no_exports {
- args: "check --quiet check/declaration_header_file_with_no_exports.ts",
- output_str: Some(""),
- });
-
- itest!(check_npm_install_diagnostics {
- args: "check --quiet check/npm_install_diagnostics/main.ts",
- output: "check/npm_install_diagnostics/main.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 1,
- });
-
- itest!(check_export_equals_declaration_file {
- args: "check --quiet check/export_equals_declaration_file/main.ts",
- exit_code: 0,
- });
-
- itest!(check_static_response_json {
- args: "check --quiet check/response_json.ts",
- exit_code: 0,
- });
-
- itest!(check_no_error_truncation {
- args: "check --quiet check/no_error_truncation/main.ts --config check/no_error_truncation/deno.json",
- output: "check/no_error_truncation/main.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 1,
- });
-
- #[test]
- fn cache_switching_config_then_no_config() {
- let deno_dir = util::new_deno_dir();
- assert!(does_type_checking(&deno_dir, true));
- assert!(does_type_checking(&deno_dir, false));
-
- // should now not do type checking even when it changes
- // configs because it previously did
- assert!(!does_type_checking(&deno_dir, true));
- assert!(!does_type_checking(&deno_dir, false));
-
- fn does_type_checking(deno_dir: &util::TempDir, with_config: bool) -> bool {
- let mut cmd = util::deno_cmd_with_deno_dir(deno_dir);
- cmd
- .current_dir(util::testdata_path())
- .stderr(Stdio::piped())
- .arg("check")
- .arg("check/cache_config_on_off/main.ts");
- if with_config {
- cmd
- .arg("--config")
- .arg("check/cache_config_on_off/deno.json");
- }
- let output = cmd.spawn().unwrap().wait_with_output().unwrap();
- assert!(output.status.success());
-
- let stderr = std::str::from_utf8(&output.stderr).unwrap();
- stderr.contains("Check")
- }
- }
-
- #[test]
- fn reload_flag() {
- // should do type checking whenever someone specifies --reload
- let deno_dir = util::new_deno_dir();
- assert!(does_type_checking(&deno_dir, false));
- assert!(!does_type_checking(&deno_dir, false));
- assert!(does_type_checking(&deno_dir, true));
- assert!(does_type_checking(&deno_dir, true));
- assert!(!does_type_checking(&deno_dir, false));
-
- fn does_type_checking(deno_dir: &util::TempDir, reload: bool) -> bool {
- let mut cmd = util::deno_cmd_with_deno_dir(deno_dir);
- cmd
- .current_dir(util::testdata_path())
- .stderr(Stdio::piped())
- .arg("check")
- .arg("check/cache_config_on_off/main.ts");
- if reload {
- cmd.arg("--reload");
- }
- let output = cmd.spawn().unwrap().wait_with_output().unwrap();
- assert!(output.status.success());
-
- let stderr = std::str::from_utf8(&output.stderr).unwrap();
- stderr.contains("Check")
- }
- }
-
- #[test]
- fn typecheck_declarations_ns() {
- let output = util::deno_cmd()
- .arg("test")
- .arg("--doc")
- .arg(util::root_path().join("cli/tsc/dts/lib.deno.ns.d.ts"))
- .output()
- .unwrap();
- println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
- println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
- assert!(output.status.success());
- }
-
- #[test]
- fn typecheck_declarations_unstable() {
- let output = util::deno_cmd()
- .arg("test")
- .arg("--doc")
- .arg("--unstable")
- .arg(util::root_path().join("cli/tsc/dts/lib.deno.unstable.d.ts"))
- .output()
- .unwrap();
- println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
- println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
- assert!(output.status.success());
- }
-
- #[test]
- fn typecheck_core() {
- let deno_dir = TempDir::new();
- let test_file = deno_dir.path().join("test_deno_core_types.ts");
- std::fs::write(
- &test_file,
- format!(
- "import \"{}\";",
- deno_core::resolve_path(
- util::root_path()
- .join("core/lib.deno_core.d.ts")
- .to_str()
- .unwrap()
- )
- .unwrap()
- ),
- )
- .unwrap();
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .arg("run")
- .arg(test_file.to_str().unwrap())
- .output()
- .unwrap();
- println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
- println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
- assert!(output.status.success());
- }
-
- #[test]
- fn ts_no_recheck_on_redirect() {
- let deno_dir = util::new_deno_dir();
- let e = util::deno_exe_path();
-
- let redirect_ts = util::testdata_path().join("run/017_import_redirect.ts");
- assert!(redirect_ts.is_file());
- let mut cmd = Command::new(e.clone());
- cmd.env("DENO_DIR", deno_dir.path());
- let mut initial = cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg(redirect_ts.clone())
- .spawn()
- .expect("failed to span script");
- let status_initial =
- initial.wait().expect("failed to wait for child process");
- assert!(status_initial.success());
-
- let mut cmd = Command::new(e);
- cmd.env("DENO_DIR", deno_dir.path());
- let output = cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg(redirect_ts)
- .output()
- .expect("failed to spawn script");
-
- assert!(std::str::from_utf8(&output.stderr).unwrap().is_empty());
- }
-}
diff --git a/cli/tests/compile_tests.rs b/cli/tests/compile_tests.rs
deleted file mode 100644
index b91710e68..000000000
--- a/cli/tests/compile_tests.rs
+++ /dev/null
@@ -1,569 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::fs::File;
-use std::process::Command;
-use test_util as util;
-use test_util::TempDir;
-
-mod compile {
- use super::*;
- #[test]
- fn compile() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("welcome.exe")
- } else {
- dir.path().join("welcome")
- };
- // try this twice to ensure it works with the cache
- for _ in 0..2 {
- let output = util::deno_cmd_with_deno_dir(&dir)
- .current_dir(util::root_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./test_util/std/examples/welcome.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(&exe)
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, "Welcome to Deno!\n".as_bytes());
- }
- }
-
- #[test]
- fn standalone_args() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("args.exe")
- } else {
- dir.path().join("args")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/args.ts")
- .arg("a")
- .arg("b")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .arg("foo")
- .arg("--bar")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"a\nb\nfoo\n--bar\n--unstable\n");
- }
-
- #[test]
- fn standalone_error() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("error.exe")
- } else {
- dir.path().join("error")
- };
- let testdata_path = util::testdata_path();
- let output = util::deno_cmd()
- .current_dir(&testdata_path)
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_error.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(output.stdout, b"");
- let stderr = String::from_utf8(output.stderr).unwrap();
- let stderr = util::strip_ansi_codes(&stderr).to_string();
- // On Windows, we cannot assert the file path (because '\').
- // Instead we just check for relevant output.
- assert!(stderr.contains("error: Uncaught Error: boom!"));
- assert!(stderr.contains("throw new Error(\"boom!\");"));
- assert!(stderr.contains("\n at boom (file://"));
- assert!(stderr.contains("standalone_error.ts:2:11"));
- assert!(stderr.contains("at foo (file://"));
- assert!(stderr.contains("standalone_error.ts:5:5"));
- assert!(stderr.contains("standalone_error.ts:7:1"));
- }
-
- #[test]
- fn standalone_error_module_with_imports() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("error.exe")
- } else {
- dir.path().join("error")
- };
- let testdata_path = util::testdata_path();
- let output = util::deno_cmd()
- .current_dir(&testdata_path)
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_error_module_with_imports_1.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(output.stdout, b"hello\n");
- let stderr = String::from_utf8(output.stderr).unwrap();
- let stderr = util::strip_ansi_codes(&stderr).to_string();
- // On Windows, we cannot assert the file path (because '\').
- // Instead we just check for relevant output.
- assert!(stderr.contains("error: Uncaught Error: boom!"));
- assert!(stderr.contains("throw new Error(\"boom!\");"));
- assert!(stderr.contains("\n at file://"));
- assert!(stderr.contains("standalone_error_module_with_imports_2.ts:2:7"));
- }
-
- #[test]
- fn standalone_load_datauri() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("load_datauri.exe")
- } else {
- dir.path().join("load_datauri")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_import_datauri.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Hello Deno!\n");
- }
-
- // https://github.com/denoland/deno/issues/13704
- #[test]
- fn standalone_follow_redirects() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("follow_redirects.exe")
- } else {
- dir.path().join("follow_redirects")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_follow_redirects.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Hello\n");
- }
-
- #[test]
- fn compile_with_file_exists_error() {
- let dir = TempDir::new();
- let output_path = if cfg!(windows) {
- dir.path().join(r"args\")
- } else {
- dir.path().join("args/")
- };
- let file_path = dir.path().join("args");
- File::create(&file_path).unwrap();
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&output_path)
- .arg("./compile/args.ts")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- let expected_stderr = format!(
- concat!(
- "Could not compile to file '{}' because its parent directory ",
- "is an existing file. You can use the `--output <file-path>` flag to ",
- "provide an alternative name.\n",
- ),
- file_path.display(),
- );
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stderr.contains(&expected_stderr));
- }
-
- #[test]
- fn compile_with_directory_exists_error() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("args.exe")
- } else {
- dir.path().join("args")
- };
- std::fs::create_dir(&exe).unwrap();
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/args.ts")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- let expected_stderr = format!(
- concat!(
- "Could not compile to file '{}' because a directory exists with ",
- "the same name. You can use the `--output <file-path>` flag to ",
- "provide an alternative name."
- ),
- exe.display()
- );
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stderr.contains(&expected_stderr));
- }
-
- #[test]
- fn compile_with_conflict_file_exists_error() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("args.exe")
- } else {
- dir.path().join("args")
- };
- std::fs::write(&exe, b"SHOULD NOT BE OVERWRITTEN").unwrap();
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/args.ts")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- let expected_stderr = format!(
- concat!(
- "Could not compile to file '{}' because the file already exists ",
- "and cannot be overwritten. Please delete the existing file or ",
- "use the `--output <file-path` flag to provide an alternative name."
- ),
- exe.display()
- );
- let stderr = String::from_utf8(output.stderr).unwrap();
- dbg!(&stderr);
- assert!(stderr.contains(&expected_stderr));
- assert!(std::fs::read(&exe)
- .unwrap()
- .eq(b"SHOULD NOT BE OVERWRITTEN"));
- }
-
- #[test]
- fn compile_and_overwrite_file() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("args.exe")
- } else {
- dir.path().join("args")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/args.ts")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert!(&exe.exists());
-
- let recompile_output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/args.ts")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(recompile_output.status.success());
- }
-
- #[test]
- fn standalone_runtime_flags() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("flags.exe")
- } else {
- dir.path().join("flags")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--allow-read")
- .arg("--seed")
- .arg("1")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_runtime_flags.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- let stdout_str = String::from_utf8(output.stdout).unwrap();
- assert_eq!(util::strip_ansi_codes(&stdout_str), "0.147205063401058\n");
- let stderr_str = String::from_utf8(output.stderr).unwrap();
- assert!(util::strip_ansi_codes(&stderr_str)
- .contains("PermissionDenied: Requires write access"));
- }
-
- #[test]
- fn standalone_import_map() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("import_map.exe")
- } else {
- dir.path().join("import_map")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--allow-read")
- .arg("--import-map")
- .arg("compile/standalone_import_map.json")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_import_map.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[test]
- fn standalone_import_map_config_file() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("import_map.exe")
- } else {
- dir.path().join("import_map")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--allow-read")
- .arg("--config")
- .arg("compile/standalone_import_map_config.json")
- .arg("--output")
- .arg(&exe)
- .arg("./compile/standalone_import_map.ts")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = Command::new(exe)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[test]
- // https://github.com/denoland/deno/issues/12670
- fn skip_rebundle() {
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("hello_world.exe")
- } else {
- dir.path().join("hello_world")
- };
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg("./run/001_hello.js")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
-
- //no "Bundle testdata_path/run/001_hello.js" in output
- assert!(!String::from_utf8(output.stderr).unwrap().contains("Bundle"));
-
- let output = Command::new(exe)
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, "Hello World\n".as_bytes());
- }
-
- #[test]
- fn check_local_by_default() {
- let _guard = util::http_server();
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("welcome.exe")
- } else {
- dir.path().join("welcome")
- };
- let status = util::deno_cmd()
- .current_dir(util::root_path())
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg(util::testdata_path().join("./compile/check_local_by_default.ts"))
- .status()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn check_local_by_default2() {
- let _guard = util::http_server();
- let dir = TempDir::new();
- let exe = if cfg!(windows) {
- dir.path().join("welcome.exe")
- } else {
- dir.path().join("welcome")
- };
- let output = util::deno_cmd()
- .current_dir(util::root_path())
- .env("NO_COLOR", "1")
- .arg("compile")
- .arg("--unstable")
- .arg("--output")
- .arg(&exe)
- .arg(util::testdata_path().join("./compile/check_local_by_default2.ts"))
- .output()
- .unwrap();
- assert!(!output.status.success());
- let stdout = String::from_utf8(output.stdout).unwrap();
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stdout.is_empty());
- assert!(stderr.contains(
- r#"error: TS2322 [ERROR]: Type '12' is not assignable to type '"b"'."#
- ));
- }
-}
diff --git a/cli/tests/coverage_tests.rs b/cli/tests/coverage_tests.rs
deleted file mode 100644
index 34293b36b..000000000
--- a/cli/tests/coverage_tests.rs
+++ /dev/null
@@ -1,382 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::fs;
-use test_util as util;
-use test_util::TempDir;
-
-mod coverage {
- use super::*;
- #[test]
- fn branch() {
- run_coverage_text("branch", "ts");
- }
-
- #[test]
- fn complex() {
- run_coverage_text("complex", "ts");
- }
-
- #[test]
- fn final_blankline() {
- run_coverage_text("final_blankline", "js");
- }
-
- #[test]
- fn no_snaps() {
- no_snaps_included("no_snaps_included", "ts");
- }
-
- #[test]
- fn error_if_invalid_cache() {
- let deno_dir = TempDir::new();
- let deno_dir_path = deno_dir.path();
- let tempdir = TempDir::new();
- let tempdir = tempdir.path().join("cov");
-
- let invalid_cache_path =
- util::testdata_path().join("coverage/invalid_cache");
- let mod_before_path = util::testdata_path()
- .join(&invalid_cache_path)
- .join("mod_before.ts");
- let mod_after_path = util::testdata_path()
- .join(&invalid_cache_path)
- .join("mod_after.ts");
- let mod_test_path = util::testdata_path()
- .join(&invalid_cache_path)
- .join("mod.test.ts");
-
- let mod_temp_path = deno_dir_path.join("mod.ts");
- let mod_test_temp_path = deno_dir_path.join("mod.test.ts");
-
- // Write the inital mod.ts file
- std::fs::copy(mod_before_path, &mod_temp_path).unwrap();
- // And the test file
- std::fs::copy(mod_test_path, mod_test_temp_path).unwrap();
-
- // Generate coverage
- let status = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(deno_dir_path)
- .arg("test")
- .arg("--quiet")
- .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .status()
- .unwrap();
-
- assert!(status.success());
-
- // Modify the file between deno test and deno coverage, thus invalidating the cache
- std::fs::copy(mod_after_path, mod_temp_path).unwrap();
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(deno_dir_path)
- .arg("coverage")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .output()
- .unwrap();
-
- assert!(output.stdout.is_empty());
-
- // Expect error
- let error =
- util::strip_ansi_codes(std::str::from_utf8(&output.stderr).unwrap())
- .to_string();
- assert!(error.contains("error: Missing transpiled source code"));
- assert!(error.contains("Before generating coverage report, run `deno test --coverage` to ensure consistent state."));
- }
-
- fn run_coverage_text(test_name: &str, extension: &str) {
- let deno_dir = TempDir::new();
- let tempdir = TempDir::new();
- let tempdir = tempdir.path().join("cov");
-
- let status = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("-A")
- .arg("--quiet")
- .arg("--unstable")
- .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
- .arg(format!("coverage/{}_test.{}", test_name, extension))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .status()
- .unwrap();
-
- assert!(status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--unstable")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .output()
- .unwrap();
-
- // Verify there's no "Check" being printed
- assert!(output.stderr.is_empty());
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path()
- .join(format!("coverage/{}_expected.out", test_name)),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--quiet")
- .arg("--unstable")
- .arg("--lcov")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .output()
- .unwrap();
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path()
- .join(format!("coverage/{}_expected.lcov", test_name)),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
- }
-
- #[test]
- fn multifile_coverage() {
- let deno_dir = TempDir::new();
- let tempdir = TempDir::new();
- let tempdir = tempdir.path().join("cov");
-
- let status = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("--quiet")
- .arg("--unstable")
- .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
- .arg("coverage/multifile/")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .status()
- .unwrap();
-
- assert!(status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--unstable")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .output()
- .unwrap();
-
- // Verify there's no "Check" being printed
- assert!(output.stderr.is_empty());
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path().join("coverage/multifile/expected.out"),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--quiet")
- .arg("--unstable")
- .arg("--lcov")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .output()
- .unwrap();
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path().join("coverage/multifile/expected.lcov"),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
- }
-
- fn no_snaps_included(test_name: &str, extension: &str) {
- let deno_dir = TempDir::new();
- let tempdir = TempDir::new();
- let tempdir = tempdir.path().join("cov");
-
- let status = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("--quiet")
- .arg("--unstable")
- .arg("--allow-read")
- .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
- .arg(format!(
- "coverage/no_snaps_included/{}_test.{}",
- test_name, extension
- ))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .status()
- .unwrap();
-
- assert!(status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--unstable")
- .arg("--include=no_snaps_included.ts")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .output()
- .unwrap();
-
- // Verify there's no "Check" being printed
- assert!(output.stderr.is_empty());
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path().join("coverage/no_snaps_included/expected.out"),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
- }
-
- #[test]
- fn no_transpiled_lines() {
- let deno_dir = TempDir::new();
- let tempdir = TempDir::new();
- let tempdir = tempdir.path().join("cov");
-
- let status = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("--quiet")
- .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
- .arg("coverage/no_transpiled_lines/")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .status()
- .unwrap();
-
- assert!(status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--include=no_transpiled_lines/index.ts")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .output()
- .unwrap();
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path().join("coverage/no_transpiled_lines/expected.out"),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("coverage")
- .arg("--lcov")
- .arg("--include=no_transpiled_lines/index.ts")
- .arg(format!("{}/", tempdir.to_str().unwrap()))
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .output()
- .unwrap();
-
- let actual =
- util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
- .to_string();
-
- let expected = fs::read_to_string(
- util::testdata_path().join("coverage/no_transpiled_lines/expected.lcov"),
- )
- .unwrap();
-
- if !util::wildcard_match(&expected, &actual) {
- println!("OUTPUT\n{}\nOUTPUT", actual);
- println!("EXPECTED\n{}\nEXPECTED", expected);
- panic!("pattern match failed");
- }
-
- assert!(output.status.success());
- }
-}
diff --git a/cli/tests/doc_tests.rs b/cli/tests/doc_tests.rs
deleted file mode 100644
index 7434e999a..000000000
--- a/cli/tests/doc_tests.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-use test_util::TempDir;
-use util::assert_contains;
-
-mod doc {
- use super::*;
-
- itest!(deno_doc_builtin {
- args: "doc",
- output: "doc/deno_doc_builtin.out",
- });
-
- #[test]
- fn deno_doc() {
- let dir = TempDir::new();
- // try this twice to ensure it works with the cache
- for _ in 0..2 {
- let output = util::deno_cmd_with_deno_dir(&dir)
- .current_dir(util::testdata_path())
- .arg("doc")
- .arg("doc/deno_doc.ts")
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_contains!(
- std::str::from_utf8(&output.stdout).unwrap(),
- "function foo"
- );
- }
- }
-
- itest!(deno_doc_import_map {
- args:
- "doc --unstable --import-map=doc/import_map.json doc/use_import_map.js",
- output: "doc/use_import_map.out",
- });
-
- itest!(deno_doc_types_hint {
- args: "doc doc/types_hint.ts",
- output: "doc/types_hint.out",
- });
-
- itest!(deno_doc_types_ref {
- args: "doc doc/types_ref.js",
- output: "doc/types_ref.out",
- });
-
- itest!(deno_doc_types_header {
- args: "doc --reload doc/types_header.ts",
- output: "doc/types_header.out",
- http_server: true,
- });
-
- itest!(_060_deno_doc_displays_all_overloads_in_details_view {
- args:
- "doc doc/060_deno_doc_displays_all_overloads_in_details_view.ts NS.test",
- output: "doc/060_deno_doc_displays_all_overloads_in_details_view.ts.out",
- });
-
- itest!(deno_doc_types_header_direct {
- args: "doc --reload http://127.0.0.1:4545/xTypeScriptTypes.js",
- output: "doc/types_header.out",
- http_server: true,
- });
-
- itest!(deno_doc_invalid_url {
- args: "doc https://raw.githubusercontent.com%2Fdyedgreen%2Fdeno-sqlite%2Frework_api%2Fmod.ts",
- output: "doc/invalid_url.out",
- exit_code: 1,
- });
-}
diff --git a/cli/tests/eval_tests.rs b/cli/tests/eval_tests.rs
deleted file mode 100644
index aa708520e..000000000
--- a/cli/tests/eval_tests.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-
-mod eval {
- use super::*;
-
- #[test]
- fn eval_p() {
- let output = util::deno_cmd()
- .arg("eval")
- .arg("-p")
- .arg("1+2")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout_str = util::strip_ansi_codes(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- );
- assert_eq!("3", stdout_str);
- }
-
- // Make sure that snapshot flags don't affect runtime.
- #[test]
- fn eval_randomness() {
- let mut numbers = Vec::with_capacity(10);
- for _ in 0..10 {
- let output = util::deno_cmd()
- .arg("eval")
- .arg("-p")
- .arg("Math.random()")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout_str = util::strip_ansi_codes(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- );
- numbers.push(stdout_str.to_string());
- }
- numbers.dedup();
- assert!(numbers.len() > 1);
- }
-
- itest!(eval_basic {
- args: "eval console.log(\"hello\")",
- output_str: Some("hello\n"),
- });
-
- // Ugly parentheses due to whitespace delimiting problem.
- itest!(eval_ts {
- args: "eval --quiet --ext=ts console.log((123)as(number))", // 'as' is a TS keyword only
- output_str: Some("123\n"),
- });
-
- itest!(dyn_import_eval {
- args: "eval import('./subdir/mod4.js').then(console.log)",
- output: "eval/dyn_import_eval.out",
- });
-
- // Cannot write the expression to evaluate as "console.log(typeof gc)"
- // because itest! splits args on whitespace.
- itest!(v8_flags_eval {
- args: "eval --v8-flags=--expose-gc console.log(typeof(gc))",
- output: "run/v8_flags.js.out",
- });
-
- itest!(check_local_by_default {
- args: "eval --quiet import('http://localhost:4545/subdir/type_error.ts').then(console.log);",
- output: "eval/check_local_by_default.out",
- http_server: true,
- });
-
- itest!(check_local_by_default2 {
- args: "eval --quiet import('./eval/check_local_by_default2.ts').then(console.log);",
- output: "eval/check_local_by_default2.out",
- http_server: true,
- });
-}
diff --git a/cli/tests/flags_tests.rs b/cli/tests/flags_tests.rs
deleted file mode 100644
index e19dad534..000000000
--- a/cli/tests/flags_tests.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-
-mod flags {
- use super::*;
-
- #[test]
- fn help_flag() {
- let status = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("--help")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn version_short_flag() {
- let status = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("-V")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn version_long_flag() {
- let status = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("--version")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- itest!(types {
- args: "types",
- output: "types/types.out",
- });
-}
diff --git a/cli/tests/fmt_tests.rs b/cli/tests/fmt_tests.rs
deleted file mode 100644
index 1a40823f4..000000000
--- a/cli/tests/fmt_tests.rs
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-use test_util::TempDir;
-
-mod fmt {
- use super::*;
-
- #[test]
- fn fmt_test() {
- let t = TempDir::new();
- let testdata_fmt_dir = util::testdata_path().join("fmt");
- let fixed_js = testdata_fmt_dir.join("badly_formatted_fixed.js");
- let badly_formatted_original_js =
- testdata_fmt_dir.join("badly_formatted.mjs");
- let badly_formatted_js = t.path().join("badly_formatted.js");
- let badly_formatted_js_str = badly_formatted_js.to_str().unwrap();
- std::fs::copy(badly_formatted_original_js, &badly_formatted_js).unwrap();
-
- let fixed_md = testdata_fmt_dir.join("badly_formatted_fixed.md");
- let badly_formatted_original_md =
- testdata_fmt_dir.join("badly_formatted.md");
- let badly_formatted_md = t.path().join("badly_formatted.md");
- let badly_formatted_md_str = badly_formatted_md.to_str().unwrap();
- std::fs::copy(badly_formatted_original_md, &badly_formatted_md).unwrap();
-
- let fixed_json = testdata_fmt_dir.join("badly_formatted_fixed.json");
- let badly_formatted_original_json =
- testdata_fmt_dir.join("badly_formatted.json");
- let badly_formatted_json = t.path().join("badly_formatted.json");
- let badly_formatted_json_str = badly_formatted_json.to_str().unwrap();
- std::fs::copy(badly_formatted_original_json, &badly_formatted_json)
- .unwrap();
- // First, check formatting by ignoring the badly formatted file.
- let status = util::deno_cmd()
- .current_dir(&testdata_fmt_dir)
- .arg("fmt")
- .arg(format!(
- "--ignore={},{},{}",
- badly_formatted_js_str,
- badly_formatted_md_str,
- badly_formatted_json_str
- ))
- .arg("--check")
- .arg(badly_formatted_js_str)
- .arg(badly_formatted_md_str)
- .arg(badly_formatted_json_str)
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- // No target files found
- assert!(!status.success());
-
- // Check without ignore.
- let status = util::deno_cmd()
- .current_dir(&testdata_fmt_dir)
- .arg("fmt")
- .arg("--check")
- .arg(badly_formatted_js_str)
- .arg(badly_formatted_md_str)
- .arg(badly_formatted_json_str)
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(!status.success());
-
- // Format the source file.
- let status = util::deno_cmd()
- .current_dir(&testdata_fmt_dir)
- .arg("fmt")
- .arg(badly_formatted_js_str)
- .arg(badly_formatted_md_str)
- .arg(badly_formatted_json_str)
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let expected_js = std::fs::read_to_string(fixed_js).unwrap();
- let expected_md = std::fs::read_to_string(fixed_md).unwrap();
- let expected_json = std::fs::read_to_string(fixed_json).unwrap();
- let actual_js = std::fs::read_to_string(badly_formatted_js).unwrap();
- let actual_md = std::fs::read_to_string(badly_formatted_md).unwrap();
- let actual_json = std::fs::read_to_string(badly_formatted_json).unwrap();
- assert_eq!(expected_js, actual_js);
- assert_eq!(expected_md, actual_md);
- assert_eq!(expected_json, actual_json);
- }
-
- #[test]
- fn fmt_stdin_error() {
- use std::io::Write;
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("fmt")
- .arg("-")
- .stdin(std::process::Stdio::piped())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let stdin = deno.stdin.as_mut().unwrap();
- let invalid_js = b"import { example }";
- stdin.write_all(invalid_js).unwrap();
- let output = deno.wait_with_output().unwrap();
- // Error message might change. Just check stdout empty, stderr not.
- assert!(output.stdout.is_empty());
- assert!(!output.stderr.is_empty());
- assert!(!output.status.success());
- }
-
- #[test]
- fn fmt_ignore_unexplicit_files() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("fmt")
- .arg("--check")
- .arg("--ignore=./")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(
- String::from_utf8_lossy(&output.stderr),
- "error: No target files found.\n"
- );
- }
-
- #[test]
- fn fmt_auto_ignore_git_and_node_modules() {
- use std::fs::{create_dir_all, File};
- use std::io::Write;
- use std::path::PathBuf;
- fn create_bad_json(t: PathBuf) {
- let bad_json_path = t.join("bad.json");
- let mut bad_json_file = File::create(bad_json_path).unwrap();
- writeln!(bad_json_file, "bad json").unwrap();
- }
- let temp_dir = TempDir::new();
- let t = temp_dir.path().join("target");
- let nest_git = t.join("nest").join(".git");
- let git_dir = t.join(".git");
- let nest_node_modules = t.join("nest").join("node_modules");
- let node_modules_dir = t.join("node_modules");
- create_dir_all(&nest_git).unwrap();
- create_dir_all(&git_dir).unwrap();
- create_dir_all(&nest_node_modules).unwrap();
- create_dir_all(&node_modules_dir).unwrap();
- create_bad_json(nest_git);
- create_bad_json(git_dir);
- create_bad_json(nest_node_modules);
- create_bad_json(node_modules_dir);
- let output = util::deno_cmd()
- .current_dir(t)
- .env("NO_COLOR", "1")
- .arg("fmt")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(
- String::from_utf8_lossy(&output.stderr),
- "error: No target files found.\n"
- );
- }
-
- itest!(fmt_quiet_check_fmt_dir {
- args: "fmt --check --quiet fmt/regular/",
- output_str: Some(""),
- exit_code: 0,
- });
-
- itest!(fmt_check_formatted_files {
- args: "fmt --check fmt/regular/formatted1.js fmt/regular/formatted2.ts fmt/regular/formatted3.markdown fmt/regular/formatted4.jsonc",
- output: "fmt/expected_fmt_check_formatted_files.out",
- exit_code: 0,
- });
-
- itest!(fmt_check_ignore {
- args: "fmt --check --ignore=fmt/regular/formatted1.js fmt/regular/",
- output: "fmt/expected_fmt_check_ignore.out",
- exit_code: 0,
- });
-
- itest!(fmt_check_parse_error {
- args: "fmt --check fmt/parse_error/parse_error.ts",
- output: "fmt/fmt_check_parse_error.out",
- exit_code: 1,
- });
-
- itest!(fmt_stdin {
- args: "fmt -",
- input: Some("const a = 1\n"),
- output_str: Some("const a = 1;\n"),
- });
-
- itest!(fmt_stdin_markdown {
- args: "fmt --ext=md -",
- input: Some("# Hello Markdown\n```ts\nconsole.log( \"text\")\n```\n\n```cts\nconsole.log( 5 )\n```"),
- output_str: Some("# Hello Markdown\n\n```ts\nconsole.log(\"text\");\n```\n\n```cts\nconsole.log(5);\n```\n"),
-});
-
- itest!(fmt_stdin_json {
- args: "fmt --ext=json -",
- input: Some("{ \"key\": \"value\"}"),
- output_str: Some("{ \"key\": \"value\" }\n"),
- });
-
- itest!(fmt_stdin_check_formatted {
- args: "fmt --check -",
- input: Some("const a = 1;\n"),
- output_str: Some(""),
- });
-
- itest!(fmt_stdin_check_not_formatted {
- args: "fmt --check -",
- input: Some("const a = 1\n"),
- output_str: Some("Not formatted stdin\n"),
- });
-
- itest!(fmt_with_config {
- args: "fmt --config fmt/with_config/deno.jsonc fmt/with_config/subdir",
- output: "fmt/fmt_with_config.out",
- });
-
- itest!(fmt_with_config_default {
- args: "fmt fmt/with_config/subdir",
- output: "fmt/fmt_with_config.out",
- });
-
- // Check if CLI flags take precedence
- itest!(fmt_with_config_and_flags {
- args: "fmt --config fmt/with_config/deno.jsonc --ignore=fmt/with_config/subdir/a.ts,fmt/with_config/subdir/b.ts",
- output: "fmt/fmt_with_config_and_flags.out",
- });
-
- itest!(fmt_with_malformed_config {
- args: "fmt --config fmt/deno.malformed.jsonc",
- output: "fmt/fmt_with_malformed_config.out",
- exit_code: 1,
- });
-
- itest!(fmt_with_malformed_config2 {
- args: "fmt --config fmt/deno.malformed2.jsonc",
- output: "fmt/fmt_with_malformed_config2.out",
- exit_code: 1,
- });
-}
diff --git a/cli/tests/info_tests.rs b/cli/tests/info_tests.rs
deleted file mode 100644
index 577aff4ca..000000000
--- a/cli/tests/info_tests.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-use test_util::TempDir;
-
-mod init {
- use super::*;
-
- #[test]
- fn info_with_compiled_source() {
- let _g = util::http_server();
- let module_path = "http://127.0.0.1:4545/run/048_media_types_jsx.ts";
- let t = TempDir::new();
-
- let mut deno = util::deno_cmd()
- .env("DENO_DIR", t.path())
- .current_dir(util::testdata_path())
- .arg("cache")
- .arg(module_path)
- .spawn()
- .unwrap();
- let status = deno.wait().unwrap();
- assert!(status.success());
-
- let output = util::deno_cmd()
- .env("DENO_DIR", t.path())
- .env("NO_COLOR", "1")
- .current_dir(util::testdata_path())
- .arg("info")
- .arg(module_path)
- .output()
- .unwrap();
-
- let str_output = std::str::from_utf8(&output.stdout).unwrap().trim();
- // check the output of the test.ts program.
- assert!(str_output.contains("emit: "));
- assert_eq!(output.stderr, b"");
- }
-
- itest!(multiple_imports {
- args: "info http://127.0.0.1:4545/run/019_media_types.ts",
- output: "info/multiple_imports.out",
- http_server: true,
- });
-
- itest!(info_ts_error {
- args: "info info/031_info_ts_error.ts",
- output: "info/031_info_ts_error.out",
- });
-
- itest!(info_flag {
- args: "info",
- output: "info/041_info_flag.out",
- });
-
- itest!(info_flag_location {
- args: "info --location https://deno.land",
- output: "info/041_info_flag_location.out",
- });
-
- itest!(info_json {
- args: "info --json --unstable",
- output: "info/info_json.out",
- });
-
- itest!(info_json_location {
- args: "info --json --unstable --location https://deno.land",
- output: "info/info_json_location.out",
- });
-
- itest!(info_flag_script_jsx {
- args: "info http://127.0.0.1:4545/run/048_media_types_jsx.ts",
- output: "info/049_info_flag_script_jsx.out",
- http_server: true,
- });
-
- itest!(json_file {
- args: "info --quiet --json --unstable info/json_output/main.ts",
- output: "info/json_output/main.out",
- exit_code: 0,
- });
-
- itest!(import_map_info {
- args:
- "info --quiet --import-map=import_maps/import_map.json import_maps/test.ts",
- output: "info/065_import_map_info.out",
- });
-
- itest!(info_json_deps_order {
- args: "info --unstable --json info/076_info_json_deps_order.ts",
- output: "info/076_info_json_deps_order.out",
- });
-
- itest!(info_missing_module {
- args: "info info/error_009_missing_js_module.js",
- output: "info/info_missing_module.out",
- });
-
- itest!(info_recursive_modules {
- args: "info --quiet info/info_recursive_imports_test.ts",
- output: "info/info_recursive_imports_test.out",
- exit_code: 0,
- });
-
- itest!(info_type_import {
- args: "info info/info_type_import.ts",
- output: "info/info_type_import.out",
- });
-
- itest!(_054_info_local_imports {
- args: "info --quiet run/005_more_imports.ts",
- output: "info/054_info_local_imports.out",
- exit_code: 0,
- });
-
- // Tests for AssertionError where "data" is unexpectedly null when
- // a file contains only triple slash references (#11196)
- itest!(data_null_error {
- args: "info info/data_null_error/mod.ts",
- output: "info/data_null_error/data_null_error.out",
- });
-
- itest!(types_header_direct {
- args: "info --reload run/type_directives_01.ts",
- output: "info/types_header.out",
- http_server: true,
- });
-
- itest!(with_config_override {
- args: "info info/with_config/test.ts --config info/with_config/deno-override.json --import-map info/with_config/import_map.json",
- output: "info/with_config/with_config.out",
- });
-}
diff --git a/cli/tests/init_tests.rs b/cli/tests/init_tests.rs
deleted file mode 100644
index 813f61f75..000000000
--- a/cli/tests/init_tests.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::process::Stdio;
-use test_util as util;
-use test_util::TempDir;
-use util::assert_contains;
-
-mod init {
- use super::*;
-
- #[test]
- fn init_subcommand_without_dir() {
- let temp_dir = TempDir::new();
- let cwd = temp_dir.path();
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .arg("init")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert_contains!(stderr, "Project initialized");
- assert!(!stderr.contains("cd"));
- assert_contains!(stderr, "deno run main.ts");
- assert_contains!(stderr, "deno task dev");
- assert_contains!(stderr, "deno test");
- assert_contains!(stderr, "deno bench");
-
- assert!(cwd.join("deno.jsonc").exists());
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("main.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("test")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert_contains!(stdout, "1 passed");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("bench")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[test]
- fn init_subcommand_with_dir_arg() {
- let temp_dir = TempDir::new();
- let cwd = temp_dir.path();
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .arg("init")
- .arg("my_dir")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert_contains!(stderr, "Project initialized");
- assert_contains!(stderr, "cd my_dir");
- assert_contains!(stderr, "deno run main.ts");
- assert_contains!(stderr, "deno task dev");
- assert_contains!(stderr, "deno test");
- assert_contains!(stderr, "deno bench");
-
- assert!(cwd.join("my_dir/deno.jsonc").exists());
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("my_dir/main.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("test")
- .arg("my_dir/main_test.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert_contains!(stdout, "1 passed");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("bench")
- .arg("my_dir/main_bench.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[test]
- fn init_subcommand_with_quiet_arg() {
- let temp_dir = TempDir::new();
- let cwd = temp_dir.path();
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .arg("init")
- .arg("--quiet")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert_eq!(stdout, "");
- assert!(cwd.join("deno.jsonc").exists());
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("main.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("test")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert_contains!(stdout, "1 passed");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(cwd)
- .env("NO_COLOR", "1")
- .arg("bench")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-}
diff --git a/cli/tests/inspector_tests.rs b/cli/tests/inspector_tests.rs
deleted file mode 100644
index d0a200ee7..000000000
--- a/cli/tests/inspector_tests.rs
+++ /dev/null
@@ -1,1378 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use deno_core::error::AnyError;
-use deno_core::futures::prelude::*;
-use deno_core::futures::stream::SplitSink;
-use deno_core::futures::stream::SplitStream;
-use deno_core::serde_json;
-use deno_core::serde_json::json;
-use deno_core::url;
-use deno_runtime::deno_fetch::reqwest;
-use deno_runtime::deno_websocket::tokio_tungstenite;
-use deno_runtime::deno_websocket::tokio_tungstenite::tungstenite;
-use std::io::BufRead;
-use std::process::Child;
-use test_util as util;
-use test_util::TempDir;
-use tokio::net::TcpStream;
-use util::http_server;
-
-mod inspector {
- use super::*;
-
- struct InspectorTester {
- socket_tx: SplitSink<
- tokio_tungstenite::WebSocketStream<
- tokio_tungstenite::MaybeTlsStream<TcpStream>,
- >,
- tungstenite::Message,
- >,
- socket_rx: SplitStream<
- tokio_tungstenite::WebSocketStream<
- tokio_tungstenite::MaybeTlsStream<TcpStream>,
- >,
- >,
- notification_filter: Box<dyn FnMut(&str) -> bool + 'static>,
- child: Child,
- stderr_lines: Box<dyn Iterator<Item = String>>,
- stdout_lines: Box<dyn Iterator<Item = String>>,
- }
-
- fn ignore_script_parsed(msg: &str) -> bool {
- !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#)
- }
-
- impl InspectorTester {
- async fn create<F>(mut child: Child, notification_filter: F) -> Self
- where
- F: FnMut(&str) -> bool + 'static,
- {
- let stdout = child.stdout.take().unwrap();
- let stdout_lines =
- std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
-
- let stderr = child.stderr.take().unwrap();
- let mut stderr_lines =
- std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
-
- let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
-
- let (socket, response) =
- tokio_tungstenite::connect_async(ws_url).await.unwrap();
- assert_eq!(response.status(), 101); // Switching protocols.
-
- let (socket_tx, socket_rx) = socket.split();
-
- Self {
- socket_tx,
- socket_rx,
- notification_filter: Box::new(notification_filter),
- child,
- stderr_lines: Box::new(stderr_lines),
- stdout_lines: Box::new(stdout_lines),
- }
- }
-
- async fn send_many(&mut self, messages: &[serde_json::Value]) {
- // TODO(bartlomieju): add graceful error handling
- for msg in messages {
- let result = self
- .socket_tx
- .send(msg.to_string().into())
- .await
- .map_err(|e| e.into());
- self.handle_error(result);
- }
- }
-
- async fn send(&mut self, message: serde_json::Value) {
- self.send_many(&[message]).await;
- }
-
- fn handle_error<T>(&mut self, result: Result<T, AnyError>) -> T {
- match result {
- Ok(result) => result,
- Err(err) => {
- let mut stdout = vec![];
- for line in self.stdout_lines.by_ref() {
- stdout.push(line);
- }
- let mut stderr = vec![];
- for line in self.stderr_lines.by_ref() {
- stderr.push(line);
- }
- let stdout = stdout.join("\n");
- let stderr = stderr.join("\n");
- self.child.kill().unwrap();
-
- panic!(
- "Inspector test failed with error: {:?}.\nstdout:\n{}\nstderr:\n{}",
- err, stdout, stderr
- );
- }
- }
- }
-
- async fn recv(&mut self) -> String {
- loop {
- let result = self.socket_rx.next().await.unwrap().map_err(|e| e.into());
- let message = self.handle_error(result).to_string();
- if (self.notification_filter)(&message) {
- return message;
- }
- }
- }
-
- async fn recv_as_json(&mut self) -> serde_json::Value {
- let msg = self.recv().await;
- serde_json::from_str(&msg).unwrap()
- }
-
- async fn assert_received_messages(
- &mut self,
- responses: &[&str],
- notifications: &[&str],
- ) {
- let expected_messages = responses.len() + notifications.len();
- let mut responses_idx = 0;
- let mut notifications_idx = 0;
-
- for _ in 0..expected_messages {
- let msg = self.recv().await;
-
- if msg.starts_with(r#"{"id":"#) {
- assert!(
- msg.starts_with(responses[responses_idx]),
- "Doesn't start with {}, instead received {}",
- responses[responses_idx],
- msg
- );
- responses_idx += 1;
- } else {
- assert!(
- msg.starts_with(notifications[notifications_idx]),
- "Doesn't start with {}, instead received {}",
- notifications[notifications_idx],
- msg
- );
- notifications_idx += 1;
- }
- }
- }
-
- fn stderr_line(&mut self) -> String {
- self.stderr_lines.next().unwrap()
- }
-
- fn stdout_line(&mut self) -> String {
- self.stdout_lines.next().unwrap()
- }
-
- fn assert_stderr_for_inspect(&mut self) {
- assert_stderr(
- &mut self.stderr_lines,
- &["Visit chrome://inspect to connect to the debugger."],
- );
- }
-
- fn assert_stderr_for_inspect_brk(&mut self) {
- assert_stderr(
- &mut self.stderr_lines,
- &[
- "Visit chrome://inspect to connect to the debugger.",
- "Deno is waiting for debugger to connect.",
- ],
- );
- }
- }
-
- macro_rules! assert_starts_with {
- ($string:expr, $($test:expr),+) => {
- let string = $string; // This might be a function call or something
- if !($(string.starts_with($test))||+) {
- panic!("{:?} does not start with {:?}", string, [$($test),+]);
- }
- }
- }
-
- fn assert_stderr(
- stderr_lines: &mut impl std::iter::Iterator<Item = String>,
- expected_lines: &[&str],
- ) {
- let mut expected_index = 0;
-
- loop {
- let line = skip_check_line(stderr_lines);
-
- assert_eq!(line, expected_lines[expected_index]);
- expected_index += 1;
-
- if expected_index >= expected_lines.len() {
- break;
- }
- }
- }
-
- fn inspect_flag_with_unique_port(flag_prefix: &str) -> String {
- use std::sync::atomic::{AtomicU16, Ordering};
- static PORT: AtomicU16 = AtomicU16::new(9229);
- let port = PORT.fetch_add(1, Ordering::Relaxed);
- format!("{}=127.0.0.1:{}", flag_prefix, port)
- }
-
- fn extract_ws_url_from_stderr(
- stderr_lines: &mut impl std::iter::Iterator<Item = String>,
- ) -> url::Url {
- let stderr_first_line = skip_check_line(stderr_lines);
- assert_starts_with!(&stderr_first_line, "Debugger listening on ");
- let v: Vec<_> = stderr_first_line.match_indices("ws:").collect();
- assert_eq!(v.len(), 1);
- let ws_url_index = v[0].0;
- let ws_url = &stderr_first_line[ws_url_index..];
- url::Url::parse(ws_url).unwrap()
- }
-
- fn skip_check_line(
- stderr_lines: &mut impl std::iter::Iterator<Item = String>,
- ) -> String {
- loop {
- let mut line = stderr_lines.next().unwrap();
- line = util::strip_ansi_codes(&line).to_string();
-
- if line.starts_with("Check") {
- continue;
- }
-
- return line;
- }
- }
-
- #[tokio::test]
- async fn inspector_connect() {
- let script = util::testdata_path().join("inspector/inspector1.js");
- let mut child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .arg(script)
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr = child.stderr.as_mut().unwrap();
- let mut stderr_lines =
- std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
- let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
-
- // We use tokio_tungstenite as a websocket client because warp (which is
- // a dependency of Deno) uses it.
- let (_socket, response) =
- tokio_tungstenite::connect_async(ws_url).await.unwrap();
- assert_eq!("101 Switching Protocols", response.status().to_string());
- child.kill().unwrap();
- child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_break_on_first_line() {
- let script = util::testdata_path().join("inspector/inspector2.js");
- let child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({
- "id":4,
- "method":"Runtime.evaluate",
- "params":{
- "expression":"Deno.core.print(\"hello from the inspector\\n\")",
- "contextId":1,
- "includeCommandLineAPI":true,
- "silent":false,
- "returnByValue":true
- }
- }))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#],
- &[],
- )
- .await;
-
- assert_eq!(
- &tester.stdout_lines.next().unwrap(),
- "hello from the inspector"
- );
-
- tester
- .send(json!({"id":5,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":5,"result":{}}"#], &[])
- .await;
-
- assert_eq!(
- &tester.stdout_lines.next().unwrap(),
- "hello from the script"
- );
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_pause() {
- let script = util::testdata_path().join("inspector/inspector1.js");
- let child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester
- .send(json!({"id":6,"method":"Debugger.enable"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":6,"result":{"debuggerId":"#], &[])
- .await;
-
- tester
- .send(json!({"id":31,"method":"Debugger.pause"}))
- .await;
-
- tester
- .assert_received_messages(&[r#"{"id":31,"result":{}}"#], &[])
- .await;
-
- tester.child.kill().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_port_collision() {
- // Skip this test on WSL, which allows multiple processes to listen on the
- // same port, rather than making `bind()` fail with `EADDRINUSE`.
- if cfg!(target_os = "linux")
- && std::env::var_os("WSL_DISTRO_NAME").is_some()
- {
- return;
- }
-
- let script = util::testdata_path().join("inspector/inspector1.js");
- let inspect_flag = inspect_flag_with_unique_port("--inspect");
-
- let mut child1 = util::deno_cmd()
- .arg("run")
- .arg(&inspect_flag)
- .arg(script.clone())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr_1 = child1.stderr.as_mut().unwrap();
- let mut stderr_1_lines = std::io::BufReader::new(stderr_1)
- .lines()
- .map(|r| r.unwrap());
- let _ = extract_ws_url_from_stderr(&mut stderr_1_lines);
-
- let mut child2 = util::deno_cmd()
- .arg("run")
- .arg(&inspect_flag)
- .arg(script)
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr_2 = child2.stderr.as_mut().unwrap();
- let stderr_2_error_message = std::io::BufReader::new(stderr_2)
- .lines()
- .map(|r| r.unwrap())
- .inspect(|line| assert!(!line.contains("Debugger listening")))
- .find(|line| line.contains("Cannot start inspector server"));
- assert!(stderr_2_error_message.is_some());
-
- child1.kill().unwrap();
- child1.wait().unwrap();
- child2.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_does_not_hang() {
- let script = util::testdata_path().join("inspector/inspector3.js");
- let child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .env("NO_COLOR", "1")
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#
- ],
- )
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({"id":4,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":4,"result":{}}"#],
- &[r#"{"method":"Debugger.resumed","params":{}}"#],
- )
- .await;
-
- for i in 0..128u32 {
- let request_id = i + 10;
- // Expect the number {i} on stdout.
- let s = i.to_string();
- assert_eq!(tester.stdout_lines.next().unwrap(), s);
-
- tester
- .assert_received_messages(
- &[],
- &[
- r#"{"method":"Runtime.consoleAPICalled","#,
- r#"{"method":"Debugger.paused","#,
- ],
- )
- .await;
-
- tester
- .send(json!({"id":request_id,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(
- &[&format!(r#"{{"id":{},"result":{{}}}}"#, request_id)],
- &[r#"{"method":"Debugger.resumed","params":{}}"#],
- )
- .await;
- }
-
- // Check that we can gracefully close the websocket connection.
- tester.socket_tx.close().await.unwrap();
- tester.socket_rx.for_each(|_| async {}).await;
-
- assert_eq!(&tester.stdout_lines.next().unwrap(), "done");
- assert!(tester.child.wait().unwrap().success());
- }
-
- #[tokio::test]
- async fn inspector_without_brk_runs_code() {
- let script = util::testdata_path().join("inspector/inspector4.js");
- let mut child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr = child.stderr.as_mut().unwrap();
- let mut stderr_lines =
- std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
- let _ = extract_ws_url_from_stderr(&mut stderr_lines);
-
- // Check that inspector actually runs code without waiting for inspector
- // connection.
- let stdout = child.stdout.as_mut().unwrap();
- let mut stdout_lines =
- std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
- let stdout_first_line = stdout_lines.next().unwrap();
- assert_eq!(stdout_first_line, "hello");
-
- child.kill().unwrap();
- child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_runtime_evaluate_does_not_crash() {
- let child = util::deno_cmd()
- .arg("repl")
- .arg("--allow-read")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .stdin(std::process::Stdio::piped())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- let stdin = tester.child.stdin.take().unwrap();
-
- tester.assert_stderr_for_inspect();
- assert_starts_with!(&tester.stdout_line(), "Deno");
- assert_eq!(
- &tester.stdout_line(),
- "exit using ctrl+d, ctrl+c, or close()"
- );
- assert_eq!(&tester.stderr_line(), "Debugger session started.");
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send(json!({
- "id":3,
- "method":"Runtime.compileScript",
- "params":{
- "expression":"Deno.cwd()",
- "sourceURL":"",
- "persistScript":false,
- "executionContextId":1
- }
- }))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":3,"result":{}}"#], &[])
- .await;
- tester
- .send(json!({
- "id":4,
- "method":"Runtime.evaluate",
- "params":{
- "expression":"Deno.cwd()",
- "objectGroup":"console",
- "includeCommandLineAPI":true,
- "silent":false,
- "contextId":1,
- "returnByValue":true,
- "generatePreview":true,
- "userGesture":true,
- "awaitPromise":false,
- "replMode":true
- }
- }))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
- &[],
- )
- .await;
- tester
- .send(json!({
- "id":5,
- "method":"Runtime.evaluate",
- "params":{
- "expression":"console.error('done');",
- "objectGroup":"console",
- "includeCommandLineAPI":true,
- "silent":false,
- "contextId":1,
- "returnByValue":true,
- "generatePreview":true,
- "userGesture":true,
- "awaitPromise":false,
- "replMode":true
- }
- }))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
- &[r#"{"method":"Runtime.consoleAPICalled"#],
- )
- .await;
- assert_eq!(&tester.stderr_line(), "done");
- drop(stdin);
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_json() {
- let script = util::testdata_path().join("inspector/inspector1.js");
- let mut child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .arg(script)
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr = child.stderr.as_mut().unwrap();
- let mut stderr_lines =
- std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
- let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
- let mut url = ws_url.clone();
- let _ = url.set_scheme("http");
- url.set_path("/json");
- let resp = reqwest::get(url).await.unwrap();
- assert_eq!(resp.status(), reqwest::StatusCode::OK);
- let endpoint_list: Vec<deno_core::serde_json::Value> =
- serde_json::from_str(&resp.text().await.unwrap()).unwrap();
- let matching_endpoint = endpoint_list
- .iter()
- .find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str());
- assert!(matching_endpoint.is_some());
- child.kill().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_json_list() {
- let script = util::testdata_path().join("inspector/inspector1.js");
- let mut child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .arg(script)
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr = child.stderr.as_mut().unwrap();
- let mut stderr_lines =
- std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
- let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
- let mut url = ws_url.clone();
- let _ = url.set_scheme("http");
- url.set_path("/json/list");
- let resp = reqwest::get(url).await.unwrap();
- assert_eq!(resp.status(), reqwest::StatusCode::OK);
- let endpoint_list: Vec<deno_core::serde_json::Value> =
- serde_json::from_str(&resp.text().await.unwrap()).unwrap();
- let matching_endpoint = endpoint_list
- .iter()
- .find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str());
- assert!(matching_endpoint.is_some());
- child.kill().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_connect_non_ws() {
- // https://github.com/denoland/deno/issues/11449
- // Verify we don't panic if non-WS connection is being established
- let script = util::testdata_path().join("inspector/inspector1.js");
- let mut child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect"))
- .arg(script)
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stderr = child.stderr.as_mut().unwrap();
- let mut stderr_lines =
- std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
- let mut ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
- // Change scheme to URL and try send a request. We're not interested
- // in the request result, just that the process doesn't panic.
- ws_url.set_scheme("http").unwrap();
- let resp = reqwest::get(ws_url).await.unwrap();
- assert_eq!("400 Bad Request", resp.status().to_string());
- child.kill().unwrap();
- child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_break_on_first_line_in_test() {
- let script = util::testdata_path().join("inspector/inspector_test.js");
- let child = util::deno_cmd()
- .arg("test")
- .arg("--quiet")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg(script)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({
- "id":4,
- "method":"Runtime.evaluate",
- "params":{
- "expression":"1 + 1",
- "contextId":1,
- "includeCommandLineAPI":true,
- "silent":false,
- "returnByValue":true
- }
- }))
- .await;
- tester.assert_received_messages(
- &[r#"{"id":4,"result":{"result":{"type":"number","value":2,"description":"2"}}}"#],
- &[],
- )
- .await;
-
- tester
- .send(json!({"id":5,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":5,"result":{}}"#], &[])
- .await;
-
- assert_starts_with!(&tester.stdout_line(), "running 1 test from");
- assert!(&tester.stdout_line().contains("basic test ... ok"));
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_with_ts_files() {
- let script = util::testdata_path().join("inspector/test.ts");
- let child = util::deno_cmd()
- .arg("run")
- .arg("--check")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- fn notification_filter(msg: &str) -> bool {
- (msg.starts_with(r#"{"method":"Debugger.scriptParsed","#)
- && msg.contains("testdata/inspector"))
- || !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#)
- }
-
- let mut tester = InspectorTester::create(child, notification_filter).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- // receive messages with sources from this test
- let script1 = tester.recv().await;
- assert!(script1.contains("testdata/inspector/test.ts"));
- let script1_id = {
- let v: serde_json::Value = serde_json::from_str(&script1).unwrap();
- v["params"]["scriptId"].as_str().unwrap().to_string()
- };
- let script2 = tester.recv().await;
- assert!(script2.contains("testdata/inspector/foo.ts"));
- let script2_id = {
- let v: serde_json::Value = serde_json::from_str(&script2).unwrap();
- v["params"]["scriptId"].as_str().unwrap().to_string()
- };
- let script3 = tester.recv().await;
- assert!(script3.contains("testdata/inspector/bar.js"));
- let script3_id = {
- let v: serde_json::Value = serde_json::from_str(&script3).unwrap();
- v["params"]["scriptId"].as_str().unwrap().to_string()
- };
-
- tester
- .assert_received_messages(&[r#"{"id":2,"result":{"debuggerId":"#], &[])
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester.send_many(
- &[
- json!({"id":4,"method":"Debugger.getScriptSource","params":{"scriptId":script1_id.as_str()}}),
- json!({"id":5,"method":"Debugger.getScriptSource","params":{"scriptId":script2_id.as_str()}}),
- json!({"id":6,"method":"Debugger.getScriptSource","params":{"scriptId":script3_id.as_str()}}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":4,"result":{"scriptSource":"import { foo } from \"./foo.ts\";\nimport { bar } from \"./bar.js\";\nconsole.log(foo());\nconsole.log(bar());\n//# sourceMappingURL=data:application/json;base64,"#,
- r#"{"id":5,"result":{"scriptSource":"class Foo {\n hello() {\n return \"hello\";\n }\n}\nexport function foo() {\n const f = new Foo();\n return f.hello();\n}\n//# sourceMappingURL=data:application/json;base64,"#,
- r#"{"id":6,"result":{"scriptSource":"export function bar() {\n return \"world\";\n}\n"#,
- ],
- &[],
- )
- .await;
-
- tester
- .send(json!({"id":7,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":7,"result":{}}"#], &[])
- .await;
-
- assert_eq!(&tester.stdout_line(), "hello");
- assert_eq!(&tester.stdout_line(), "world");
-
- tester.assert_received_messages(
- &[],
- &[
- r#"{"method":"Debugger.resumed","params":{}}"#,
- r#"{"method":"Runtime.consoleAPICalled","#,
- r#"{"method":"Runtime.consoleAPICalled","#,
- r#"{"method":"Runtime.executionContextDestroyed","params":{"executionContextId":1}}"#,
- ],
- )
- .await;
-
- assert_eq!(
- &tester.stdout_line(),
- "Program finished. Waiting for inspector to disconnect to exit the process..."
- );
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_memory() {
- let script = util::testdata_path().join("inspector/memory.js");
- let child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send_many(&[
- json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}),
- json!({"id":4,"method":"HeapProfiler.enable"}),
- ])
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#, r#"{"id":4,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({"id":5,"method":"Runtime.getHeapUsage", "params": {}}))
- .await;
-
- let json_msg = tester.recv_as_json().await;
- assert_eq!(json_msg["id"].as_i64().unwrap(), 5);
- let result = &json_msg["result"];
- assert!(
- result["usedSize"].as_i64().unwrap()
- <= result["totalSize"].as_i64().unwrap()
- );
-
- tester
- .send(json!({
- "id":6,
- "method":"HeapProfiler.takeHeapSnapshot",
- "params": {
- "reportProgress": true,
- "treatGlobalObjectsAsRoots": true,
- "captureNumberValue": false
- }
- }))
- .await;
-
- let mut progress_report_completed = false;
- loop {
- let msg = tester.recv().await;
-
- // TODO(bartlomieju): can be abstracted
- if !progress_report_completed
- && msg.starts_with(
- r#"{"method":"HeapProfiler.reportHeapSnapshotProgress","params""#,
- )
- {
- let json_msg: serde_json::Value = serde_json::from_str(&msg).unwrap();
- if let Some(finished) = json_msg["params"].get("finished") {
- progress_report_completed = finished.as_bool().unwrap();
- }
- continue;
- }
-
- if msg.starts_with(r#"{"method":"HeapProfiler.reportHeapSnapshotProgress","params":{"done":"#,) {
- continue;
- }
-
- if msg.starts_with(r#"{"id":6,"result":{}}"#) {
- assert!(progress_report_completed);
- break;
- }
- }
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_profile() {
- let script = util::testdata_path().join("inspector/memory.js");
- let child = util::deno_cmd()
- .arg("run")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send_many(&[
- json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}),
- json!({"id":4,"method":"Profiler.enable"}),
- ])
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#, r#"{"id":4,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester.send_many(
- &[
- json!({"id":5,"method":"Profiler.setSamplingInterval","params":{"interval": 100}}),
- json!({"id":6,"method":"Profiler.start","params":{}}),
- ],
- ).await;
- tester
- .assert_received_messages(
- &[r#"{"id":5,"result":{}}"#, r#"{"id":6,"result":{}}"#],
- &[],
- )
- .await;
-
- tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
-
- tester
- .send(json!({"id":7,"method":"Profiler.stop", "params": {}}))
- .await;
- let json_msg = tester.recv_as_json().await;
- assert_eq!(json_msg["id"].as_i64().unwrap(), 7);
- let result = &json_msg["result"];
- let profile = &result["profile"];
- assert!(
- profile["startTime"].as_i64().unwrap()
- < profile["endTime"].as_i64().unwrap()
- );
- profile["samples"].as_array().unwrap();
- profile["nodes"].as_array().unwrap();
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_break_on_first_line_npm_esm() {
- let _server = http_server();
-
- let child = util::deno_cmd()
- .arg("run")
- .arg("--quiet")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg("npm:@denotest/bin/cli-esm")
- .arg("this")
- .arg("is")
- .arg("a")
- .arg("test")
- .envs(util::env_vars_for_npm_tests())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({"id":4,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":4,"result":{}}"#], &[])
- .await;
-
- assert_eq!(&tester.stdout_line(), "this");
- assert_eq!(&tester.stdout_line(), "is");
- assert_eq!(&tester.stdout_line(), "a");
- assert_eq!(&tester.stdout_line(), "test");
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_break_on_first_line_npm_cjs() {
- let _server = http_server();
- let child = util::deno_cmd()
- .arg("run")
- .arg("--quiet")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg("npm:@denotest/bin/cli-cjs")
- .arg("this")
- .arg("is")
- .arg("a")
- .arg("test")
- .envs(util::env_vars_for_npm_tests())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({"id":4,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":4,"result":{}}"#], &[])
- .await;
-
- assert_eq!(&tester.stdout_line(), "this");
- assert_eq!(&tester.stdout_line(), "is");
- assert_eq!(&tester.stdout_line(), "a");
- assert_eq!(&tester.stdout_line(), "test");
-
- tester.child.kill().unwrap();
- tester.child.wait().unwrap();
- }
-
- #[tokio::test]
- async fn inspector_error_with_npm_import() {
- let script =
- util::testdata_path().join("inspector/error_with_npm_import.js");
- let _server = http_server();
-
- let child = util::deno_cmd()
- .arg("run")
- .arg("--quiet")
- .arg("-A")
- .arg(inspect_flag_with_unique_port("--inspect-brk"))
- .arg(script)
- .envs(util::env_vars_for_npm_tests())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
-
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
-
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(
- &[r#"{"id":3,"result":{}}"#],
- &[r#"{"method":"Debugger.paused","#],
- )
- .await;
-
- tester
- .send(json!({"id":4,"method":"Debugger.resume"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":4,"result":{}}"#], &[])
- .await;
-
- // TODO(bartlomieju): this is a partial fix, we should assert that
- // "Runtime.exceptionThrown" notification was sent, but a bindings for this
- // notification is not yet there
- assert_eq!(&tester.stderr_line(), "Debugger session started.");
- assert_eq!(&tester.stderr_line(), "error: Uncaught Error: boom!");
-
- assert_eq!(tester.child.wait().unwrap().code(), Some(1));
- }
-
- #[tokio::test]
- async fn inspector_wait() {
- let script = util::testdata_path().join("inspector/inspect_wait.js");
- let temp_dir = TempDir::new();
-
- let child = util::deno_cmd()
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--quiet")
- .arg("-A")
- .arg(inspect_flag_with_unique_port("--inspect-wait"))
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
- assert!(!temp_dir.path().join("hello.txt").exists());
-
- let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
-
- tester.assert_stderr_for_inspect_brk();
- tester
- .send_many(&[
- json!({"id":1,"method":"Runtime.enable"}),
- json!({"id":2,"method":"Debugger.enable"}),
- ])
- .await;
- tester.assert_received_messages(
- &[
- r#"{"id":1,"result":{}}"#,
- r#"{"id":2,"result":{"debuggerId":"#,
- ],
- &[
- r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
- ],
- )
- .await;
- // TODO(bartlomieju): ideally this shouldn't be needed, but currently there's
- // no way to express that in inspector code. Most clients always send this
- // message anyway.
- tester
- .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
- .await;
- tester
- .assert_received_messages(&[r#"{"id":3,"result":{}}"#], &[])
- .await;
- assert_eq!(&tester.stderr_line(), "Debugger session started.");
- tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
- assert_eq!(&tester.stderr_line(), "did run");
- assert!(temp_dir.path().join("hello.txt").exists());
- tester.child.kill().unwrap();
- }
-}
diff --git a/cli/tests/install_tests.rs b/cli/tests/install_tests.rs
deleted file mode 100644
index 4b6efc924..000000000
--- a/cli/tests/install_tests.rs
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::fs;
-use std::process::Command;
-use test_util as util;
-use test_util::assert_contains;
-use test_util::assert_ends_with;
-use test_util::TempDir;
-
-mod install {
- use super::*;
-
- #[test]
- fn install_basic() {
- let _guard = util::http_server();
- let temp_dir = TempDir::new();
- let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
-
- // ensure a lockfile doesn't get created or updated locally
- temp_dir.write("deno.json", "{}");
-
- let status = util::deno_cmd()
- .current_dir(temp_dir.path())
- .arg("install")
- .arg("--check")
- .arg("--name")
- .arg("echo_test")
- .arg("http://localhost:4545/echo.ts")
- .envs([
- ("HOME", temp_dir_str.as_str()),
- ("USERPROFILE", temp_dir_str.as_str()),
- ("DENO_INSTALL_ROOT", ""),
- ])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
-
- // no lockfile should be created locally
- assert!(!temp_dir.path().join("deno.lock").exists());
-
- let mut file_path = temp_dir.path().join(".deno/bin/echo_test");
- assert!(file_path.exists());
-
- if cfg!(windows) {
- file_path = file_path.with_extension("cmd");
- }
-
- let content = fs::read_to_string(&file_path).unwrap();
- // ensure there's a trailing newline so the shell script can be
- // more versatile.
- assert_eq!(content.chars().last().unwrap(), '\n');
-
- if cfg!(windows) {
- assert_contains!(
- content,
- r#""run" "--check" "--no-config" "http://localhost:4545/echo.ts""#
- );
- } else {
- assert_contains!(
- content,
- r#"run --check --no-config 'http://localhost:4545/echo.ts'"#
- );
- }
-
- // now uninstall
- let status = util::deno_cmd()
- .current_dir(temp_dir.path())
- .arg("uninstall")
- .arg("echo_test")
- .envs([
- ("HOME", temp_dir_str.as_str()),
- ("USERPROFILE", temp_dir_str.as_str()),
- ("DENO_INSTALL_ROOT", ""),
- ])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
-
- // ensure local lockfile still doesn't exist
- assert!(!temp_dir.path().join("deno.lock").exists());
- // ensure uninstall occurred
- assert!(!file_path.exists());
- }
-
- #[test]
- fn install_custom_dir_env_var() {
- let _guard = util::http_server();
- let temp_dir = TempDir::new();
- let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
-
- let status = util::deno_cmd()
- .current_dir(util::root_path()) // different cwd
- .arg("install")
- .arg("--check")
- .arg("--name")
- .arg("echo_test")
- .arg("http://localhost:4545/echo.ts")
- .envs([
- ("HOME", temp_dir_str.as_str()),
- ("USERPROFILE", temp_dir_str.as_str()),
- ("DENO_INSTALL_ROOT", temp_dir_str.as_str()),
- ])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
-
- let mut file_path = temp_dir.path().join("bin/echo_test");
- assert!(file_path.exists());
-
- if cfg!(windows) {
- file_path = file_path.with_extension("cmd");
- }
-
- let content = fs::read_to_string(file_path).unwrap();
- if cfg!(windows) {
- assert_contains!(
- content,
- r#""run" "--check" "--no-config" "http://localhost:4545/echo.ts""#
- );
- } else {
- assert_contains!(
- content,
- r#"run --check --no-config 'http://localhost:4545/echo.ts'"#
- );
- }
- }
-
- #[test]
- fn installer_test_local_module_run() {
- let temp_dir = TempDir::new();
- let bin_dir = temp_dir.path().join("bin");
- std::fs::create_dir(&bin_dir).unwrap();
- let status = util::deno_cmd()
- .current_dir(util::root_path())
- .arg("install")
- .arg("--name")
- .arg("echo_test")
- .arg("--root")
- .arg(temp_dir.path())
- .arg(util::testdata_path().join("echo.ts"))
- .arg("hello")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let mut file_path = bin_dir.join("echo_test");
- if cfg!(windows) {
- file_path = file_path.with_extension("cmd");
- }
- assert!(file_path.exists());
- // NOTE: using file_path here instead of exec_name, because tests
- // shouldn't mess with user's PATH env variable
- let output = Command::new(file_path)
- .current_dir(temp_dir.path())
- .arg("foo")
- .env("PATH", util::target_dir())
- .output()
- .unwrap();
- let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
- assert_ends_with!(stdout_str, "hello, foo");
- }
-
- #[test]
- fn installer_test_remote_module_run() {
- let _g = util::http_server();
- let temp_dir = TempDir::new();
- let bin_dir = temp_dir.path().join("bin");
- std::fs::create_dir(&bin_dir).unwrap();
- let status = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("install")
- .arg("--name")
- .arg("echo_test")
- .arg("--root")
- .arg(temp_dir.path())
- .arg("http://localhost:4545/echo.ts")
- .arg("hello")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let mut file_path = bin_dir.join("echo_test");
- if cfg!(windows) {
- file_path = file_path.with_extension("cmd");
- }
- assert!(file_path.exists());
- let output = Command::new(file_path)
- .current_dir(temp_dir.path())
- .arg("foo")
- .env("PATH", util::target_dir())
- .output()
- .unwrap();
- assert_ends_with!(
- std::str::from_utf8(&output.stdout).unwrap().trim(),
- "hello, foo",
- );
- }
-
- #[test]
- fn check_local_by_default() {
- let _guard = util::http_server();
- let temp_dir = TempDir::new();
- let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
-
- let status = util::deno_cmd()
- .current_dir(temp_dir.path())
- .arg("install")
- .arg(util::testdata_path().join("./install/check_local_by_default.ts"))
- .envs([
- ("HOME", temp_dir_str.as_str()),
- ("USERPROFILE", temp_dir_str.as_str()),
- ("DENO_INSTALL_ROOT", ""),
- ])
- .status()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn check_local_by_default2() {
- let _guard = util::http_server();
- let temp_dir = TempDir::new();
- let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
-
- let status = util::deno_cmd()
- .current_dir(temp_dir.path())
- .arg("install")
- .arg(util::testdata_path().join("./install/check_local_by_default2.ts"))
- .envs([
- ("HOME", temp_dir_str.as_str()),
- ("NO_COLOR", "1"),
- ("USERPROFILE", temp_dir_str.as_str()),
- ("DENO_INSTALL_ROOT", ""),
- ])
- .status()
- .unwrap();
- assert!(status.success());
- }
-}
diff --git a/cli/tests/integration/bench_tests.rs b/cli/tests/integration/bench_tests.rs
new file mode 100644
index 000000000..7953bef34
--- /dev/null
+++ b/cli/tests/integration/bench_tests.rs
@@ -0,0 +1,212 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::url::Url;
+use test_util as util;
+
+itest!(overloads {
+ args: "bench bench/overloads.ts",
+ exit_code: 0,
+ output: "bench/overloads.out",
+});
+
+itest!(meta {
+ args: "bench bench/meta.ts",
+ exit_code: 0,
+ output: "bench/meta.out",
+});
+
+itest!(pass {
+ args: "bench bench/pass.ts",
+ exit_code: 0,
+ output: "bench/pass.out",
+});
+
+itest!(ignore {
+ args: "bench bench/ignore.ts",
+ exit_code: 0,
+ output: "bench/ignore.out",
+});
+
+itest!(ignore_permissions {
+ args: "bench bench/ignore_permissions.ts",
+ exit_code: 0,
+ output: "bench/ignore_permissions.out",
+});
+
+itest!(fail {
+ args: "bench bench/fail.ts",
+ exit_code: 1,
+ output: "bench/fail.out",
+});
+
+itest!(collect {
+ args: "bench --ignore=bench/collect/ignore bench/collect",
+ exit_code: 0,
+ output: "bench/collect.out",
+});
+
+itest!(load_unload {
+ args: "bench bench/load_unload.ts",
+ exit_code: 0,
+ output: "bench/load_unload.out",
+});
+
+itest!(interval {
+ args: "bench bench/interval.ts",
+ exit_code: 0,
+ output: "bench/interval.out",
+});
+
+itest!(quiet {
+ args: "bench --quiet bench/quiet.ts",
+ exit_code: 0,
+ output: "bench/quiet.out",
+});
+
+itest!(only {
+ args: "bench bench/only.ts",
+ exit_code: 1,
+ output: "bench/only.out",
+});
+
+itest!(multifile_summary {
+ args: "bench bench/group_baseline.ts bench/pass.ts bench/group_baseline.ts",
+ exit_code: 0,
+ output: "bench/multifile_summary.out",
+});
+
+itest!(no_check {
+ args: "bench --no-check bench/no_check.ts",
+ exit_code: 1,
+ output: "bench/no_check.out",
+});
+
+itest!(allow_all {
+ args: "bench --allow-all bench/allow_all.ts",
+ exit_code: 0,
+ output: "bench/allow_all.out",
+});
+
+itest!(allow_none {
+ args: "bench bench/allow_none.ts",
+ exit_code: 1,
+ output: "bench/allow_none.out",
+});
+
+itest!(exit_sanitizer {
+ args: "bench bench/exit_sanitizer.ts",
+ output: "bench/exit_sanitizer.out",
+ exit_code: 1,
+});
+
+itest!(clear_timeout {
+ args: "bench bench/clear_timeout.ts",
+ exit_code: 0,
+ output: "bench/clear_timeout.out",
+});
+
+itest!(finally_timeout {
+ args: "bench bench/finally_timeout.ts",
+ exit_code: 1,
+ output: "bench/finally_timeout.out",
+});
+
+itest!(group_baseline {
+ args: "bench bench/group_baseline.ts",
+ exit_code: 0,
+ output: "bench/group_baseline.out",
+});
+
+itest!(unresolved_promise {
+ args: "bench bench/unresolved_promise.ts",
+ exit_code: 1,
+ output: "bench/unresolved_promise.out",
+});
+
+itest!(unhandled_rejection {
+ args: "bench bench/unhandled_rejection.ts",
+ exit_code: 1,
+ output: "bench/unhandled_rejection.out",
+});
+
+itest!(filter {
+ args: "bench --filter=foo bench/filter",
+ exit_code: 0,
+ output: "bench/filter.out",
+});
+
+itest!(no_prompt_by_default {
+ args: "bench --quiet bench/no_prompt_by_default.ts",
+ exit_code: 1,
+ output: "bench/no_prompt_by_default.out",
+});
+
+itest!(no_prompt_with_denied_perms {
+ args: "bench --quiet --allow-read bench/no_prompt_with_denied_perms.ts",
+ exit_code: 1,
+ output: "bench/no_prompt_with_denied_perms.out",
+});
+
+itest!(check_local_by_default {
+ args: "bench --quiet bench/check_local_by_default.ts",
+ output: "bench/check_local_by_default.out",
+ http_server: true,
+});
+
+itest!(check_local_by_default2 {
+ args: "bench --quiet bench/check_local_by_default2.ts",
+ output: "bench/check_local_by_default2.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(bench_with_config {
+ args: "bench --config bench/collect/deno.jsonc bench/collect",
+ exit_code: 0,
+ output: "bench/collect.out",
+});
+
+itest!(bench_with_config2 {
+ args: "bench --config bench/collect/deno2.jsonc bench/collect",
+ exit_code: 0,
+ output: "bench/collect2.out",
+});
+
+itest!(bench_with_malformed_config {
+ args: "bench --config bench/collect/deno.malformed.jsonc",
+ exit_code: 1,
+ output: "bench/collect_with_malformed_config.out",
+});
+
+#[test]
+fn recursive_permissions_pledge() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bench")
+ .arg("bench/recursive_permissions_pledge.js")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert!(String::from_utf8(output.stderr).unwrap().contains(
+ "pledge test permissions called before restoring previous pledge"
+ ));
+}
+
+#[test]
+fn file_protocol() {
+ let file_url =
+ Url::from_file_path(util::testdata_path().join("bench/file_protocol.ts"))
+ .unwrap()
+ .to_string();
+
+ (util::CheckOutputIntegrationTest {
+ args_vec: vec!["bench", &file_url],
+ exit_code: 0,
+ output: "bench/file_protocol.out",
+ ..Default::default()
+ })
+ .run();
+}
diff --git a/cli/tests/integration/bundle_tests.rs b/cli/tests/integration/bundle_tests.rs
new file mode 100644
index 000000000..08665091e
--- /dev/null
+++ b/cli/tests/integration/bundle_tests.rs
@@ -0,0 +1,472 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+use test_util::assert_contains;
+use test_util::assert_ends_with;
+use test_util::TempDir;
+
+#[test]
+fn bundle_exports() {
+ // First we have to generate a bundle of some module that has exports.
+ let mod1 = util::testdata_path().join("subdir/mod1.ts");
+ assert!(mod1.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("mod1.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(mod1)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ // Now we try to use that bundle from another module.
+ let test = t.path().join("test.js");
+ std::fs::write(
+ &test,
+ "
+ import { printHello3 } from \"./mod1.bundle.js\";
+ printHello3(); ",
+ )
+ .unwrap();
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&test)
+ .output()
+ .unwrap();
+ // check the output of the test.ts program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_exports_no_check() {
+ // First we have to generate a bundle of some module that has exports.
+ let mod1 = util::testdata_path().join("subdir/mod1.ts");
+ assert!(mod1.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("mod1.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(mod1)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ // Now we try to use that bundle from another module.
+ let test = t.path().join("test.js");
+ std::fs::write(
+ &test,
+ "
+ import { printHello3 } from \"./mod1.bundle.js\";
+ printHello3(); ",
+ )
+ .unwrap();
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&test)
+ .output()
+ .unwrap();
+ // check the output of the test.ts program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_circular() {
+ // First we have to generate a bundle of some module that has exports.
+ let circular1_path = util::testdata_path().join("subdir/circular1.ts");
+ assert!(circular1_path.is_file());
+ let t = TempDir::new();
+ let bundle_path = t.path().join("circular1.bundle.js");
+
+ // run this twice to ensure it works even when cached
+ for _ in 0..2 {
+ let mut deno = util::deno_cmd_with_deno_dir(&t)
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(&circular1_path)
+ .arg(&bundle_path)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle_path.is_file());
+ }
+
+ let output = util::deno_cmd_with_deno_dir(&t)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&bundle_path)
+ .output()
+ .unwrap();
+ // check the output of the the bundle program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "f2\nf1",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_single_module() {
+ // First we have to generate a bundle of some module that has exports.
+ let single_module = util::testdata_path().join("subdir/single_module.ts");
+ assert!(single_module.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("single_module.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(single_module)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&bundle)
+ .output()
+ .unwrap();
+ // check the output of the the bundle program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello world!",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_tla() {
+ // First we have to generate a bundle of some module that has exports.
+ let tla_import = util::testdata_path().join("subdir/tla.ts");
+ assert!(tla_import.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("tla.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(tla_import)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ // Now we try to use that bundle from another module.
+ let test = t.path().join("test.js");
+ std::fs::write(
+ &test,
+ "
+ import { foo } from \"./tla.bundle.js\";
+ console.log(foo); ",
+ )
+ .unwrap();
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&test)
+ .output()
+ .unwrap();
+ // check the output of the test.ts program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_js() {
+ // First we have to generate a bundle of some module that has exports.
+ let mod6 = util::testdata_path().join("subdir/mod6.js");
+ assert!(mod6.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("mod6.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(mod6)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&bundle)
+ .output()
+ .unwrap();
+ // check that nothing went to stderr
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_dynamic_import() {
+ let _g = util::http_server();
+ let dynamic_import = util::testdata_path().join("bundle/dynamic_import.ts");
+ assert!(dynamic_import.is_file());
+ let t = TempDir::new();
+ let output_path = t.path().join("bundle_dynamic_import.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(dynamic_import)
+ .arg(&output_path)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(output_path.is_file());
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-net")
+ .arg("--quiet")
+ .arg(&output_path)
+ .output()
+ .unwrap();
+ // check the output of the test.ts program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_import_map() {
+ let import = util::testdata_path().join("bundle/import_map/main.ts");
+ let import_map_path =
+ util::testdata_path().join("bundle/import_map/import_map.json");
+ assert!(import.is_file());
+ let t = TempDir::new();
+ let output_path = t.path().join("import_map.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg("--import-map")
+ .arg(import_map_path)
+ .arg(import)
+ .arg(&output_path)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(output_path.is_file());
+
+ // Now we try to use that bundle from another module.
+ let test = t.path().join("test.js");
+ std::fs::write(
+ &test,
+ "
+ import { printHello3 } from \"./import_map.bundle.js\";
+ printHello3(); ",
+ )
+ .unwrap();
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg(&test)
+ .output()
+ .unwrap();
+ // check the output of the test.ts program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_import_map_no_check() {
+ let import = util::testdata_path().join("bundle/import_map/main.ts");
+ let import_map_path =
+ util::testdata_path().join("bundle/import_map/import_map.json");
+ assert!(import.is_file());
+ let t = TempDir::new();
+ let output_path = t.path().join("import_map.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg("--import-map")
+ .arg(import_map_path)
+ .arg(import)
+ .arg(&output_path)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(output_path.is_file());
+
+ // Now we try to use that bundle from another module.
+ let test = t.path().join("test.js");
+ std::fs::write(
+ &test,
+ "
+ import { printHello3 } from \"./import_map.bundle.js\";
+ printHello3(); ",
+ )
+ .unwrap();
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&test)
+ .output()
+ .unwrap();
+ // check the output of the test.ts program.
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "Hello",
+ );
+ assert_eq!(output.stderr, b"");
+}
+
+#[test]
+fn bundle_json_module() {
+ // First we have to generate a bundle of some module that has exports.
+ let mod7 = util::testdata_path().join("subdir/mod7.js");
+ assert!(mod7.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("mod7.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(mod7)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&bundle)
+ .output()
+ .unwrap();
+ // check that nothing went to stderr
+ assert_eq!(output.stderr, b"");
+ // ensure the output looks right
+ assert_contains!(String::from_utf8(output.stdout).unwrap(), "with space",);
+}
+
+#[test]
+fn bundle_json_module_escape_sub() {
+ // First we have to generate a bundle of some module that has exports.
+ let mod8 = util::testdata_path().join("subdir/mod8.js");
+ assert!(mod8.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("mod8.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(mod8)
+ .arg(&bundle)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&bundle)
+ .output()
+ .unwrap();
+ // check that nothing went to stderr
+ assert_eq!(output.stderr, b"");
+ // make sure the output looks right and the escapes were effective
+ assert_contains!(
+ String::from_utf8(output.stdout).unwrap(),
+ "${globalThis}`and string literal`",
+ );
+}
+
+itest!(lockfile_check_error {
+ args: "bundle --lock=bundle/lockfile/check_error.json http://127.0.0.1:4545/subdir/mod1.ts",
+ output: "bundle/lockfile/check_error.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(bundle {
+ args: "bundle subdir/mod1.ts",
+ output: "bundle/bundle.test.out",
+});
+
+itest!(bundle_jsx {
+ args: "bundle run/jsx_import_from_ts.ts",
+ output: "bundle/jsx.out",
+});
+
+itest!(error_bundle_with_bare_import {
+ args: "bundle bundle/bare_imports/error_with_bare_import.ts",
+ output: "bundle/bare_imports/error_with_bare_import.ts.out",
+ exit_code: 1,
+});
+
+itest!(ts_decorators_bundle {
+ args: "bundle bundle/decorators/ts_decorators.ts",
+ output: "bundle/decorators/ts_decorators.out",
+});
+
+itest!(bundle_export_specifier_with_alias {
+ args: "bundle bundle/file_tests-fixture16.ts",
+ output: "bundle/fixture16.out",
+});
+
+itest!(bundle_ignore_directives {
+ args: "bundle subdir/mod1.ts",
+ output: "bundle/ignore_directives.test.out",
+});
+
+itest!(check_local_by_default_no_errors {
+ args: "bundle --quiet bundle/check_local_by_default/no_errors.ts",
+ output: "bundle/check_local_by_default/no_errors.out",
+ http_server: true,
+});
+
+itest!(check_local_by_default_type_error {
+ args: "bundle --quiet bundle/check_local_by_default/type_error.ts",
+ output: "bundle/check_local_by_default/type_error.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(bundle_shebang_file {
+ args: "bundle subdir/shebang_file.js",
+ output: "bundle/shebang_file.bundle.out",
+});
diff --git a/cli/tests/integration/cache_tests.rs b/cli/tests/integration/cache_tests.rs
new file mode 100644
index 000000000..ae4dc001a
--- /dev/null
+++ b/cli/tests/integration/cache_tests.rs
@@ -0,0 +1,97 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+itest!(_036_import_map_fetch {
+ args:
+ "cache --quiet --reload --import-map=import_maps/import_map.json import_maps/test.ts",
+ output: "cache/036_import_map_fetch.out",
+ });
+
+itest!(_037_fetch_multiple {
+ args: "cache --reload --check=all run/fetch/test.ts run/fetch/other.ts",
+ http_server: true,
+ output: "cache/037_fetch_multiple.out",
+});
+
+itest!(_095_cache_with_bare_import {
+ args: "cache cache/095_cache_with_bare_import.ts",
+ output: "cache/095_cache_with_bare_import.ts.out",
+ exit_code: 1,
+});
+
+itest!(cache_extensionless {
+ args: "cache --reload --check=all http://localhost:4545/subdir/no_js_ext",
+ output: "cache/cache_extensionless.out",
+ http_server: true,
+});
+
+itest!(cache_random_extension {
+ args:
+ "cache --reload --check=all http://localhost:4545/subdir/no_js_ext@1.0.0",
+ output: "cache/cache_random_extension.out",
+ http_server: true,
+});
+
+itest!(performance_stats {
+ args: "cache --reload --check=all --log-level debug run/002_hello.ts",
+ output: "cache/performance_stats.out",
+});
+
+itest!(redirect_cache {
+ http_server: true,
+ args:
+ "cache --reload --check=all http://localhost:4548/subdir/redirects/a.ts",
+ output: "cache/redirect_cache.out",
+});
+
+itest!(ignore_require {
+ args: "cache --reload --no-check cache/ignore_require.js",
+ output_str: Some(""),
+ exit_code: 0,
+});
+
+// This test only runs on linux, because it hardcodes the XDG_CACHE_HOME env var
+// which is only used on linux.
+#[cfg(target_os = "linux")]
+#[test]
+fn relative_home_dir() {
+ use test_util as util;
+ use test_util::TempDir;
+
+ let deno_dir = TempDir::new_in(&util::testdata_path());
+ let path = deno_dir.path().strip_prefix(util::testdata_path()).unwrap();
+
+ let mut deno_cmd = util::deno_cmd();
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .env("XDG_CACHE_HOME", path)
+ .env_remove("HOME")
+ .env_remove("DENO_DIR")
+ .arg("cache")
+ .arg("--reload")
+ .arg("--no-check")
+ .arg("run/002_hello.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"");
+}
+
+itest!(check_local_by_default {
+ args: "cache --quiet cache/check_local_by_default.ts",
+ output: "cache/check_local_by_default.out",
+ http_server: true,
+});
+
+itest!(check_local_by_default2 {
+ args: "cache --quiet cache/check_local_by_default2.ts",
+ output: "cache/check_local_by_default2.out",
+ http_server: true,
+});
+
+itest!(json_import {
+ // should not error
+ args: "cache --quiet cache/json_import/main.ts",
+});
diff --git a/cli/tests/integration/cert_tests.rs b/cli/tests/integration/cert_tests.rs
new file mode 100644
index 000000000..0a403c39a
--- /dev/null
+++ b/cli/tests/integration/cert_tests.rs
@@ -0,0 +1,315 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_runtime::deno_net::ops_tls::TlsStream;
+use deno_runtime::deno_tls::rustls;
+use deno_runtime::deno_tls::rustls_pemfile;
+use std::io::BufReader;
+use std::io::Cursor;
+use std::io::Read;
+use std::process::Command;
+use std::sync::Arc;
+use test_util as util;
+use test_util::TempDir;
+use tokio::task::LocalSet;
+
+itest_flaky!(cafile_url_imports {
+ args: "run --quiet --reload --cert tls/RootCA.pem cert/cafile_url_imports.ts",
+ output: "cert/cafile_url_imports.ts.out",
+ http_server: true,
+});
+
+itest_flaky!(cafile_ts_fetch {
+ args:
+ "run --quiet --reload --allow-net --cert tls/RootCA.pem cert/cafile_ts_fetch.ts",
+ output: "cert/cafile_ts_fetch.ts.out",
+ http_server: true,
+ });
+
+itest_flaky!(cafile_eval {
+ args: "eval --cert tls/RootCA.pem fetch('https://localhost:5545/cert/cafile_ts_fetch.ts.out').then(r=>r.text()).then(t=>console.log(t.trimEnd()))",
+ output: "cert/cafile_ts_fetch.ts.out",
+ http_server: true,
+ });
+
+itest_flaky!(cafile_info {
+ args:
+ "info --quiet --cert tls/RootCA.pem https://localhost:5545/cert/cafile_info.ts",
+ output: "cert/cafile_info.ts.out",
+ http_server: true,
+ });
+
+itest_flaky!(cafile_url_imports_unsafe_ssl {
+ args: "run --quiet --reload --unsafely-ignore-certificate-errors=localhost cert/cafile_url_imports.ts",
+ output: "cert/cafile_url_imports_unsafe_ssl.ts.out",
+ http_server: true,
+ });
+
+itest_flaky!(cafile_ts_fetch_unsafe_ssl {
+ args:
+ "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors cert/cafile_ts_fetch.ts",
+ output: "cert/cafile_ts_fetch_unsafe_ssl.ts.out",
+ http_server: true,
+ });
+
+itest!(deno_land_unsafe_ssl {
+ args:
+ "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=deno.land cert/deno_land_unsafe_ssl.ts",
+ output: "cert/deno_land_unsafe_ssl.ts.out",
+ });
+
+itest!(ip_address_unsafe_ssl {
+ args:
+ "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=1.1.1.1 cert/ip_address_unsafe_ssl.ts",
+ output: "cert/ip_address_unsafe_ssl.ts.out",
+ });
+
+itest!(localhost_unsafe_ssl {
+ args:
+ "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=deno.land cert/cafile_url_imports.ts",
+ output: "cert/localhost_unsafe_ssl.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+#[flaky_test::flaky_test]
+fn cafile_env_fetch() {
+ use deno_core::url::Url;
+ let _g = util::http_server();
+ let deno_dir = TempDir::new();
+ let module_url =
+ Url::parse("https://localhost:5545/cert/cafile_url_imports.ts").unwrap();
+ let cafile = util::testdata_path().join("tls/RootCA.pem");
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .env("DENO_CERT", cafile)
+ .current_dir(util::testdata_path())
+ .arg("cache")
+ .arg(module_url.to_string())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+}
+
+#[flaky_test::flaky_test]
+fn cafile_fetch() {
+ use deno_core::url::Url;
+ let _g = util::http_server();
+ let deno_dir = TempDir::new();
+ let module_url =
+ Url::parse("http://localhost:4545/cert/cafile_url_imports.ts").unwrap();
+ let cafile = util::testdata_path().join("tls/RootCA.pem");
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("cache")
+ .arg("--cert")
+ .arg(cafile)
+ .arg(module_url.to_string())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+ let out = std::str::from_utf8(&output.stdout).unwrap();
+ assert_eq!(out, "");
+}
+
+#[flaky_test::flaky_test]
+fn cafile_install_remote_module() {
+ let _g = util::http_server();
+ let temp_dir = TempDir::new();
+ let bin_dir = temp_dir.path().join("bin");
+ std::fs::create_dir(&bin_dir).unwrap();
+ let deno_dir = TempDir::new();
+ let cafile = util::testdata_path().join("tls/RootCA.pem");
+
+ let install_output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("install")
+ .arg("--cert")
+ .arg(cafile)
+ .arg("--root")
+ .arg(temp_dir.path())
+ .arg("-n")
+ .arg("echo_test")
+ .arg("https://localhost:5545/echo.ts")
+ .output()
+ .expect("Failed to spawn script");
+ println!("{}", std::str::from_utf8(&install_output.stdout).unwrap());
+ eprintln!("{}", std::str::from_utf8(&install_output.stderr).unwrap());
+ assert!(install_output.status.success());
+
+ let mut echo_test_path = bin_dir.join("echo_test");
+ if cfg!(windows) {
+ echo_test_path = echo_test_path.with_extension("cmd");
+ }
+ assert!(echo_test_path.exists());
+
+ let output = Command::new(echo_test_path)
+ .current_dir(temp_dir.path())
+ .arg("foo")
+ .env("PATH", util::target_dir())
+ .output()
+ .expect("failed to spawn script");
+ let stdout = std::str::from_utf8(&output.stdout).unwrap().trim();
+ assert!(stdout.ends_with("foo"));
+}
+
+#[flaky_test::flaky_test]
+fn cafile_bundle_remote_exports() {
+ let _g = util::http_server();
+
+ // First we have to generate a bundle of some remote module that has exports.
+ let mod1 = "https://localhost:5545/subdir/mod1.ts";
+ let cafile = util::testdata_path().join("tls/RootCA.pem");
+ let t = TempDir::new();
+ let bundle = t.path().join("mod1.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg("--cert")
+ .arg(cafile)
+ .arg(mod1)
+ .arg(&bundle)
+ .spawn()
+ .expect("failed to spawn script");
+ let status = deno.wait().expect("failed to wait for the child process");
+ assert!(status.success());
+ assert!(bundle.is_file());
+
+ // Now we try to use that bundle from another module.
+ let test = t.path().join("test.js");
+ std::fs::write(
+ &test,
+ "
+ import { printHello3 } from \"./mod1.bundle.js\";
+ printHello3(); ",
+ )
+ .expect("error writing file");
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg(&test)
+ .output()
+ .expect("failed to spawn script");
+ // check the output of the test.ts program.
+ assert!(std::str::from_utf8(&output.stdout)
+ .unwrap()
+ .trim()
+ .ends_with("Hello"));
+ assert_eq!(output.stderr, b"");
+}
+
+#[tokio::test]
+async fn listen_tls_alpn() {
+ // TLS streams require the presence of an ambient local task set to gracefully
+ // close dropped connections in the background.
+ LocalSet::new()
+ .run_until(async {
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("--quiet")
+ .arg("--allow-net")
+ .arg("--allow-read")
+ .arg("./cert/listen_tls_alpn.ts")
+ .arg("4504")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let stdout = child.stdout.as_mut().unwrap();
+ let mut msg = [0; 5];
+ let read = stdout.read(&mut msg).unwrap();
+ assert_eq!(read, 5);
+ assert_eq!(&msg, b"READY");
+
+ let mut reader = &mut BufReader::new(Cursor::new(include_bytes!(
+ "../testdata/tls/RootCA.crt"
+ )));
+ let certs = rustls_pemfile::certs(&mut reader).unwrap();
+ let mut root_store = rustls::RootCertStore::empty();
+ root_store.add_parsable_certificates(&certs);
+ let mut cfg = rustls::ClientConfig::builder()
+ .with_safe_defaults()
+ .with_root_certificates(root_store)
+ .with_no_client_auth();
+ cfg.alpn_protocols.push(b"foobar".to_vec());
+ let cfg = Arc::new(cfg);
+
+ let hostname = rustls::ServerName::try_from("localhost").unwrap();
+
+ let tcp_stream = tokio::net::TcpStream::connect("localhost:4504")
+ .await
+ .unwrap();
+ let mut tls_stream =
+ TlsStream::new_client_side(tcp_stream, cfg, hostname);
+
+ tls_stream.handshake().await.unwrap();
+
+ let (_, rustls_connection) = tls_stream.get_ref();
+ let alpn = rustls_connection.alpn_protocol().unwrap();
+ assert_eq!(alpn, b"foobar");
+
+ let status = child.wait().unwrap();
+ assert!(status.success());
+ })
+ .await;
+}
+
+#[tokio::test]
+async fn listen_tls_alpn_fail() {
+ // TLS streams require the presence of an ambient local task set to gracefully
+ // close dropped connections in the background.
+ LocalSet::new()
+ .run_until(async {
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("--quiet")
+ .arg("--allow-net")
+ .arg("--allow-read")
+ .arg("./cert/listen_tls_alpn_fail.ts")
+ .arg("4505")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let stdout = child.stdout.as_mut().unwrap();
+ let mut msg = [0; 5];
+ let read = stdout.read(&mut msg).unwrap();
+ assert_eq!(read, 5);
+ assert_eq!(&msg, b"READY");
+
+ let mut reader = &mut BufReader::new(Cursor::new(include_bytes!(
+ "../testdata/tls/RootCA.crt"
+ )));
+ let certs = rustls_pemfile::certs(&mut reader).unwrap();
+ let mut root_store = rustls::RootCertStore::empty();
+ root_store.add_parsable_certificates(&certs);
+ let mut cfg = rustls::ClientConfig::builder()
+ .with_safe_defaults()
+ .with_root_certificates(root_store)
+ .with_no_client_auth();
+ cfg.alpn_protocols.push(b"boofar".to_vec());
+ let cfg = Arc::new(cfg);
+
+ let hostname = rustls::ServerName::try_from("localhost").unwrap();
+
+ let tcp_stream = tokio::net::TcpStream::connect("localhost:4505")
+ .await
+ .unwrap();
+ let mut tls_stream =
+ TlsStream::new_client_side(tcp_stream, cfg, hostname);
+
+ tls_stream.handshake().await.unwrap_err();
+
+ let (_, rustls_connection) = tls_stream.get_ref();
+ assert!(rustls_connection.alpn_protocol().is_none());
+
+ let status = child.wait().unwrap();
+ assert!(status.success());
+ })
+ .await;
+}
diff --git a/cli/tests/integration/check_tests.rs b/cli/tests/integration/check_tests.rs
new file mode 100644
index 000000000..38301f079
--- /dev/null
+++ b/cli/tests/integration/check_tests.rs
@@ -0,0 +1,219 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::process::Command;
+use std::process::Stdio;
+use test_util as util;
+use util::TempDir;
+
+itest!(_095_check_with_bare_import {
+ args: "check cache/095_cache_with_bare_import.ts",
+ output: "cache/095_cache_with_bare_import.ts.out",
+ exit_code: 1,
+});
+
+itest!(check_extensionless {
+ args: "check --reload http://localhost:4545/subdir/no_js_ext",
+ output: "cache/cache_extensionless.out",
+ http_server: true,
+});
+
+itest!(check_random_extension {
+ args: "check --reload http://localhost:4545/subdir/no_js_ext@1.0.0",
+ output: "cache/cache_random_extension.out",
+ http_server: true,
+});
+
+itest!(check_all {
+ args: "check --quiet --all check/check_all.ts",
+ output: "check/check_all.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(check_all_local {
+ args: "check --quiet check/check_all.ts",
+ output_str: Some(""),
+ http_server: true,
+});
+
+itest!(module_detection_force {
+ args: "check --quiet check/module_detection_force/main.ts",
+ output_str: Some(""),
+});
+
+// Regression test for https://github.com/denoland/deno/issues/14937.
+itest!(declaration_header_file_with_no_exports {
+ args: "check --quiet check/declaration_header_file_with_no_exports.ts",
+ output_str: Some(""),
+});
+
+itest!(check_npm_install_diagnostics {
+ args: "check --quiet check/npm_install_diagnostics/main.ts",
+ output: "check/npm_install_diagnostics/main.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+});
+
+itest!(check_export_equals_declaration_file {
+ args: "check --quiet check/export_equals_declaration_file/main.ts",
+ exit_code: 0,
+});
+
+itest!(check_static_response_json {
+ args: "check --quiet check/response_json.ts",
+ exit_code: 0,
+});
+
+itest!(check_no_error_truncation {
+ args: "check --quiet check/no_error_truncation/main.ts --config check/no_error_truncation/deno.json",
+ output: "check/no_error_truncation/main.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+ });
+
+#[test]
+fn cache_switching_config_then_no_config() {
+ let deno_dir = util::new_deno_dir();
+ assert!(does_type_checking(&deno_dir, true));
+ assert!(does_type_checking(&deno_dir, false));
+
+ // should now not do type checking even when it changes
+ // configs because it previously did
+ assert!(!does_type_checking(&deno_dir, true));
+ assert!(!does_type_checking(&deno_dir, false));
+
+ fn does_type_checking(deno_dir: &util::TempDir, with_config: bool) -> bool {
+ let mut cmd = util::deno_cmd_with_deno_dir(deno_dir);
+ cmd
+ .current_dir(util::testdata_path())
+ .stderr(Stdio::piped())
+ .arg("check")
+ .arg("check/cache_config_on_off/main.ts");
+ if with_config {
+ cmd
+ .arg("--config")
+ .arg("check/cache_config_on_off/deno.json");
+ }
+ let output = cmd.spawn().unwrap().wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ stderr.contains("Check")
+ }
+}
+
+#[test]
+fn reload_flag() {
+ // should do type checking whenever someone specifies --reload
+ let deno_dir = util::new_deno_dir();
+ assert!(does_type_checking(&deno_dir, false));
+ assert!(!does_type_checking(&deno_dir, false));
+ assert!(does_type_checking(&deno_dir, true));
+ assert!(does_type_checking(&deno_dir, true));
+ assert!(!does_type_checking(&deno_dir, false));
+
+ fn does_type_checking(deno_dir: &util::TempDir, reload: bool) -> bool {
+ let mut cmd = util::deno_cmd_with_deno_dir(deno_dir);
+ cmd
+ .current_dir(util::testdata_path())
+ .stderr(Stdio::piped())
+ .arg("check")
+ .arg("check/cache_config_on_off/main.ts");
+ if reload {
+ cmd.arg("--reload");
+ }
+ let output = cmd.spawn().unwrap().wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ stderr.contains("Check")
+ }
+}
+
+#[test]
+fn typecheck_declarations_ns() {
+ let output = util::deno_cmd()
+ .arg("test")
+ .arg("--doc")
+ .arg(util::root_path().join("cli/tsc/dts/lib.deno.ns.d.ts"))
+ .output()
+ .unwrap();
+ println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
+ println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
+ assert!(output.status.success());
+}
+
+#[test]
+fn typecheck_declarations_unstable() {
+ let output = util::deno_cmd()
+ .arg("test")
+ .arg("--doc")
+ .arg("--unstable")
+ .arg(util::root_path().join("cli/tsc/dts/lib.deno.unstable.d.ts"))
+ .output()
+ .unwrap();
+ println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
+ println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
+ assert!(output.status.success());
+}
+
+#[test]
+fn typecheck_core() {
+ let deno_dir = TempDir::new();
+ let test_file = deno_dir.path().join("test_deno_core_types.ts");
+ std::fs::write(
+ &test_file,
+ format!(
+ "import \"{}\";",
+ deno_core::resolve_path(
+ util::root_path()
+ .join("core/lib.deno_core.d.ts")
+ .to_str()
+ .unwrap()
+ )
+ .unwrap()
+ ),
+ )
+ .unwrap();
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .arg("run")
+ .arg(test_file.to_str().unwrap())
+ .output()
+ .unwrap();
+ println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
+ println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
+ assert!(output.status.success());
+}
+
+#[test]
+fn ts_no_recheck_on_redirect() {
+ let deno_dir = util::new_deno_dir();
+ let e = util::deno_exe_path();
+
+ let redirect_ts = util::testdata_path().join("run/017_import_redirect.ts");
+ assert!(redirect_ts.is_file());
+ let mut cmd = Command::new(e.clone());
+ cmd.env("DENO_DIR", deno_dir.path());
+ let mut initial = cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg(redirect_ts.clone())
+ .spawn()
+ .expect("failed to span script");
+ let status_initial =
+ initial.wait().expect("failed to wait for child process");
+ assert!(status_initial.success());
+
+ let mut cmd = Command::new(e);
+ cmd.env("DENO_DIR", deno_dir.path());
+ let output = cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg(redirect_ts)
+ .output()
+ .expect("failed to spawn script");
+
+ assert!(std::str::from_utf8(&output.stderr).unwrap().is_empty());
+}
diff --git a/cli/tests/integration/compile_tests.rs b/cli/tests/integration/compile_tests.rs
new file mode 100644
index 000000000..fdeadecd0
--- /dev/null
+++ b/cli/tests/integration/compile_tests.rs
@@ -0,0 +1,566 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::fs::File;
+use std::process::Command;
+use test_util as util;
+use test_util::TempDir;
+
+#[test]
+fn compile() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("welcome.exe")
+ } else {
+ dir.path().join("welcome")
+ };
+ // try this twice to ensure it works with the cache
+ for _ in 0..2 {
+ let output = util::deno_cmd_with_deno_dir(&dir)
+ .current_dir(util::root_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./test_util/std/examples/welcome.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(&exe)
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, "Welcome to Deno!\n".as_bytes());
+ }
+}
+
+#[test]
+fn standalone_args() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("args.exe")
+ } else {
+ dir.path().join("args")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/args.ts")
+ .arg("a")
+ .arg("b")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .arg("foo")
+ .arg("--bar")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"a\nb\nfoo\n--bar\n--unstable\n");
+}
+
+#[test]
+fn standalone_error() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("error.exe")
+ } else {
+ dir.path().join("error")
+ };
+ let testdata_path = util::testdata_path();
+ let output = util::deno_cmd()
+ .current_dir(&testdata_path)
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_error.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(output.stdout, b"");
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ let stderr = util::strip_ansi_codes(&stderr).to_string();
+ // On Windows, we cannot assert the file path (because '\').
+ // Instead we just check for relevant output.
+ assert!(stderr.contains("error: Uncaught Error: boom!"));
+ assert!(stderr.contains("throw new Error(\"boom!\");"));
+ assert!(stderr.contains("\n at boom (file://"));
+ assert!(stderr.contains("standalone_error.ts:2:11"));
+ assert!(stderr.contains("at foo (file://"));
+ assert!(stderr.contains("standalone_error.ts:5:5"));
+ assert!(stderr.contains("standalone_error.ts:7:1"));
+}
+
+#[test]
+fn standalone_error_module_with_imports() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("error.exe")
+ } else {
+ dir.path().join("error")
+ };
+ let testdata_path = util::testdata_path();
+ let output = util::deno_cmd()
+ .current_dir(&testdata_path)
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_error_module_with_imports_1.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(output.stdout, b"hello\n");
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ let stderr = util::strip_ansi_codes(&stderr).to_string();
+ // On Windows, we cannot assert the file path (because '\').
+ // Instead we just check for relevant output.
+ assert!(stderr.contains("error: Uncaught Error: boom!"));
+ assert!(stderr.contains("throw new Error(\"boom!\");"));
+ assert!(stderr.contains("\n at file://"));
+ assert!(stderr.contains("standalone_error_module_with_imports_2.ts:2:7"));
+}
+
+#[test]
+fn standalone_load_datauri() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("load_datauri.exe")
+ } else {
+ dir.path().join("load_datauri")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_import_datauri.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Hello Deno!\n");
+}
+
+// https://github.com/denoland/deno/issues/13704
+#[test]
+fn standalone_follow_redirects() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("follow_redirects.exe")
+ } else {
+ dir.path().join("follow_redirects")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_follow_redirects.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Hello\n");
+}
+
+#[test]
+fn compile_with_file_exists_error() {
+ let dir = TempDir::new();
+ let output_path = if cfg!(windows) {
+ dir.path().join(r"args\")
+ } else {
+ dir.path().join("args/")
+ };
+ let file_path = dir.path().join("args");
+ File::create(&file_path).unwrap();
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&output_path)
+ .arg("./compile/args.ts")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let expected_stderr = format!(
+ concat!(
+ "Could not compile to file '{}' because its parent directory ",
+ "is an existing file. You can use the `--output <file-path>` flag to ",
+ "provide an alternative name.\n",
+ ),
+ file_path.display(),
+ );
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert!(stderr.contains(&expected_stderr));
+}
+
+#[test]
+fn compile_with_directory_exists_error() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("args.exe")
+ } else {
+ dir.path().join("args")
+ };
+ std::fs::create_dir(&exe).unwrap();
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/args.ts")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let expected_stderr = format!(
+ concat!(
+ "Could not compile to file '{}' because a directory exists with ",
+ "the same name. You can use the `--output <file-path>` flag to ",
+ "provide an alternative name."
+ ),
+ exe.display()
+ );
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert!(stderr.contains(&expected_stderr));
+}
+
+#[test]
+fn compile_with_conflict_file_exists_error() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("args.exe")
+ } else {
+ dir.path().join("args")
+ };
+ std::fs::write(&exe, b"SHOULD NOT BE OVERWRITTEN").unwrap();
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/args.ts")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let expected_stderr = format!(
+ concat!(
+ "Could not compile to file '{}' because the file already exists ",
+ "and cannot be overwritten. Please delete the existing file or ",
+ "use the `--output <file-path` flag to provide an alternative name."
+ ),
+ exe.display()
+ );
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ dbg!(&stderr);
+ assert!(stderr.contains(&expected_stderr));
+ assert!(std::fs::read(&exe)
+ .unwrap()
+ .eq(b"SHOULD NOT BE OVERWRITTEN"));
+}
+
+#[test]
+fn compile_and_overwrite_file() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("args.exe")
+ } else {
+ dir.path().join("args")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/args.ts")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert!(&exe.exists());
+
+ let recompile_output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/args.ts")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(recompile_output.status.success());
+}
+
+#[test]
+fn standalone_runtime_flags() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("flags.exe")
+ } else {
+ dir.path().join("flags")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--allow-read")
+ .arg("--seed")
+ .arg("1")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_runtime_flags.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let stdout_str = String::from_utf8(output.stdout).unwrap();
+ assert_eq!(util::strip_ansi_codes(&stdout_str), "0.147205063401058\n");
+ let stderr_str = String::from_utf8(output.stderr).unwrap();
+ assert!(util::strip_ansi_codes(&stderr_str)
+ .contains("PermissionDenied: Requires write access"));
+}
+
+#[test]
+fn standalone_import_map() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("import_map.exe")
+ } else {
+ dir.path().join("import_map")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--allow-read")
+ .arg("--import-map")
+ .arg("compile/standalone_import_map.json")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_import_map.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[test]
+fn standalone_import_map_config_file() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("import_map.exe")
+ } else {
+ dir.path().join("import_map")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--allow-read")
+ .arg("--config")
+ .arg("compile/standalone_import_map_config.json")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./compile/standalone_import_map.ts")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = Command::new(exe)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[test]
+// https://github.com/denoland/deno/issues/12670
+fn skip_rebundle() {
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("hello_world.exe")
+ } else {
+ dir.path().join("hello_world")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg("./run/001_hello.js")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+
+ //no "Bundle testdata_path/run/001_hello.js" in output
+ assert!(!String::from_utf8(output.stderr).unwrap().contains("Bundle"));
+
+ let output = Command::new(exe)
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, "Hello World\n".as_bytes());
+}
+
+#[test]
+fn check_local_by_default() {
+ let _guard = util::http_server();
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("welcome.exe")
+ } else {
+ dir.path().join("welcome")
+ };
+ let status = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg(util::testdata_path().join("./compile/check_local_by_default.ts"))
+ .status()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn check_local_by_default2() {
+ let _guard = util::http_server();
+ let dir = TempDir::new();
+ let exe = if cfg!(windows) {
+ dir.path().join("welcome.exe")
+ } else {
+ dir.path().join("welcome")
+ };
+ let output = util::deno_cmd()
+ .current_dir(util::root_path())
+ .env("NO_COLOR", "1")
+ .arg("compile")
+ .arg("--unstable")
+ .arg("--output")
+ .arg(&exe)
+ .arg(util::testdata_path().join("./compile/check_local_by_default2.ts"))
+ .output()
+ .unwrap();
+ assert!(!output.status.success());
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert!(stdout.is_empty());
+ assert!(stderr.contains(
+ r#"error: TS2322 [ERROR]: Type '12' is not assignable to type '"b"'."#
+ ));
+}
diff --git a/cli/tests/integration/coverage_tests.rs b/cli/tests/integration/coverage_tests.rs
new file mode 100644
index 000000000..5f82971c6
--- /dev/null
+++ b/cli/tests/integration/coverage_tests.rs
@@ -0,0 +1,376 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::fs;
+use test_util as util;
+use test_util::TempDir;
+
+#[test]
+fn branch() {
+ run_coverage_text("branch", "ts");
+}
+
+#[test]
+fn complex() {
+ run_coverage_text("complex", "ts");
+}
+
+#[test]
+fn final_blankline() {
+ run_coverage_text("final_blankline", "js");
+}
+
+#[test]
+fn no_snaps() {
+ no_snaps_included("no_snaps_included", "ts");
+}
+
+#[test]
+fn error_if_invalid_cache() {
+ let deno_dir = TempDir::new();
+ let deno_dir_path = deno_dir.path();
+ let tempdir = TempDir::new();
+ let tempdir = tempdir.path().join("cov");
+
+ let invalid_cache_path = util::testdata_path().join("coverage/invalid_cache");
+ let mod_before_path = util::testdata_path()
+ .join(&invalid_cache_path)
+ .join("mod_before.ts");
+ let mod_after_path = util::testdata_path()
+ .join(&invalid_cache_path)
+ .join("mod_after.ts");
+ let mod_test_path = util::testdata_path()
+ .join(&invalid_cache_path)
+ .join("mod.test.ts");
+
+ let mod_temp_path = deno_dir_path.join("mod.ts");
+ let mod_test_temp_path = deno_dir_path.join("mod.test.ts");
+
+ // Write the inital mod.ts file
+ std::fs::copy(mod_before_path, &mod_temp_path).unwrap();
+ // And the test file
+ std::fs::copy(mod_test_path, mod_test_temp_path).unwrap();
+
+ // Generate coverage
+ let status = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(deno_dir_path)
+ .arg("test")
+ .arg("--quiet")
+ .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .status()
+ .unwrap();
+
+ assert!(status.success());
+
+ // Modify the file between deno test and deno coverage, thus invalidating the cache
+ std::fs::copy(mod_after_path, mod_temp_path).unwrap();
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(deno_dir_path)
+ .arg("coverage")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .output()
+ .unwrap();
+
+ assert!(output.stdout.is_empty());
+
+ // Expect error
+ let error =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stderr).unwrap())
+ .to_string();
+ assert!(error.contains("error: Missing transpiled source code"));
+ assert!(error.contains("Before generating coverage report, run `deno test --coverage` to ensure consistent state."));
+}
+
+fn run_coverage_text(test_name: &str, extension: &str) {
+ let deno_dir = TempDir::new();
+ let tempdir = TempDir::new();
+ let tempdir = tempdir.path().join("cov");
+
+ let status = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("-A")
+ .arg("--quiet")
+ .arg("--unstable")
+ .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
+ .arg(format!("coverage/{}_test.{}", test_name, extension))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .status()
+ .unwrap();
+
+ assert!(status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--unstable")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .output()
+ .unwrap();
+
+ // Verify there's no "Check" being printed
+ assert!(output.stderr.is_empty());
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join(format!("coverage/{}_expected.out", test_name)),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--quiet")
+ .arg("--unstable")
+ .arg("--lcov")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .output()
+ .unwrap();
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join(format!("coverage/{}_expected.lcov", test_name)),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+}
+
+#[test]
+fn multifile_coverage() {
+ let deno_dir = TempDir::new();
+ let tempdir = TempDir::new();
+ let tempdir = tempdir.path().join("cov");
+
+ let status = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("--quiet")
+ .arg("--unstable")
+ .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
+ .arg("coverage/multifile/")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .status()
+ .unwrap();
+
+ assert!(status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--unstable")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .output()
+ .unwrap();
+
+ // Verify there's no "Check" being printed
+ assert!(output.stderr.is_empty());
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join("coverage/multifile/expected.out"),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--quiet")
+ .arg("--unstable")
+ .arg("--lcov")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .output()
+ .unwrap();
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join("coverage/multifile/expected.lcov"),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+}
+
+fn no_snaps_included(test_name: &str, extension: &str) {
+ let deno_dir = TempDir::new();
+ let tempdir = TempDir::new();
+ let tempdir = tempdir.path().join("cov");
+
+ let status = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("--quiet")
+ .arg("--unstable")
+ .arg("--allow-read")
+ .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
+ .arg(format!(
+ "coverage/no_snaps_included/{}_test.{}",
+ test_name, extension
+ ))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .status()
+ .unwrap();
+
+ assert!(status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--unstable")
+ .arg("--include=no_snaps_included.ts")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .output()
+ .unwrap();
+
+ // Verify there's no "Check" being printed
+ assert!(output.stderr.is_empty());
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join("coverage/no_snaps_included/expected.out"),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+}
+
+#[test]
+fn no_transpiled_lines() {
+ let deno_dir = TempDir::new();
+ let tempdir = TempDir::new();
+ let tempdir = tempdir.path().join("cov");
+
+ let status = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("--quiet")
+ .arg(format!("--coverage={}", tempdir.to_str().unwrap()))
+ .arg("coverage/no_transpiled_lines/")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .status()
+ .unwrap();
+
+ assert!(status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--include=no_transpiled_lines/index.ts")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .output()
+ .unwrap();
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join("coverage/no_transpiled_lines/expected.out"),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("coverage")
+ .arg("--lcov")
+ .arg("--include=no_transpiled_lines/index.ts")
+ .arg(format!("{}/", tempdir.to_str().unwrap()))
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::inherit())
+ .output()
+ .unwrap();
+
+ let actual =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap())
+ .to_string();
+
+ let expected = fs::read_to_string(
+ util::testdata_path().join("coverage/no_transpiled_lines/expected.lcov"),
+ )
+ .unwrap();
+
+ if !util::wildcard_match(&expected, &actual) {
+ println!("OUTPUT\n{}\nOUTPUT", actual);
+ println!("EXPECTED\n{}\nEXPECTED", expected);
+ panic!("pattern match failed");
+ }
+
+ assert!(output.status.success());
+}
diff --git a/cli/tests/integration/doc_tests.rs b/cli/tests/integration/doc_tests.rs
new file mode 100644
index 000000000..014a1d136
--- /dev/null
+++ b/cli/tests/integration/doc_tests.rs
@@ -0,0 +1,72 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+use test_util::TempDir;
+use util::assert_contains;
+
+itest!(deno_doc_builtin {
+ args: "doc",
+ output: "doc/deno_doc_builtin.out",
+});
+
+#[test]
+fn deno_doc() {
+ let dir = TempDir::new();
+ // try this twice to ensure it works with the cache
+ for _ in 0..2 {
+ let output = util::deno_cmd_with_deno_dir(&dir)
+ .current_dir(util::testdata_path())
+ .arg("doc")
+ .arg("doc/deno_doc.ts")
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_contains!(
+ std::str::from_utf8(&output.stdout).unwrap(),
+ "function foo"
+ );
+ }
+}
+
+itest!(deno_doc_import_map {
+ args: "doc --unstable --import-map=doc/import_map.json doc/use_import_map.js",
+ output: "doc/use_import_map.out",
+});
+
+itest!(deno_doc_types_hint {
+ args: "doc doc/types_hint.ts",
+ output: "doc/types_hint.out",
+});
+
+itest!(deno_doc_types_ref {
+ args: "doc doc/types_ref.js",
+ output: "doc/types_ref.out",
+});
+
+itest!(deno_doc_types_header {
+ args: "doc --reload doc/types_header.ts",
+ output: "doc/types_header.out",
+ http_server: true,
+});
+
+itest!(_060_deno_doc_displays_all_overloads_in_details_view {
+ args:
+ "doc doc/060_deno_doc_displays_all_overloads_in_details_view.ts NS.test",
+ output: "doc/060_deno_doc_displays_all_overloads_in_details_view.ts.out",
+});
+
+itest!(deno_doc_types_header_direct {
+ args: "doc --reload http://127.0.0.1:4545/xTypeScriptTypes.js",
+ output: "doc/types_header.out",
+ http_server: true,
+});
+
+itest!(deno_doc_invalid_url {
+ args: "doc https://raw.githubusercontent.com%2Fdyedgreen%2Fdeno-sqlite%2Frework_api%2Fmod.ts",
+ output: "doc/invalid_url.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/integration/eval_tests.rs b/cli/tests/integration/eval_tests.rs
new file mode 100644
index 000000000..df2362c7f
--- /dev/null
+++ b/cli/tests/integration/eval_tests.rs
@@ -0,0 +1,79 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+
+#[test]
+fn eval_p() {
+ let output = util::deno_cmd()
+ .arg("eval")
+ .arg("-p")
+ .arg("1+2")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout_str =
+ util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap().trim());
+ assert_eq!("3", stdout_str);
+}
+
+// Make sure that snapshot flags don't affect runtime.
+#[test]
+fn eval_randomness() {
+ let mut numbers = Vec::with_capacity(10);
+ for _ in 0..10 {
+ let output = util::deno_cmd()
+ .arg("eval")
+ .arg("-p")
+ .arg("Math.random()")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout_str = util::strip_ansi_codes(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ );
+ numbers.push(stdout_str.to_string());
+ }
+ numbers.dedup();
+ assert!(numbers.len() > 1);
+}
+
+itest!(eval_basic {
+ args: "eval console.log(\"hello\")",
+ output_str: Some("hello\n"),
+});
+
+// Ugly parentheses due to whitespace delimiting problem.
+itest!(eval_ts {
+ args: "eval --quiet --ext=ts console.log((123)as(number))", // 'as' is a TS keyword only
+ output_str: Some("123\n"),
+});
+
+itest!(dyn_import_eval {
+ args: "eval import('./subdir/mod4.js').then(console.log)",
+ output: "eval/dyn_import_eval.out",
+});
+
+// Cannot write the expression to evaluate as "console.log(typeof gc)"
+// because itest! splits args on whitespace.
+itest!(v8_flags_eval {
+ args: "eval --v8-flags=--expose-gc console.log(typeof(gc))",
+ output: "run/v8_flags.js.out",
+});
+
+itest!(check_local_by_default {
+ args: "eval --quiet import('http://localhost:4545/subdir/type_error.ts').then(console.log);",
+ output: "eval/check_local_by_default.out",
+ http_server: true,
+});
+
+itest!(check_local_by_default2 {
+ args: "eval --quiet import('./eval/check_local_by_default2.ts').then(console.log);",
+ output: "eval/check_local_by_default2.out",
+ http_server: true,
+});
diff --git a/cli/tests/integration/flags_tests.rs b/cli/tests/integration/flags_tests.rs
new file mode 100644
index 000000000..6addf39ea
--- /dev/null
+++ b/cli/tests/integration/flags_tests.rs
@@ -0,0 +1,44 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+
+#[test]
+fn help_flag() {
+ let status = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("--help")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn version_short_flag() {
+ let status = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("-V")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn version_long_flag() {
+ let status = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("--version")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+itest!(types {
+ args: "types",
+ output: "types/types.out",
+});
diff --git a/cli/tests/integration/fmt_tests.rs b/cli/tests/integration/fmt_tests.rs
new file mode 100644
index 000000000..5c21b2b7d
--- /dev/null
+++ b/cli/tests/integration/fmt_tests.rs
@@ -0,0 +1,247 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+use test_util::TempDir;
+
+#[test]
+fn fmt_test() {
+ let t = TempDir::new();
+ let testdata_fmt_dir = util::testdata_path().join("fmt");
+ let fixed_js = testdata_fmt_dir.join("badly_formatted_fixed.js");
+ let badly_formatted_original_js =
+ testdata_fmt_dir.join("badly_formatted.mjs");
+ let badly_formatted_js = t.path().join("badly_formatted.js");
+ let badly_formatted_js_str = badly_formatted_js.to_str().unwrap();
+ std::fs::copy(badly_formatted_original_js, &badly_formatted_js).unwrap();
+
+ let fixed_md = testdata_fmt_dir.join("badly_formatted_fixed.md");
+ let badly_formatted_original_md = testdata_fmt_dir.join("badly_formatted.md");
+ let badly_formatted_md = t.path().join("badly_formatted.md");
+ let badly_formatted_md_str = badly_formatted_md.to_str().unwrap();
+ std::fs::copy(badly_formatted_original_md, &badly_formatted_md).unwrap();
+
+ let fixed_json = testdata_fmt_dir.join("badly_formatted_fixed.json");
+ let badly_formatted_original_json =
+ testdata_fmt_dir.join("badly_formatted.json");
+ let badly_formatted_json = t.path().join("badly_formatted.json");
+ let badly_formatted_json_str = badly_formatted_json.to_str().unwrap();
+ std::fs::copy(badly_formatted_original_json, &badly_formatted_json).unwrap();
+ // First, check formatting by ignoring the badly formatted file.
+ let status = util::deno_cmd()
+ .current_dir(&testdata_fmt_dir)
+ .arg("fmt")
+ .arg(format!(
+ "--ignore={},{},{}",
+ badly_formatted_js_str, badly_formatted_md_str, badly_formatted_json_str
+ ))
+ .arg("--check")
+ .arg(badly_formatted_js_str)
+ .arg(badly_formatted_md_str)
+ .arg(badly_formatted_json_str)
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ // No target files found
+ assert!(!status.success());
+
+ // Check without ignore.
+ let status = util::deno_cmd()
+ .current_dir(&testdata_fmt_dir)
+ .arg("fmt")
+ .arg("--check")
+ .arg(badly_formatted_js_str)
+ .arg(badly_formatted_md_str)
+ .arg(badly_formatted_json_str)
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(!status.success());
+
+ // Format the source file.
+ let status = util::deno_cmd()
+ .current_dir(&testdata_fmt_dir)
+ .arg("fmt")
+ .arg(badly_formatted_js_str)
+ .arg(badly_formatted_md_str)
+ .arg(badly_formatted_json_str)
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let expected_js = std::fs::read_to_string(fixed_js).unwrap();
+ let expected_md = std::fs::read_to_string(fixed_md).unwrap();
+ let expected_json = std::fs::read_to_string(fixed_json).unwrap();
+ let actual_js = std::fs::read_to_string(badly_formatted_js).unwrap();
+ let actual_md = std::fs::read_to_string(badly_formatted_md).unwrap();
+ let actual_json = std::fs::read_to_string(badly_formatted_json).unwrap();
+ assert_eq!(expected_js, actual_js);
+ assert_eq!(expected_md, actual_md);
+ assert_eq!(expected_json, actual_json);
+}
+
+#[test]
+fn fmt_stdin_error() {
+ use std::io::Write;
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("fmt")
+ .arg("-")
+ .stdin(std::process::Stdio::piped())
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let stdin = deno.stdin.as_mut().unwrap();
+ let invalid_js = b"import { example }";
+ stdin.write_all(invalid_js).unwrap();
+ let output = deno.wait_with_output().unwrap();
+ // Error message might change. Just check stdout empty, stderr not.
+ assert!(output.stdout.is_empty());
+ assert!(!output.stderr.is_empty());
+ assert!(!output.status.success());
+}
+
+#[test]
+fn fmt_ignore_unexplicit_files() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("fmt")
+ .arg("--check")
+ .arg("--ignore=./")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr),
+ "error: No target files found.\n"
+ );
+}
+
+#[test]
+fn fmt_auto_ignore_git_and_node_modules() {
+ use std::fs::{create_dir_all, File};
+ use std::io::Write;
+ use std::path::PathBuf;
+ fn create_bad_json(t: PathBuf) {
+ let bad_json_path = t.join("bad.json");
+ let mut bad_json_file = File::create(bad_json_path).unwrap();
+ writeln!(bad_json_file, "bad json").unwrap();
+ }
+ let temp_dir = TempDir::new();
+ let t = temp_dir.path().join("target");
+ let nest_git = t.join("nest").join(".git");
+ let git_dir = t.join(".git");
+ let nest_node_modules = t.join("nest").join("node_modules");
+ let node_modules_dir = t.join("node_modules");
+ create_dir_all(&nest_git).unwrap();
+ create_dir_all(&git_dir).unwrap();
+ create_dir_all(&nest_node_modules).unwrap();
+ create_dir_all(&node_modules_dir).unwrap();
+ create_bad_json(nest_git);
+ create_bad_json(git_dir);
+ create_bad_json(nest_node_modules);
+ create_bad_json(node_modules_dir);
+ let output = util::deno_cmd()
+ .current_dir(t)
+ .env("NO_COLOR", "1")
+ .arg("fmt")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr),
+ "error: No target files found.\n"
+ );
+}
+
+itest!(fmt_quiet_check_fmt_dir {
+ args: "fmt --check --quiet fmt/regular/",
+ output_str: Some(""),
+ exit_code: 0,
+});
+
+itest!(fmt_check_formatted_files {
+ args: "fmt --check fmt/regular/formatted1.js fmt/regular/formatted2.ts fmt/regular/formatted3.markdown fmt/regular/formatted4.jsonc",
+ output: "fmt/expected_fmt_check_formatted_files.out",
+ exit_code: 0,
+});
+
+itest!(fmt_check_ignore {
+ args: "fmt --check --ignore=fmt/regular/formatted1.js fmt/regular/",
+ output: "fmt/expected_fmt_check_ignore.out",
+ exit_code: 0,
+});
+
+itest!(fmt_check_parse_error {
+ args: "fmt --check fmt/parse_error/parse_error.ts",
+ output: "fmt/fmt_check_parse_error.out",
+ exit_code: 1,
+});
+
+itest!(fmt_stdin {
+ args: "fmt -",
+ input: Some("const a = 1\n"),
+ output_str: Some("const a = 1;\n"),
+});
+
+itest!(fmt_stdin_markdown {
+ args: "fmt --ext=md -",
+ input: Some("# Hello Markdown\n```ts\nconsole.log( \"text\")\n```\n\n```cts\nconsole.log( 5 )\n```"),
+ output_str: Some("# Hello Markdown\n\n```ts\nconsole.log(\"text\");\n```\n\n```cts\nconsole.log(5);\n```\n"),
+});
+
+itest!(fmt_stdin_json {
+ args: "fmt --ext=json -",
+ input: Some("{ \"key\": \"value\"}"),
+ output_str: Some("{ \"key\": \"value\" }\n"),
+});
+
+itest!(fmt_stdin_check_formatted {
+ args: "fmt --check -",
+ input: Some("const a = 1;\n"),
+ output_str: Some(""),
+});
+
+itest!(fmt_stdin_check_not_formatted {
+ args: "fmt --check -",
+ input: Some("const a = 1\n"),
+ output_str: Some("Not formatted stdin\n"),
+});
+
+itest!(fmt_with_config {
+ args: "fmt --config fmt/with_config/deno.jsonc fmt/with_config/subdir",
+ output: "fmt/fmt_with_config.out",
+});
+
+itest!(fmt_with_config_default {
+ args: "fmt fmt/with_config/subdir",
+ output: "fmt/fmt_with_config.out",
+});
+
+// Check if CLI flags take precedence
+itest!(fmt_with_config_and_flags {
+ args: "fmt --config fmt/with_config/deno.jsonc --ignore=fmt/with_config/subdir/a.ts,fmt/with_config/subdir/b.ts",
+ output: "fmt/fmt_with_config_and_flags.out",
+});
+
+itest!(fmt_with_malformed_config {
+ args: "fmt --config fmt/deno.malformed.jsonc",
+ output: "fmt/fmt_with_malformed_config.out",
+ exit_code: 1,
+});
+
+itest!(fmt_with_malformed_config2 {
+ args: "fmt --config fmt/deno.malformed2.jsonc",
+ output: "fmt/fmt_with_malformed_config2.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/integration/info_tests.rs b/cli/tests/integration/info_tests.rs
new file mode 100644
index 000000000..6c75deea6
--- /dev/null
+++ b/cli/tests/integration/info_tests.rs
@@ -0,0 +1,129 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+use test_util::TempDir;
+
+#[test]
+fn info_with_compiled_source() {
+ let _g = util::http_server();
+ let module_path = "http://127.0.0.1:4545/run/048_media_types_jsx.ts";
+ let t = TempDir::new();
+
+ let mut deno = util::deno_cmd()
+ .env("DENO_DIR", t.path())
+ .current_dir(util::testdata_path())
+ .arg("cache")
+ .arg(module_path)
+ .spawn()
+ .unwrap();
+ let status = deno.wait().unwrap();
+ assert!(status.success());
+
+ let output = util::deno_cmd()
+ .env("DENO_DIR", t.path())
+ .env("NO_COLOR", "1")
+ .current_dir(util::testdata_path())
+ .arg("info")
+ .arg(module_path)
+ .output()
+ .unwrap();
+
+ let str_output = std::str::from_utf8(&output.stdout).unwrap().trim();
+ // check the output of the test.ts program.
+ assert!(str_output.contains("emit: "));
+ assert_eq!(output.stderr, b"");
+}
+
+itest!(multiple_imports {
+ args: "info http://127.0.0.1:4545/run/019_media_types.ts",
+ output: "info/multiple_imports.out",
+ http_server: true,
+});
+
+itest!(info_ts_error {
+ args: "info info/031_info_ts_error.ts",
+ output: "info/031_info_ts_error.out",
+});
+
+itest!(info_flag {
+ args: "info",
+ output: "info/041_info_flag.out",
+});
+
+itest!(info_flag_location {
+ args: "info --location https://deno.land",
+ output: "info/041_info_flag_location.out",
+});
+
+itest!(info_json {
+ args: "info --json --unstable",
+ output: "info/info_json.out",
+});
+
+itest!(info_json_location {
+ args: "info --json --unstable --location https://deno.land",
+ output: "info/info_json_location.out",
+});
+
+itest!(info_flag_script_jsx {
+ args: "info http://127.0.0.1:4545/run/048_media_types_jsx.ts",
+ output: "info/049_info_flag_script_jsx.out",
+ http_server: true,
+});
+
+itest!(json_file {
+ args: "info --quiet --json --unstable info/json_output/main.ts",
+ output: "info/json_output/main.out",
+ exit_code: 0,
+});
+
+itest!(import_map_info {
+ args:
+ "info --quiet --import-map=import_maps/import_map.json import_maps/test.ts",
+ output: "info/065_import_map_info.out",
+});
+
+itest!(info_json_deps_order {
+ args: "info --unstable --json info/076_info_json_deps_order.ts",
+ output: "info/076_info_json_deps_order.out",
+});
+
+itest!(info_missing_module {
+ args: "info info/error_009_missing_js_module.js",
+ output: "info/info_missing_module.out",
+});
+
+itest!(info_recursive_modules {
+ args: "info --quiet info/info_recursive_imports_test.ts",
+ output: "info/info_recursive_imports_test.out",
+ exit_code: 0,
+});
+
+itest!(info_type_import {
+ args: "info info/info_type_import.ts",
+ output: "info/info_type_import.out",
+});
+
+itest!(_054_info_local_imports {
+ args: "info --quiet run/005_more_imports.ts",
+ output: "info/054_info_local_imports.out",
+ exit_code: 0,
+});
+
+// Tests for AssertionError where "data" is unexpectedly null when
+// a file contains only triple slash references (#11196)
+itest!(data_null_error {
+ args: "info info/data_null_error/mod.ts",
+ output: "info/data_null_error/data_null_error.out",
+});
+
+itest!(types_header_direct {
+ args: "info --reload run/type_directives_01.ts",
+ output: "info/types_header.out",
+ http_server: true,
+});
+
+itest!(with_config_override {
+ args: "info info/with_config/test.ts --config info/with_config/deno-override.json --import-map info/with_config/import_map.json",
+ output: "info/with_config/with_config.out",
+});
diff --git a/cli/tests/integration/init_tests.rs b/cli/tests/integration/init_tests.rs
new file mode 100644
index 000000000..5ba69b87f
--- /dev/null
+++ b/cli/tests/integration/init_tests.rs
@@ -0,0 +1,205 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::process::Stdio;
+use test_util as util;
+use test_util::TempDir;
+use util::assert_contains;
+
+#[test]
+fn init_subcommand_without_dir() {
+ let temp_dir = TempDir::new();
+ let cwd = temp_dir.path();
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .arg("init")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert_contains!(stderr, "Project initialized");
+ assert!(!stderr.contains("cd"));
+ assert_contains!(stderr, "deno run main.ts");
+ assert_contains!(stderr, "deno task dev");
+ assert_contains!(stderr, "deno test");
+ assert_contains!(stderr, "deno bench");
+
+ assert!(cwd.join("deno.jsonc").exists());
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("main.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("test")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ assert_contains!(stdout, "1 passed");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("bench")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[test]
+fn init_subcommand_with_dir_arg() {
+ let temp_dir = TempDir::new();
+ let cwd = temp_dir.path();
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .arg("init")
+ .arg("my_dir")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert_contains!(stderr, "Project initialized");
+ assert_contains!(stderr, "cd my_dir");
+ assert_contains!(stderr, "deno run main.ts");
+ assert_contains!(stderr, "deno task dev");
+ assert_contains!(stderr, "deno test");
+ assert_contains!(stderr, "deno bench");
+
+ assert!(cwd.join("my_dir/deno.jsonc").exists());
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("my_dir/main.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("test")
+ .arg("my_dir/main_test.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ assert_contains!(stdout, "1 passed");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("bench")
+ .arg("my_dir/main_bench.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[test]
+fn init_subcommand_with_quiet_arg() {
+ let temp_dir = TempDir::new();
+ let cwd = temp_dir.path();
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .arg("init")
+ .arg("--quiet")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ assert_eq!(stdout, "");
+ assert!(cwd.join("deno.jsonc").exists());
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("main.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("test")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ assert_contains!(stdout, "1 passed");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(cwd)
+ .env("NO_COLOR", "1")
+ .arg("bench")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
diff --git a/cli/tests/integration/inspector_tests.rs b/cli/tests/integration/inspector_tests.rs
new file mode 100644
index 000000000..af1325d64
--- /dev/null
+++ b/cli/tests/integration/inspector_tests.rs
@@ -0,0 +1,1372 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::futures::prelude::*;
+use deno_core::futures::stream::SplitSink;
+use deno_core::futures::stream::SplitStream;
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use deno_core::url;
+use deno_runtime::deno_fetch::reqwest;
+use deno_runtime::deno_websocket::tokio_tungstenite;
+use deno_runtime::deno_websocket::tokio_tungstenite::tungstenite;
+use std::io::BufRead;
+use std::process::Child;
+use test_util as util;
+use test_util::TempDir;
+use tokio::net::TcpStream;
+use util::http_server;
+
+struct InspectorTester {
+ socket_tx: SplitSink<
+ tokio_tungstenite::WebSocketStream<
+ tokio_tungstenite::MaybeTlsStream<TcpStream>,
+ >,
+ tungstenite::Message,
+ >,
+ socket_rx: SplitStream<
+ tokio_tungstenite::WebSocketStream<
+ tokio_tungstenite::MaybeTlsStream<TcpStream>,
+ >,
+ >,
+ notification_filter: Box<dyn FnMut(&str) -> bool + 'static>,
+ child: Child,
+ stderr_lines: Box<dyn Iterator<Item = String>>,
+ stdout_lines: Box<dyn Iterator<Item = String>>,
+}
+
+fn ignore_script_parsed(msg: &str) -> bool {
+ !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#)
+}
+
+impl InspectorTester {
+ async fn create<F>(mut child: Child, notification_filter: F) -> Self
+ where
+ F: FnMut(&str) -> bool + 'static,
+ {
+ let stdout = child.stdout.take().unwrap();
+ let stdout_lines =
+ std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
+
+ let stderr = child.stderr.take().unwrap();
+ let mut stderr_lines =
+ std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
+
+ let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
+
+ let (socket, response) =
+ tokio_tungstenite::connect_async(ws_url).await.unwrap();
+ assert_eq!(response.status(), 101); // Switching protocols.
+
+ let (socket_tx, socket_rx) = socket.split();
+
+ Self {
+ socket_tx,
+ socket_rx,
+ notification_filter: Box::new(notification_filter),
+ child,
+ stderr_lines: Box::new(stderr_lines),
+ stdout_lines: Box::new(stdout_lines),
+ }
+ }
+
+ async fn send_many(&mut self, messages: &[serde_json::Value]) {
+ // TODO(bartlomieju): add graceful error handling
+ for msg in messages {
+ let result = self
+ .socket_tx
+ .send(msg.to_string().into())
+ .await
+ .map_err(|e| e.into());
+ self.handle_error(result);
+ }
+ }
+
+ async fn send(&mut self, message: serde_json::Value) {
+ self.send_many(&[message]).await;
+ }
+
+ fn handle_error<T>(&mut self, result: Result<T, AnyError>) -> T {
+ match result {
+ Ok(result) => result,
+ Err(err) => {
+ let mut stdout = vec![];
+ for line in self.stdout_lines.by_ref() {
+ stdout.push(line);
+ }
+ let mut stderr = vec![];
+ for line in self.stderr_lines.by_ref() {
+ stderr.push(line);
+ }
+ let stdout = stdout.join("\n");
+ let stderr = stderr.join("\n");
+ self.child.kill().unwrap();
+
+ panic!(
+ "Inspector test failed with error: {:?}.\nstdout:\n{}\nstderr:\n{}",
+ err, stdout, stderr
+ );
+ }
+ }
+ }
+
+ async fn recv(&mut self) -> String {
+ loop {
+ let result = self.socket_rx.next().await.unwrap().map_err(|e| e.into());
+ let message = self.handle_error(result).to_string();
+ if (self.notification_filter)(&message) {
+ return message;
+ }
+ }
+ }
+
+ async fn recv_as_json(&mut self) -> serde_json::Value {
+ let msg = self.recv().await;
+ serde_json::from_str(&msg).unwrap()
+ }
+
+ async fn assert_received_messages(
+ &mut self,
+ responses: &[&str],
+ notifications: &[&str],
+ ) {
+ let expected_messages = responses.len() + notifications.len();
+ let mut responses_idx = 0;
+ let mut notifications_idx = 0;
+
+ for _ in 0..expected_messages {
+ let msg = self.recv().await;
+
+ if msg.starts_with(r#"{"id":"#) {
+ assert!(
+ msg.starts_with(responses[responses_idx]),
+ "Doesn't start with {}, instead received {}",
+ responses[responses_idx],
+ msg
+ );
+ responses_idx += 1;
+ } else {
+ assert!(
+ msg.starts_with(notifications[notifications_idx]),
+ "Doesn't start with {}, instead received {}",
+ notifications[notifications_idx],
+ msg
+ );
+ notifications_idx += 1;
+ }
+ }
+ }
+
+ fn stderr_line(&mut self) -> String {
+ self.stderr_lines.next().unwrap()
+ }
+
+ fn stdout_line(&mut self) -> String {
+ self.stdout_lines.next().unwrap()
+ }
+
+ fn assert_stderr_for_inspect(&mut self) {
+ assert_stderr(
+ &mut self.stderr_lines,
+ &["Visit chrome://inspect to connect to the debugger."],
+ );
+ }
+
+ fn assert_stderr_for_inspect_brk(&mut self) {
+ assert_stderr(
+ &mut self.stderr_lines,
+ &[
+ "Visit chrome://inspect to connect to the debugger.",
+ "Deno is waiting for debugger to connect.",
+ ],
+ );
+ }
+}
+
+macro_rules! assert_starts_with {
+ ($string:expr, $($test:expr),+) => {
+ let string = $string; // This might be a function call or something
+ if !($(string.starts_with($test))||+) {
+ panic!("{:?} does not start with {:?}", string, [$($test),+]);
+ }
+ }
+ }
+
+fn assert_stderr(
+ stderr_lines: &mut impl std::iter::Iterator<Item = String>,
+ expected_lines: &[&str],
+) {
+ let mut expected_index = 0;
+
+ loop {
+ let line = skip_check_line(stderr_lines);
+
+ assert_eq!(line, expected_lines[expected_index]);
+ expected_index += 1;
+
+ if expected_index >= expected_lines.len() {
+ break;
+ }
+ }
+}
+
+fn inspect_flag_with_unique_port(flag_prefix: &str) -> String {
+ use std::sync::atomic::{AtomicU16, Ordering};
+ static PORT: AtomicU16 = AtomicU16::new(9229);
+ let port = PORT.fetch_add(1, Ordering::Relaxed);
+ format!("{}=127.0.0.1:{}", flag_prefix, port)
+}
+
+fn extract_ws_url_from_stderr(
+ stderr_lines: &mut impl std::iter::Iterator<Item = String>,
+) -> url::Url {
+ let stderr_first_line = skip_check_line(stderr_lines);
+ assert_starts_with!(&stderr_first_line, "Debugger listening on ");
+ let v: Vec<_> = stderr_first_line.match_indices("ws:").collect();
+ assert_eq!(v.len(), 1);
+ let ws_url_index = v[0].0;
+ let ws_url = &stderr_first_line[ws_url_index..];
+ url::Url::parse(ws_url).unwrap()
+}
+
+fn skip_check_line(
+ stderr_lines: &mut impl std::iter::Iterator<Item = String>,
+) -> String {
+ loop {
+ let mut line = stderr_lines.next().unwrap();
+ line = util::strip_ansi_codes(&line).to_string();
+
+ if line.starts_with("Check") {
+ continue;
+ }
+
+ return line;
+ }
+}
+
+#[tokio::test]
+async fn inspector_connect() {
+ let script = util::testdata_path().join("inspector/inspector1.js");
+ let mut child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .arg(script)
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr = child.stderr.as_mut().unwrap();
+ let mut stderr_lines =
+ std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
+ let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
+
+ // We use tokio_tungstenite as a websocket client because warp (which is
+ // a dependency of Deno) uses it.
+ let (_socket, response) =
+ tokio_tungstenite::connect_async(ws_url).await.unwrap();
+ assert_eq!("101 Switching Protocols", response.status().to_string());
+ child.kill().unwrap();
+ child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_break_on_first_line() {
+ let script = util::testdata_path().join("inspector/inspector2.js");
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({
+ "id":4,
+ "method":"Runtime.evaluate",
+ "params":{
+ "expression":"Deno.core.print(\"hello from the inspector\\n\")",
+ "contextId":1,
+ "includeCommandLineAPI":true,
+ "silent":false,
+ "returnByValue":true
+ }
+ }))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#],
+ &[],
+ )
+ .await;
+
+ assert_eq!(
+ &tester.stdout_lines.next().unwrap(),
+ "hello from the inspector"
+ );
+
+ tester
+ .send(json!({"id":5,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":5,"result":{}}"#], &[])
+ .await;
+
+ assert_eq!(
+ &tester.stdout_lines.next().unwrap(),
+ "hello from the script"
+ );
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_pause() {
+ let script = util::testdata_path().join("inspector/inspector1.js");
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester
+ .send(json!({"id":6,"method":"Debugger.enable"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":6,"result":{"debuggerId":"#], &[])
+ .await;
+
+ tester
+ .send(json!({"id":31,"method":"Debugger.pause"}))
+ .await;
+
+ tester
+ .assert_received_messages(&[r#"{"id":31,"result":{}}"#], &[])
+ .await;
+
+ tester.child.kill().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_port_collision() {
+ // Skip this test on WSL, which allows multiple processes to listen on the
+ // same port, rather than making `bind()` fail with `EADDRINUSE`.
+ if cfg!(target_os = "linux") && std::env::var_os("WSL_DISTRO_NAME").is_some()
+ {
+ return;
+ }
+
+ let script = util::testdata_path().join("inspector/inspector1.js");
+ let inspect_flag = inspect_flag_with_unique_port("--inspect");
+
+ let mut child1 = util::deno_cmd()
+ .arg("run")
+ .arg(&inspect_flag)
+ .arg(script.clone())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr_1 = child1.stderr.as_mut().unwrap();
+ let mut stderr_1_lines = std::io::BufReader::new(stderr_1)
+ .lines()
+ .map(|r| r.unwrap());
+ let _ = extract_ws_url_from_stderr(&mut stderr_1_lines);
+
+ let mut child2 = util::deno_cmd()
+ .arg("run")
+ .arg(&inspect_flag)
+ .arg(script)
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr_2 = child2.stderr.as_mut().unwrap();
+ let stderr_2_error_message = std::io::BufReader::new(stderr_2)
+ .lines()
+ .map(|r| r.unwrap())
+ .inspect(|line| assert!(!line.contains("Debugger listening")))
+ .find(|line| line.contains("Cannot start inspector server"));
+ assert!(stderr_2_error_message.is_some());
+
+ child1.kill().unwrap();
+ child1.wait().unwrap();
+ child2.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_does_not_hang() {
+ let script = util::testdata_path().join("inspector/inspector3.js");
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .env("NO_COLOR", "1")
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":4,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":4,"result":{}}"#],
+ &[r#"{"method":"Debugger.resumed","params":{}}"#],
+ )
+ .await;
+
+ for i in 0..128u32 {
+ let request_id = i + 10;
+ // Expect the number {i} on stdout.
+ let s = i.to_string();
+ assert_eq!(tester.stdout_lines.next().unwrap(), s);
+
+ tester
+ .assert_received_messages(
+ &[],
+ &[
+ r#"{"method":"Runtime.consoleAPICalled","#,
+ r#"{"method":"Debugger.paused","#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":request_id,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[&format!(r#"{{"id":{},"result":{{}}}}"#, request_id)],
+ &[r#"{"method":"Debugger.resumed","params":{}}"#],
+ )
+ .await;
+ }
+
+ // Check that we can gracefully close the websocket connection.
+ tester.socket_tx.close().await.unwrap();
+ tester.socket_rx.for_each(|_| async {}).await;
+
+ assert_eq!(&tester.stdout_lines.next().unwrap(), "done");
+ assert!(tester.child.wait().unwrap().success());
+}
+
+#[tokio::test]
+async fn inspector_without_brk_runs_code() {
+ let script = util::testdata_path().join("inspector/inspector4.js");
+ let mut child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr = child.stderr.as_mut().unwrap();
+ let mut stderr_lines =
+ std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
+ let _ = extract_ws_url_from_stderr(&mut stderr_lines);
+
+ // Check that inspector actually runs code without waiting for inspector
+ // connection.
+ let stdout = child.stdout.as_mut().unwrap();
+ let mut stdout_lines =
+ std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
+ let stdout_first_line = stdout_lines.next().unwrap();
+ assert_eq!(stdout_first_line, "hello");
+
+ child.kill().unwrap();
+ child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_runtime_evaluate_does_not_crash() {
+ let child = util::deno_cmd()
+ .arg("repl")
+ .arg("--allow-read")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .stdin(std::process::Stdio::piped())
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ let stdin = tester.child.stdin.take().unwrap();
+
+ tester.assert_stderr_for_inspect();
+ assert_starts_with!(&tester.stdout_line(), "Deno");
+ assert_eq!(
+ &tester.stdout_line(),
+ "exit using ctrl+d, ctrl+c, or close()"
+ );
+ assert_eq!(&tester.stderr_line(), "Debugger session started.");
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({
+ "id":3,
+ "method":"Runtime.compileScript",
+ "params":{
+ "expression":"Deno.cwd()",
+ "sourceURL":"",
+ "persistScript":false,
+ "executionContextId":1
+ }
+ }))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":3,"result":{}}"#], &[])
+ .await;
+ tester
+ .send(json!({
+ "id":4,
+ "method":"Runtime.evaluate",
+ "params":{
+ "expression":"Deno.cwd()",
+ "objectGroup":"console",
+ "includeCommandLineAPI":true,
+ "silent":false,
+ "contextId":1,
+ "returnByValue":true,
+ "generatePreview":true,
+ "userGesture":true,
+ "awaitPromise":false,
+ "replMode":true
+ }
+ }))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
+ &[],
+ )
+ .await;
+ tester
+ .send(json!({
+ "id":5,
+ "method":"Runtime.evaluate",
+ "params":{
+ "expression":"console.error('done');",
+ "objectGroup":"console",
+ "includeCommandLineAPI":true,
+ "silent":false,
+ "contextId":1,
+ "returnByValue":true,
+ "generatePreview":true,
+ "userGesture":true,
+ "awaitPromise":false,
+ "replMode":true
+ }
+ }))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
+ &[r#"{"method":"Runtime.consoleAPICalled"#],
+ )
+ .await;
+ assert_eq!(&tester.stderr_line(), "done");
+ drop(stdin);
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_json() {
+ let script = util::testdata_path().join("inspector/inspector1.js");
+ let mut child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .arg(script)
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr = child.stderr.as_mut().unwrap();
+ let mut stderr_lines =
+ std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
+ let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
+ let mut url = ws_url.clone();
+ let _ = url.set_scheme("http");
+ url.set_path("/json");
+ let resp = reqwest::get(url).await.unwrap();
+ assert_eq!(resp.status(), reqwest::StatusCode::OK);
+ let endpoint_list: Vec<deno_core::serde_json::Value> =
+ serde_json::from_str(&resp.text().await.unwrap()).unwrap();
+ let matching_endpoint = endpoint_list
+ .iter()
+ .find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str());
+ assert!(matching_endpoint.is_some());
+ child.kill().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_json_list() {
+ let script = util::testdata_path().join("inspector/inspector1.js");
+ let mut child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .arg(script)
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr = child.stderr.as_mut().unwrap();
+ let mut stderr_lines =
+ std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
+ let ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
+ let mut url = ws_url.clone();
+ let _ = url.set_scheme("http");
+ url.set_path("/json/list");
+ let resp = reqwest::get(url).await.unwrap();
+ assert_eq!(resp.status(), reqwest::StatusCode::OK);
+ let endpoint_list: Vec<deno_core::serde_json::Value> =
+ serde_json::from_str(&resp.text().await.unwrap()).unwrap();
+ let matching_endpoint = endpoint_list
+ .iter()
+ .find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str());
+ assert!(matching_endpoint.is_some());
+ child.kill().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_connect_non_ws() {
+ // https://github.com/denoland/deno/issues/11449
+ // Verify we don't panic if non-WS connection is being established
+ let script = util::testdata_path().join("inspector/inspector1.js");
+ let mut child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect"))
+ .arg(script)
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stderr = child.stderr.as_mut().unwrap();
+ let mut stderr_lines =
+ std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
+ let mut ws_url = extract_ws_url_from_stderr(&mut stderr_lines);
+ // Change scheme to URL and try send a request. We're not interested
+ // in the request result, just that the process doesn't panic.
+ ws_url.set_scheme("http").unwrap();
+ let resp = reqwest::get(ws_url).await.unwrap();
+ assert_eq!("400 Bad Request", resp.status().to_string());
+ child.kill().unwrap();
+ child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_break_on_first_line_in_test() {
+ let script = util::testdata_path().join("inspector/inspector_test.js");
+ let child = util::deno_cmd()
+ .arg("test")
+ .arg("--quiet")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg(script)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({
+ "id":4,
+ "method":"Runtime.evaluate",
+ "params":{
+ "expression":"1 + 1",
+ "contextId":1,
+ "includeCommandLineAPI":true,
+ "silent":false,
+ "returnByValue":true
+ }
+ }))
+ .await;
+ tester.assert_received_messages(
+ &[r#"{"id":4,"result":{"result":{"type":"number","value":2,"description":"2"}}}"#],
+ &[],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":5,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":5,"result":{}}"#], &[])
+ .await;
+
+ assert_starts_with!(&tester.stdout_line(), "running 1 test from");
+ assert!(&tester.stdout_line().contains("basic test ... ok"));
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_with_ts_files() {
+ let script = util::testdata_path().join("inspector/test.ts");
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg("--check")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ fn notification_filter(msg: &str) -> bool {
+ (msg.starts_with(r#"{"method":"Debugger.scriptParsed","#)
+ && msg.contains("testdata/inspector"))
+ || !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#)
+ }
+
+ let mut tester = InspectorTester::create(child, notification_filter).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ // receive messages with sources from this test
+ let script1 = tester.recv().await;
+ assert!(script1.contains("testdata/inspector/test.ts"));
+ let script1_id = {
+ let v: serde_json::Value = serde_json::from_str(&script1).unwrap();
+ v["params"]["scriptId"].as_str().unwrap().to_string()
+ };
+ let script2 = tester.recv().await;
+ assert!(script2.contains("testdata/inspector/foo.ts"));
+ let script2_id = {
+ let v: serde_json::Value = serde_json::from_str(&script2).unwrap();
+ v["params"]["scriptId"].as_str().unwrap().to_string()
+ };
+ let script3 = tester.recv().await;
+ assert!(script3.contains("testdata/inspector/bar.js"));
+ let script3_id = {
+ let v: serde_json::Value = serde_json::from_str(&script3).unwrap();
+ v["params"]["scriptId"].as_str().unwrap().to_string()
+ };
+
+ tester
+ .assert_received_messages(&[r#"{"id":2,"result":{"debuggerId":"#], &[])
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester.send_many(
+ &[
+ json!({"id":4,"method":"Debugger.getScriptSource","params":{"scriptId":script1_id.as_str()}}),
+ json!({"id":5,"method":"Debugger.getScriptSource","params":{"scriptId":script2_id.as_str()}}),
+ json!({"id":6,"method":"Debugger.getScriptSource","params":{"scriptId":script3_id.as_str()}}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":4,"result":{"scriptSource":"import { foo } from \"./foo.ts\";\nimport { bar } from \"./bar.js\";\nconsole.log(foo());\nconsole.log(bar());\n//# sourceMappingURL=data:application/json;base64,"#,
+ r#"{"id":5,"result":{"scriptSource":"class Foo {\n hello() {\n return \"hello\";\n }\n}\nexport function foo() {\n const f = new Foo();\n return f.hello();\n}\n//# sourceMappingURL=data:application/json;base64,"#,
+ r#"{"id":6,"result":{"scriptSource":"export function bar() {\n return \"world\";\n}\n"#,
+ ],
+ &[],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":7,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":7,"result":{}}"#], &[])
+ .await;
+
+ assert_eq!(&tester.stdout_line(), "hello");
+ assert_eq!(&tester.stdout_line(), "world");
+
+ tester.assert_received_messages(
+ &[],
+ &[
+ r#"{"method":"Debugger.resumed","params":{}}"#,
+ r#"{"method":"Runtime.consoleAPICalled","#,
+ r#"{"method":"Runtime.consoleAPICalled","#,
+ r#"{"method":"Runtime.executionContextDestroyed","params":{"executionContextId":1}}"#,
+ ],
+ )
+ .await;
+
+ assert_eq!(
+ &tester.stdout_line(),
+ "Program finished. Waiting for inspector to disconnect to exit the process..."
+ );
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_memory() {
+ let script = util::testdata_path().join("inspector/memory.js");
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send_many(&[
+ json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}),
+ json!({"id":4,"method":"HeapProfiler.enable"}),
+ ])
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#, r#"{"id":4,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":5,"method":"Runtime.getHeapUsage", "params": {}}))
+ .await;
+
+ let json_msg = tester.recv_as_json().await;
+ assert_eq!(json_msg["id"].as_i64().unwrap(), 5);
+ let result = &json_msg["result"];
+ assert!(
+ result["usedSize"].as_i64().unwrap()
+ <= result["totalSize"].as_i64().unwrap()
+ );
+
+ tester
+ .send(json!({
+ "id":6,
+ "method":"HeapProfiler.takeHeapSnapshot",
+ "params": {
+ "reportProgress": true,
+ "treatGlobalObjectsAsRoots": true,
+ "captureNumberValue": false
+ }
+ }))
+ .await;
+
+ let mut progress_report_completed = false;
+ loop {
+ let msg = tester.recv().await;
+
+ // TODO(bartlomieju): can be abstracted
+ if !progress_report_completed
+ && msg.starts_with(
+ r#"{"method":"HeapProfiler.reportHeapSnapshotProgress","params""#,
+ )
+ {
+ let json_msg: serde_json::Value = serde_json::from_str(&msg).unwrap();
+ if let Some(finished) = json_msg["params"].get("finished") {
+ progress_report_completed = finished.as_bool().unwrap();
+ }
+ continue;
+ }
+
+ if msg.starts_with(r#"{"method":"HeapProfiler.reportHeapSnapshotProgress","params":{"done":"#,) {
+ continue;
+ }
+
+ if msg.starts_with(r#"{"id":6,"result":{}}"#) {
+ assert!(progress_report_completed);
+ break;
+ }
+ }
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_profile() {
+ let script = util::testdata_path().join("inspector/memory.js");
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send_many(&[
+ json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}),
+ json!({"id":4,"method":"Profiler.enable"}),
+ ])
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#, r#"{"id":4,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester.send_many(
+ &[
+ json!({"id":5,"method":"Profiler.setSamplingInterval","params":{"interval": 100}}),
+ json!({"id":6,"method":"Profiler.start","params":{}}),
+ ],
+ ).await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":5,"result":{}}"#, r#"{"id":6,"result":{}}"#],
+ &[],
+ )
+ .await;
+
+ tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
+
+ tester
+ .send(json!({"id":7,"method":"Profiler.stop", "params": {}}))
+ .await;
+ let json_msg = tester.recv_as_json().await;
+ assert_eq!(json_msg["id"].as_i64().unwrap(), 7);
+ let result = &json_msg["result"];
+ let profile = &result["profile"];
+ assert!(
+ profile["startTime"].as_i64().unwrap()
+ < profile["endTime"].as_i64().unwrap()
+ );
+ profile["samples"].as_array().unwrap();
+ profile["nodes"].as_array().unwrap();
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_break_on_first_line_npm_esm() {
+ let _server = http_server();
+
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg("--quiet")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg("npm:@denotest/bin/cli-esm")
+ .arg("this")
+ .arg("is")
+ .arg("a")
+ .arg("test")
+ .envs(util::env_vars_for_npm_tests())
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":4,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":4,"result":{}}"#], &[])
+ .await;
+
+ assert_eq!(&tester.stdout_line(), "this");
+ assert_eq!(&tester.stdout_line(), "is");
+ assert_eq!(&tester.stdout_line(), "a");
+ assert_eq!(&tester.stdout_line(), "test");
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_break_on_first_line_npm_cjs() {
+ let _server = http_server();
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg("--quiet")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg("npm:@denotest/bin/cli-cjs")
+ .arg("this")
+ .arg("is")
+ .arg("a")
+ .arg("test")
+ .envs(util::env_vars_for_npm_tests())
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":4,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":4,"result":{}}"#], &[])
+ .await;
+
+ assert_eq!(&tester.stdout_line(), "this");
+ assert_eq!(&tester.stdout_line(), "is");
+ assert_eq!(&tester.stdout_line(), "a");
+ assert_eq!(&tester.stdout_line(), "test");
+
+ tester.child.kill().unwrap();
+ tester.child.wait().unwrap();
+}
+
+#[tokio::test]
+async fn inspector_error_with_npm_import() {
+ let script = util::testdata_path().join("inspector/error_with_npm_import.js");
+ let _server = http_server();
+
+ let child = util::deno_cmd()
+ .arg("run")
+ .arg("--quiet")
+ .arg("-A")
+ .arg(inspect_flag_with_unique_port("--inspect-brk"))
+ .arg(script)
+ .envs(util::env_vars_for_npm_tests())
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(
+ &[r#"{"id":3,"result":{}}"#],
+ &[r#"{"method":"Debugger.paused","#],
+ )
+ .await;
+
+ tester
+ .send(json!({"id":4,"method":"Debugger.resume"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":4,"result":{}}"#], &[])
+ .await;
+
+ // TODO(bartlomieju): this is a partial fix, we should assert that
+ // "Runtime.exceptionThrown" notification was sent, but a bindings for this
+ // notification is not yet there
+ assert_eq!(&tester.stderr_line(), "Debugger session started.");
+ assert_eq!(&tester.stderr_line(), "error: Uncaught Error: boom!");
+
+ assert_eq!(tester.child.wait().unwrap().code(), Some(1));
+}
+
+#[tokio::test]
+async fn inspector_wait() {
+ let script = util::testdata_path().join("inspector/inspect_wait.js");
+ let temp_dir = TempDir::new();
+
+ let child = util::deno_cmd()
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--quiet")
+ .arg("-A")
+ .arg(inspect_flag_with_unique_port("--inspect-wait"))
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
+ assert!(!temp_dir.path().join("hello.txt").exists());
+
+ let mut tester = InspectorTester::create(child, ignore_script_parsed).await;
+
+ tester.assert_stderr_for_inspect_brk();
+ tester
+ .send_many(&[
+ json!({"id":1,"method":"Runtime.enable"}),
+ json!({"id":2,"method":"Debugger.enable"}),
+ ])
+ .await;
+ tester.assert_received_messages(
+ &[
+ r#"{"id":1,"result":{}}"#,
+ r#"{"id":2,"result":{"debuggerId":"#,
+ ],
+ &[
+ r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
+ ],
+ )
+ .await;
+ // TODO(bartlomieju): ideally this shouldn't be needed, but currently there's
+ // no way to express that in inspector code. Most clients always send this
+ // message anyway.
+ tester
+ .send(json!({"id":3,"method":"Runtime.runIfWaitingForDebugger"}))
+ .await;
+ tester
+ .assert_received_messages(&[r#"{"id":3,"result":{}}"#], &[])
+ .await;
+ assert_eq!(&tester.stderr_line(), "Debugger session started.");
+ tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
+ assert_eq!(&tester.stderr_line(), "did run");
+ assert!(temp_dir.path().join("hello.txt").exists());
+ tester.child.kill().unwrap();
+}
diff --git a/cli/tests/integration/install_tests.rs b/cli/tests/integration/install_tests.rs
new file mode 100644
index 000000000..af4edd2ee
--- /dev/null
+++ b/cli/tests/integration/install_tests.rs
@@ -0,0 +1,243 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::fs;
+use std::process::Command;
+use test_util as util;
+use test_util::assert_contains;
+use test_util::assert_ends_with;
+use test_util::TempDir;
+
+#[test]
+fn install_basic() {
+ let _guard = util::http_server();
+ let temp_dir = TempDir::new();
+ let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
+
+ // ensure a lockfile doesn't get created or updated locally
+ temp_dir.write("deno.json", "{}");
+
+ let status = util::deno_cmd()
+ .current_dir(temp_dir.path())
+ .arg("install")
+ .arg("--check")
+ .arg("--name")
+ .arg("echo_test")
+ .arg("http://localhost:4545/echo.ts")
+ .envs([
+ ("HOME", temp_dir_str.as_str()),
+ ("USERPROFILE", temp_dir_str.as_str()),
+ ("DENO_INSTALL_ROOT", ""),
+ ])
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+
+ // no lockfile should be created locally
+ assert!(!temp_dir.path().join("deno.lock").exists());
+
+ let mut file_path = temp_dir.path().join(".deno/bin/echo_test");
+ assert!(file_path.exists());
+
+ if cfg!(windows) {
+ file_path = file_path.with_extension("cmd");
+ }
+
+ let content = fs::read_to_string(&file_path).unwrap();
+ // ensure there's a trailing newline so the shell script can be
+ // more versatile.
+ assert_eq!(content.chars().last().unwrap(), '\n');
+
+ if cfg!(windows) {
+ assert_contains!(
+ content,
+ r#""run" "--check" "--no-config" "http://localhost:4545/echo.ts""#
+ );
+ } else {
+ assert_contains!(
+ content,
+ r#"run --check --no-config 'http://localhost:4545/echo.ts'"#
+ );
+ }
+
+ // now uninstall
+ let status = util::deno_cmd()
+ .current_dir(temp_dir.path())
+ .arg("uninstall")
+ .arg("echo_test")
+ .envs([
+ ("HOME", temp_dir_str.as_str()),
+ ("USERPROFILE", temp_dir_str.as_str()),
+ ("DENO_INSTALL_ROOT", ""),
+ ])
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+
+ // ensure local lockfile still doesn't exist
+ assert!(!temp_dir.path().join("deno.lock").exists());
+ // ensure uninstall occurred
+ assert!(!file_path.exists());
+}
+
+#[test]
+fn install_custom_dir_env_var() {
+ let _guard = util::http_server();
+ let temp_dir = TempDir::new();
+ let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
+
+ let status = util::deno_cmd()
+ .current_dir(util::root_path()) // different cwd
+ .arg("install")
+ .arg("--check")
+ .arg("--name")
+ .arg("echo_test")
+ .arg("http://localhost:4545/echo.ts")
+ .envs([
+ ("HOME", temp_dir_str.as_str()),
+ ("USERPROFILE", temp_dir_str.as_str()),
+ ("DENO_INSTALL_ROOT", temp_dir_str.as_str()),
+ ])
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+
+ let mut file_path = temp_dir.path().join("bin/echo_test");
+ assert!(file_path.exists());
+
+ if cfg!(windows) {
+ file_path = file_path.with_extension("cmd");
+ }
+
+ let content = fs::read_to_string(file_path).unwrap();
+ if cfg!(windows) {
+ assert_contains!(
+ content,
+ r#""run" "--check" "--no-config" "http://localhost:4545/echo.ts""#
+ );
+ } else {
+ assert_contains!(
+ content,
+ r#"run --check --no-config 'http://localhost:4545/echo.ts'"#
+ );
+ }
+}
+
+#[test]
+fn installer_test_local_module_run() {
+ let temp_dir = TempDir::new();
+ let bin_dir = temp_dir.path().join("bin");
+ std::fs::create_dir(&bin_dir).unwrap();
+ let status = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("install")
+ .arg("--name")
+ .arg("echo_test")
+ .arg("--root")
+ .arg(temp_dir.path())
+ .arg(util::testdata_path().join("echo.ts"))
+ .arg("hello")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let mut file_path = bin_dir.join("echo_test");
+ if cfg!(windows) {
+ file_path = file_path.with_extension("cmd");
+ }
+ assert!(file_path.exists());
+ // NOTE: using file_path here instead of exec_name, because tests
+ // shouldn't mess with user's PATH env variable
+ let output = Command::new(file_path)
+ .current_dir(temp_dir.path())
+ .arg("foo")
+ .env("PATH", util::target_dir())
+ .output()
+ .unwrap();
+ let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
+ assert_ends_with!(stdout_str, "hello, foo");
+}
+
+#[test]
+fn installer_test_remote_module_run() {
+ let _g = util::http_server();
+ let temp_dir = TempDir::new();
+ let bin_dir = temp_dir.path().join("bin");
+ std::fs::create_dir(&bin_dir).unwrap();
+ let status = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("install")
+ .arg("--name")
+ .arg("echo_test")
+ .arg("--root")
+ .arg(temp_dir.path())
+ .arg("http://localhost:4545/echo.ts")
+ .arg("hello")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let mut file_path = bin_dir.join("echo_test");
+ if cfg!(windows) {
+ file_path = file_path.with_extension("cmd");
+ }
+ assert!(file_path.exists());
+ let output = Command::new(file_path)
+ .current_dir(temp_dir.path())
+ .arg("foo")
+ .env("PATH", util::target_dir())
+ .output()
+ .unwrap();
+ assert_ends_with!(
+ std::str::from_utf8(&output.stdout).unwrap().trim(),
+ "hello, foo",
+ );
+}
+
+#[test]
+fn check_local_by_default() {
+ let _guard = util::http_server();
+ let temp_dir = TempDir::new();
+ let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
+
+ let status = util::deno_cmd()
+ .current_dir(temp_dir.path())
+ .arg("install")
+ .arg(util::testdata_path().join("./install/check_local_by_default.ts"))
+ .envs([
+ ("HOME", temp_dir_str.as_str()),
+ ("USERPROFILE", temp_dir_str.as_str()),
+ ("DENO_INSTALL_ROOT", ""),
+ ])
+ .status()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn check_local_by_default2() {
+ let _guard = util::http_server();
+ let temp_dir = TempDir::new();
+ let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
+
+ let status = util::deno_cmd()
+ .current_dir(temp_dir.path())
+ .arg("install")
+ .arg(util::testdata_path().join("./install/check_local_by_default2.ts"))
+ .envs([
+ ("HOME", temp_dir_str.as_str()),
+ ("NO_COLOR", "1"),
+ ("USERPROFILE", temp_dir_str.as_str()),
+ ("DENO_INSTALL_ROOT", ""),
+ ])
+ .status()
+ .unwrap();
+ assert!(status.success());
+}
diff --git a/cli/tests/integration/js_unit_tests.rs b/cli/tests/integration/js_unit_tests.rs
new file mode 100644
index 000000000..46e66ab77
--- /dev/null
+++ b/cli/tests/integration/js_unit_tests.rs
@@ -0,0 +1,39 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+
+#[test]
+fn js_unit_tests_lint() {
+ let status = util::deno_cmd()
+ .arg("lint")
+ .arg("--unstable")
+ .arg(util::tests_path().join("unit"))
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn js_unit_tests() {
+ let _g = util::http_server();
+
+ // Note that the unit tests are not safe for concurrency and must be run with a concurrency limit
+ // of one because there are some chdir tests in there.
+ // TODO(caspervonb) split these tests into two groups: parallel and serial.
+ let mut deno = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("test")
+ .arg("--unstable")
+ .arg("--location=http://js-unit-tests/foo/bar")
+ .arg("--no-prompt")
+ .arg("-A")
+ .arg(util::tests_path().join("unit"))
+ .spawn()
+ .expect("failed to spawn script");
+
+ let status = deno.wait().expect("failed to wait for the child process");
+ assert_eq!(Some(0), status.code());
+ assert!(status.success());
+}
diff --git a/cli/tests/integration/lint_tests.rs b/cli/tests/integration/lint_tests.rs
new file mode 100644
index 000000000..990db16b6
--- /dev/null
+++ b/cli/tests/integration/lint_tests.rs
@@ -0,0 +1,132 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+
+#[test]
+fn ignore_unexplicit_files() {
+ let output = util::deno_cmd()
+ .current_dir(util::root_path())
+ .env("NO_COLOR", "1")
+ .arg("lint")
+ .arg("--unstable")
+ .arg("--ignore=./")
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr),
+ "error: No target files found.\n"
+ );
+}
+
+itest!(all {
+ args: "lint lint/without_config/file1.js lint/without_config/file2.ts lint/without_config/ignored_file.ts",
+ output: "lint/expected.out",
+ exit_code: 1,
+});
+
+itest!(quiet {
+ args: "lint --quiet lint/without_config/file1.js",
+ output: "lint/expected_quiet.out",
+ exit_code: 1,
+});
+
+itest!(json {
+ args:
+ "lint --json lint/without_config/file1.js lint/without_config/file2.ts lint/without_config/ignored_file.ts lint/without_config/malformed.js",
+ output: "lint/expected_json.out",
+ exit_code: 1,
+});
+
+itest!(compact {
+ args:
+ "lint --compact lint/without_config/file1.js lint/without_config/ignored_file.tss",
+ output: "lint/expected_compact.out",
+ exit_code: 1,
+});
+
+itest!(ignore {
+ args:
+ "lint --ignore=lint/without_config/file1.js,lint/without_config/malformed.js,lint/without_config/lint_with_config/ lint/without_config/",
+ output: "lint/expected_ignore.out",
+ exit_code: 1,
+});
+
+itest!(glob {
+ args: "lint --ignore=lint/without_config/malformed.js,lint/with_config/ lint/without_config/",
+ output: "lint/expected_glob.out",
+ exit_code: 1,
+});
+
+itest!(stdin {
+ args: "lint -",
+ input: Some("let _a: any;"),
+ output: "lint/expected_from_stdin.out",
+ exit_code: 1,
+});
+
+itest!(stdin_json {
+ args: "lint --json -",
+ input: Some("let _a: any;"),
+ output: "lint/expected_from_stdin_json.out",
+ exit_code: 1,
+});
+
+itest!(rules {
+ args: "lint --rules",
+ output: "lint/expected_rules.out",
+ exit_code: 0,
+});
+
+// Make sure that the rules are printed if quiet option is enabled.
+itest!(rules_quiet {
+ args: "lint --rules -q",
+ output: "lint/expected_rules.out",
+ exit_code: 0,
+});
+
+itest!(lint_with_config {
+ args: "lint --config lint/Deno.jsonc lint/with_config/",
+ output: "lint/with_config.out",
+ exit_code: 1,
+});
+
+itest!(lint_with_report_config {
+ args: "lint --config lint/Deno.compact.format.jsonc lint/with_config/",
+ output: "lint/with_report_config_compact.out",
+ exit_code: 1,
+});
+
+// Check if CLI flags take precedence
+itest!(lint_with_report_config_override {
+ args: "lint --config lint/Deno.compact.format.jsonc lint/with_config/ --json",
+ output: "lint/with_report_config_override.out",
+ exit_code: 1,
+});
+
+itest!(lint_with_config_and_flags {
+ args: "lint --config lint/Deno.jsonc --ignore=lint/with_config/a.ts",
+ output: "lint/with_config_and_flags.out",
+ exit_code: 1,
+});
+
+itest!(lint_with_config_without_tags {
+ args: "lint --config lint/Deno.no_tags.jsonc lint/with_config/",
+ output: "lint/with_config_without_tags.out",
+ exit_code: 1,
+});
+
+itest!(lint_with_malformed_config {
+ args: "lint --config lint/Deno.malformed.jsonc",
+ output: "lint/with_malformed_config.out",
+ exit_code: 1,
+});
+
+itest!(lint_with_malformed_config2 {
+ args: "lint --config lint/Deno.malformed2.jsonc",
+ output: "lint/with_malformed_config2.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
new file mode 100644
index 000000000..3fba65c46
--- /dev/null
+++ b/cli/tests/integration/lsp_tests.rs
@@ -0,0 +1,6434 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_ast::ModuleSpecifier;
+use deno_core::serde::de::DeserializeOwned;
+use deno_core::serde::Deserialize;
+use deno_core::serde::Serialize;
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::url::Url;
+use pretty_assertions::assert_eq;
+use std::collections::HashSet;
+use std::fs;
+use std::process::Stdio;
+use test_util::deno_cmd_with_deno_dir;
+use test_util::deno_exe_path;
+use test_util::env_vars_for_npm_tests;
+use test_util::http_server;
+use test_util::lsp::LspClient;
+use test_util::testdata_path;
+use test_util::TempDir;
+use tower_lsp::lsp_types as lsp;
+
+fn load_fixture(path: &str) -> Value {
+ load_fixture_as(path)
+}
+
+fn load_fixture_as<T>(path: &str) -> T
+where
+ T: DeserializeOwned,
+{
+ let fixture_str = load_fixture_str(path);
+ serde_json::from_str::<T>(&fixture_str).unwrap()
+}
+
+fn load_fixture_str(path: &str) -> String {
+ let fixtures_path = testdata_path().join("lsp");
+ let path = fixtures_path.join(path);
+ fs::read_to_string(path).unwrap()
+}
+
+fn init(init_path: &str) -> LspClient {
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", load_fixture(init_path))
+ .unwrap();
+ client.write_notification("initialized", json!({})).unwrap();
+ client
+}
+
+fn did_open<V>(
+ client: &mut LspClient,
+ params: V,
+) -> Vec<lsp::PublishDiagnosticsParams>
+where
+ V: Serialize,
+{
+ client
+ .write_notification("textDocument/didOpen", params)
+ .unwrap();
+
+ handle_configuration_request(
+ client,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": true
+ }
+ }]),
+ );
+ read_diagnostics(client).0
+}
+
+fn handle_configuration_request(client: &mut LspClient, result: Value) {
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client.write_response(id, result).unwrap();
+}
+
+fn read_diagnostics(client: &mut LspClient) -> CollectedDiagnostics {
+ // diagnostics come in batches of three unless they're cancelled
+ let mut diagnostics = vec![];
+ for _ in 0..3 {
+ let (method, response) = client
+ .read_notification::<lsp::PublishDiagnosticsParams>()
+ .unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ diagnostics.push(response.unwrap());
+ }
+ CollectedDiagnostics(diagnostics)
+}
+
+fn shutdown(client: &mut LspClient) {
+ client
+ .write_request::<_, _, Value>("shutdown", json!(null))
+ .unwrap();
+ client.write_notification("exit", json!(null)).unwrap();
+}
+
+pub fn ensure_directory_specifier(
+ mut specifier: ModuleSpecifier,
+) -> ModuleSpecifier {
+ let path = specifier.path();
+ if !path.ends_with('/') {
+ let new_path = format!("{}/", path);
+ specifier.set_path(&new_path);
+ }
+ specifier
+}
+
+struct TestSession {
+ client: LspClient,
+ open_file_count: usize,
+}
+
+impl TestSession {
+ pub fn from_file(init_path: &str) -> Self {
+ Self::from_client(init(init_path))
+ }
+
+ pub fn from_client(client: LspClient) -> Self {
+ Self {
+ client,
+ open_file_count: 0,
+ }
+ }
+
+ pub fn did_open<V>(&mut self, params: V) -> CollectedDiagnostics
+ where
+ V: Serialize,
+ {
+ self
+ .client
+ .write_notification("textDocument/didOpen", params)
+ .unwrap();
+
+ let (id, method, _) = self.client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ self
+ .client
+ .write_response(
+ id,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": true
+ }
+ }]),
+ )
+ .unwrap();
+
+ self.open_file_count += 1;
+ self.read_diagnostics()
+ }
+
+ pub fn read_diagnostics(&mut self) -> CollectedDiagnostics {
+ let mut all_diagnostics = Vec::new();
+ for _ in 0..self.open_file_count {
+ all_diagnostics.extend(read_diagnostics(&mut self.client).0);
+ }
+ CollectedDiagnostics(all_diagnostics)
+ }
+
+ pub fn shutdown_and_exit(&mut self) {
+ shutdown(&mut self.client);
+ }
+}
+
+#[derive(Debug, Clone)]
+struct CollectedDiagnostics(Vec<lsp::PublishDiagnosticsParams>);
+
+impl CollectedDiagnostics {
+ /// Gets the diagnostics that the editor will see after all the publishes.
+ pub fn viewed(&self) -> Vec<lsp::Diagnostic> {
+ self
+ .viewed_messages()
+ .into_iter()
+ .flat_map(|m| m.diagnostics)
+ .collect()
+ }
+
+ /// Gets the messages that the editor will see after all the publishes.
+ pub fn viewed_messages(&self) -> Vec<lsp::PublishDiagnosticsParams> {
+ // go over the publishes in reverse order in order to get
+ // the final messages that will be shown in the editor
+ let mut messages = Vec::new();
+ let mut had_specifier = HashSet::new();
+ for message in self.0.iter().rev() {
+ if had_specifier.insert(message.uri.clone()) {
+ messages.insert(0, message.clone());
+ }
+ }
+ messages
+ }
+
+ pub fn with_source(&self, source: &str) -> lsp::PublishDiagnosticsParams {
+ self
+ .viewed_messages()
+ .iter()
+ .find(|p| {
+ p.diagnostics
+ .iter()
+ .any(|d| d.source == Some(source.to_string()))
+ })
+ .map(ToOwned::to_owned)
+ .unwrap()
+ }
+
+ pub fn with_file_and_source(
+ &self,
+ specifier: &str,
+ source: &str,
+ ) -> lsp::PublishDiagnosticsParams {
+ let specifier = ModuleSpecifier::parse(specifier).unwrap();
+ self
+ .viewed_messages()
+ .iter()
+ .find(|p| {
+ p.uri == specifier
+ && p
+ .diagnostics
+ .iter()
+ .any(|d| d.source == Some(source.to_string()))
+ })
+ .map(ToOwned::to_owned)
+ .unwrap()
+ }
+}
+
+#[test]
+fn lsp_startup_shutdown() {
+ let mut client = init("initialize_params.json");
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_init_tsconfig() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let tsconfig =
+ serde_json::to_vec_pretty(&load_fixture("lib.tsconfig.json")).unwrap();
+ fs::write(temp_dir.path().join("lib.tsconfig.json"), tsconfig).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./lib.tsconfig.json"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "location.pathname;\n"
+ }
+ }),
+ );
+
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_tsconfig_types() {
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let temp_dir = TempDir::new();
+ let tsconfig =
+ serde_json::to_vec_pretty(&load_fixture("types.tsconfig.json")).unwrap();
+ fs::write(temp_dir.path().join("types.tsconfig.json"), tsconfig).unwrap();
+ let a_dts = load_fixture_str("a.d.ts");
+ fs::write(temp_dir.path().join("a.d.ts"), a_dts).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./types.tsconfig.json"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(a);\n"
+ }
+ }),
+ );
+
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_tsconfig_bad_config_path() {
+ let mut client = init("initialize_params_bad_config_option.json");
+ let (method, maybe_params) = client.read_notification().unwrap();
+ assert_eq!(method, "window/showMessage");
+ assert_eq!(maybe_params, Some(lsp::ShowMessageParams {
+ typ: lsp::MessageType::WARNING,
+ message: "The path to the configuration file (\"bad_tsconfig.json\") is not resolvable.".to_string()
+ }));
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Deno.args);\n"
+ }
+ }),
+ );
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+}
+
+#[test]
+fn lsp_triple_slash_types() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let a_dts = load_fixture_str("a.d.ts");
+ fs::write(temp_dir.path().join("a.d.ts"), a_dts).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "/// <reference types=\"./a.d.ts\" />\n\nconsole.log(a);\n"
+ }
+ }),
+ );
+
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_import_map() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let import_map =
+ serde_json::to_vec_pretty(&load_fixture("import-map.json")).unwrap();
+ fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
+ fs::create_dir(temp_dir.path().join("lib")).unwrap();
+ fs::write(
+ temp_dir.path().join("lib").join("b.ts"),
+ r#"export const b = "b";"#,
+ )
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("importMap".to_string(), json!("import-map.json"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+ let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": uri,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
+ }
+ }),
+ );
+
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": uri
+ },
+ "position": {
+ "line": 2,
+ "character": 12
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value":"(alias) const b: \"b\"\nimport b"
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 12
+ },
+ "end": {
+ "line": 2,
+ "character": 13
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_import_map_data_url() {
+ let mut client = init("initialize_params_import_map.json");
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import example from \"example\";\n"
+ }
+ }),
+ );
+
+ let mut diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ // This indicates that the import map from initialize_params_import_map.json
+ // is applied correctly.
+ assert!(diagnostics.any(|diagnostic| diagnostic.code
+ == Some(lsp::NumberOrString::String("no-cache".to_string()))
+ && diagnostic
+ .message
+ .contains("https://deno.land/x/example/mod.ts")));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_import_map_config_file() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+
+ let deno_import_map_jsonc =
+ serde_json::to_vec_pretty(&load_fixture("deno.import_map.jsonc")).unwrap();
+ fs::write(
+ temp_dir.path().join("deno.import_map.jsonc"),
+ deno_import_map_jsonc,
+ )
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./deno.import_map.jsonc"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+ let import_map =
+ serde_json::to_vec_pretty(&load_fixture("import-map.json")).unwrap();
+ fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
+ fs::create_dir(temp_dir.path().join("lib")).unwrap();
+ fs::write(
+ temp_dir.path().join("lib").join("b.ts"),
+ r#"export const b = "b";"#,
+ )
+ .unwrap();
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+ let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": uri,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
+ }
+ }),
+ );
+
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": uri
+ },
+ "position": {
+ "line": 2,
+ "character": 12
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value":"(alias) const b: \"b\"\nimport b"
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 12
+ },
+ "end": {
+ "line": 2,
+ "character": 13
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_deno_task() {
+ let temp_dir = TempDir::new();
+ let workspace_root = temp_dir.path().canonicalize().unwrap();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ fs::write(
+ workspace_root.join("deno.jsonc"),
+ r#"{
+ "tasks": {
+ "build": "deno test",
+ "some:test": "deno bundle mod.ts"
+ }
+ }"#,
+ )
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(workspace_root).unwrap());
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>("deno/task", json!(null))
+ .unwrap();
+
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([
+ {
+ "name": "build",
+ "detail": "deno test"
+ },
+ {
+ "name": "some:test",
+ "detail": "deno bundle mod.ts"
+ }
+ ]))
+ );
+}
+
+#[test]
+fn lsp_import_assertions() {
+ let mut client = init("initialize_params_import_map.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/test.json",
+ "languageId": "json",
+ "version": 1,
+ "text": "{\"a\":1}"
+ }
+ }),
+ )
+ .unwrap();
+ handle_configuration_request(
+ &mut client,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": true
+ }
+ }]),
+ );
+
+ let diagnostics = CollectedDiagnostics(did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/a.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import a from \"./test.json\";\n\nconsole.log(a);\n"
+ }
+ }),
+ ));
+
+ assert_eq!(
+ json!(
+ diagnostics
+ .with_file_and_source("file:///a/a.ts", "deno")
+ .diagnostics
+ ),
+ json!([
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 14
+ },
+ "end": {
+ "line": 0,
+ "character": 27
+ }
+ },
+ "severity": 1,
+ "code": "no-assert-type",
+ "source": "deno",
+ "message": "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement."
+ }
+ ])
+ );
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_params_import_assertion.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_response_import_assertion.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_import_map_import_completions() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let import_map =
+ serde_json::to_vec_pretty(&load_fixture("import-map-completions.json"))
+ .unwrap();
+ fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
+ fs::create_dir(temp_dir.path().join("lib")).unwrap();
+ fs::write(
+ temp_dir.path().join("lib").join("b.ts"),
+ r#"export const b = "b";"#,
+ )
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("importMap".to_string(), json!("import-map.json"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+ let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": uri,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"/~/b.ts\";\nimport * as b from \"\""
+ }
+ }),
+ );
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": uri
+ },
+ "position": {
+ "line": 1,
+ "character": 20
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "\""
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": ".",
+ "kind": 19,
+ "detail": "(local)",
+ "sortText": "1",
+ "insertText": ".",
+ "commitCharacters": ["\"", "'"],
+ },
+ {
+ "label": "..",
+ "kind": 19,
+ "detail": "(local)",
+ "sortText": "1",
+ "insertText": "..",
+ "commitCharacters": ["\"", "'"],
+ },
+ {
+ "label": "std",
+ "kind": 19,
+ "detail": "(import map)",
+ "sortText": "std",
+ "insertText": "std",
+ "commitCharacters": ["\"", "'"],
+ },
+ {
+ "label": "fs",
+ "kind": 17,
+ "detail": "(import map)",
+ "sortText": "fs",
+ "insertText": "fs",
+ "commitCharacters": ["\"", "'"],
+ },
+ {
+ "label": "/~",
+ "kind": 19,
+ "detail": "(import map)",
+ "sortText": "/~",
+ "insertText": "/~",
+ "commitCharacters": ["\"", "'"],
+ }
+ ]
+ }))
+ );
+
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": uri,
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 20
+ },
+ "end": {
+ "line": 1,
+ "character": 20
+ }
+ },
+ "text": "/~/"
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": uri
+ },
+ "position": {
+ "line": 1,
+ "character": 23
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "/"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "b.ts",
+ "kind": 9,
+ "detail": "(import map)",
+ "sortText": "1",
+ "filterText": "/~/b.ts",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 20
+ },
+ "end": {
+ "line": 1,
+ "character": 23
+ }
+ },
+ "newText": "/~/b.ts"
+ },
+ "commitCharacters": ["\"", "'"],
+ }
+ ]
+ }))
+ );
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Deno.args);\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "const Deno.args: string[]"
+ },
+ "Returns the script arguments to the program.\n\nGive the following command line invocation of Deno:\n\n```sh\ndeno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd\n```\n\nThen `Deno.args` will contain:\n\n```\n[ \"/etc/passwd\" ]\n```\n\nIf you are looking for a structured way to parse arguments, there is the\n[`std/flags`](https://deno.land/std/flags) module as part of the Deno\nstandard library.",
+ "\n\n*@category* - Runtime Environment",
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 17
+ },
+ "end": {
+ "line": 0,
+ "character": 21
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_asset() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+ let (_, maybe_error) = client
+ .write_request::<_, _, Value>(
+ "textDocument/definition",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 14
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ let (_, maybe_error) = client
+ .write_request::<_, _, Value>(
+ "deno/virtualTextDocument",
+ json!({
+ "textDocument": {
+ "uri": "deno:/asset/lib.deno.shared_globals.d.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "deno:/asset/lib.es2015.symbol.wellknown.d.ts"
+ },
+ "position": {
+ "line": 109,
+ "character": 13
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "interface Date",
+ },
+ "Enables basic storage and retrieval of dates and times."
+ ],
+ "range": {
+ "start": {
+ "line": 109,
+ "character": 10,
+ },
+ "end": {
+ "line": 109,
+ "character": 14,
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_disabled() {
+ let mut client = init("initialize_params_disabled.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ )
+ .unwrap();
+
+ handle_configuration_request(&mut client, json!([{ "enable": false }]));
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_inlay_hints() {
+ let mut client = init("initialize_params_hints.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"function a(b: string) {
+ return b;
+ }
+
+ a("foo");
+
+ enum C {
+ A,
+ }
+
+ parseInt("123", 8);
+
+ const d = Date.now();
+
+ class E {
+ f = Date.now();
+ }
+
+ ["a"].map((v) => v + v);
+ "#
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/inlayHint",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 19,
+ "character": 0,
+ }
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ json!(maybe_res),
+ json!([
+ {
+ "position": {
+ "line": 0,
+ "character": 21
+ },
+ "label": ": string",
+ "kind": 1,
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 4,
+ "character": 10
+ },
+ "label": "b:",
+ "kind": 2,
+ "paddingRight": true
+ },
+ {
+ "position": {
+ "line": 7,
+ "character": 11
+ },
+ "label": "= 0",
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 10,
+ "character": 17
+ },
+ "label": "string:",
+ "kind": 2,
+ "paddingRight": true
+ },
+ {
+ "position": {
+ "line": 10,
+ "character": 24
+ },
+ "label": "radix:",
+ "kind": 2,
+ "paddingRight": true
+ },
+ {
+ "position": {
+ "line": 12,
+ "character": 15
+ },
+ "label": ": number",
+ "kind": 1,
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 15,
+ "character": 11
+ },
+ "label": ": number",
+ "kind": 1,
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 18,
+ "character": 18
+ },
+ "label": "callbackfn:",
+ "kind": 2,
+ "paddingRight": true
+ },
+ {
+ "position": {
+ "line": 18,
+ "character": 20
+ },
+ "label": ": string",
+ "kind": 1,
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 18,
+ "character": 21
+ },
+ "label": ": string",
+ "kind": 1,
+ "paddingLeft": true
+ }
+ ])
+ );
+}
+
+#[test]
+fn lsp_inlay_hints_not_enabled() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"function a(b: string) {
+ return b;
+ }
+
+ a("foo");
+
+ enum C {
+ A,
+ }
+
+ parseInt("123", 8);
+
+ const d = Date.now();
+
+ class E {
+ f = Date.now();
+ }
+
+ ["a"].map((v) => v + v);
+ "#
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/inlayHint",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 19,
+ "character": 0,
+ }
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(json!(maybe_res), json!(null));
+}
+
+#[test]
+fn lsp_workspace_enable_paths() {
+ let mut params: lsp::InitializeParams = serde_json::from_value(load_fixture(
+ "initialize_params_workspace_enable_paths.json",
+ ))
+ .unwrap();
+ // we aren't actually writing anything to the tempdir in this test, but we
+ // just need a legitimate file path on the host system so that logic that
+ // tries to convert to and from the fs paths works on all env
+ let temp_dir = TempDir::new();
+
+ let root_specifier =
+ ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
+
+ params.root_uri = Some(root_specifier.clone());
+ params.workspace_folders = Some(vec![lsp::WorkspaceFolder {
+ uri: root_specifier.clone(),
+ name: "project".to_string(),
+ }]);
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+
+ handle_configuration_request(
+ &mut client,
+ json!([{
+ "enable": false,
+ "enablePaths": [
+ "./worker"
+ ],
+ }]),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./other/file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./other/file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "(method) DateConstructor.now(): number",
+ },
+ "Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC)."
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 17,
+ },
+ "end": {
+ "line": 0,
+ "character": 20,
+ }
+ }
+ }))
+ );
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "(method) DateConstructor.now(): number",
+ },
+ "Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC)."
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 17,
+ },
+ "end": {
+ "line": 0,
+ "character": 20,
+ }
+ }
+ }))
+ );
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_unstable_disabled() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Deno.dlopen);\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "any"
+ }
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 17
+ },
+ "end": {
+ "line": 0,
+ "character": 23
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_unstable_enabled() {
+ let mut client = init("initialize_params_unstable.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Deno.ppid);\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents":[
+ {
+ "language":"typescript",
+ "value":"const Deno.ppid: number"
+ },
+ "The process ID of parent process of this instance of the Deno CLI.\n\n```ts\nconsole.log(Deno.ppid);\n```",
+ "\n\n*@category* - Runtime Environment",
+ ],
+ "range":{
+ "start":{
+ "line":0,
+ "character":17
+ },
+ "end":{
+ "line":0,
+ "character":21
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_change_mbc() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "const a = `编写软件很难`;\nconst b = `👍🦕😃`;\nconsole.log(a, b);\n"
+ }
+ }),
+ );
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 11
+ },
+ "end": {
+ "line": 1,
+ // the LSP uses utf16 encoded characters indexes, so
+ // after the deno emoiji is character index 15
+ "character": 15
+ }
+ },
+ "text": ""
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 2,
+ "character": 15
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "const b: \"😃\"",
+ },
+ "",
+ ],
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 15,
+ },
+ "end": {
+ "line": 2,
+ "character": 16,
+ },
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_closed_document() {
+ let temp_dir_guard = TempDir::new();
+ let temp_dir = temp_dir_guard.path();
+ let a_path = temp_dir.join("a.ts");
+ fs::write(a_path, r#"export const a = "a";"#).unwrap();
+ let b_path = temp_dir.join("b.ts");
+ fs::write(&b_path, r#"export * from "./a.ts";"#).unwrap();
+ let b_specifier = Url::from_file_path(b_path).unwrap();
+ let c_path = temp_dir.join("c.ts");
+ fs::write(&c_path, "import { a } from \"./b.ts\";\nconsole.log(a);\n")
+ .unwrap();
+ let c_specifier = Url::from_file_path(c_path).unwrap();
+
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": b_specifier,
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"export * from "./a.ts";"#
+ }
+ }),
+ )
+ .unwrap();
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client
+ .write_response(id, json!([{ "enable": true }]))
+ .unwrap();
+
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": c_specifier,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { a } from \"./b.ts\";\nconsole.log(a);\n",
+ }
+ }),
+ )
+ .unwrap();
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client
+ .write_response(id, json!([{ "enable": true }]))
+ .unwrap();
+
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": c_specifier,
+ },
+ "position": {
+ "line": 0,
+ "character": 10
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "(alias) const a: \"a\"\nimport a"
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 9
+ },
+ "end": {
+ "line": 0,
+ "character": 10
+ }
+ }
+ }))
+ );
+ client
+ .write_notification(
+ "textDocument/didClose",
+ json!({
+ "textDocument": {
+ "uri": b_specifier,
+ }
+ }),
+ )
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": c_specifier,
+ },
+ "position": {
+ "line": 0,
+ "character": 10
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "(alias) const a: \"a\"\nimport a"
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 9
+ },
+ "end": {
+ "line": 0,
+ "character": 10
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_dependency() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file_01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export const a = \"a\";\n",
+ }
+ }),
+ );
+ did_open(
+ &mut client,
+ load_fixture("did_open_params_import_hover.json"),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 0,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 19
+ },
+ "end":{
+ "line": 0,
+ "character": 62
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 3,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/subdir/type_reference.js\n\n**Types**: http&#8203;://127.0.0.1:4545/subdir/type_reference.d.ts\n"
+ },
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 19
+ },
+ "end":{
+ "line": 3,
+ "character": 67
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 4,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/subdir/mod1.ts\n"
+ },
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 19
+ },
+ "end":{
+ "line": 4,
+ "character": 57
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 5,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: _(a data url)_\n"
+ },
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 19
+ },
+ "end":{
+ "line": 5,
+ "character": 132
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 6,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: file&#8203;:///a/file_01.ts\n"
+ },
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 19
+ },
+ "end":{
+ "line": 6,
+ "character": 33
+ }
+ }
+ }))
+ );
+}
+
+// This tests for a regression covered by denoland/deno#12753 where the lsp was
+// unable to resolve dependencies when there was an invalid syntax in the module
+#[test]
+fn lsp_hover_deps_preserved_when_invalid_parse() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file1.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export type Foo = { bar(): string };\n"
+ }
+ }),
+ );
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file2.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { Foo } from './file1.ts'; declare const f: Foo; f\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_error) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file2.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 56
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "const f: Foo",
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 56,
+ },
+ "end": {
+ "line": 0,
+ "character": 57,
+ }
+ }
+ }))
+ );
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file2.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 57
+ },
+ "end": {
+ "line": 0,
+ "character": 58
+ }
+ },
+ "text": "."
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let (maybe_res, maybe_error) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file2.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 56
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "const f: Foo",
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 56,
+ },
+ "end": {
+ "line": 0,
+ "character": 57,
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_typescript_types() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"http://127.0.0.1:4545/xTypeScriptTypes.js\";\n\nconsole.log(a.foo);\n",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [
+ {
+ "uri": "http://127.0.0.1:4545/xTypeScriptTypes.js",
+ }
+ ],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 24
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_res.is_some());
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ json!(maybe_res.unwrap()),
+ json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 19
+ },
+ "end": {
+ "line": 0,
+ "character": 62
+ }
+ }
+ })
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_hover_jsdoc_symbol_link() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/b.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export function hello() {}\n"
+ }
+ }),
+ );
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { hello } from \"./b.ts\";\n\nhello();\n\nconst b = \"b\";\n\n/** JSDoc {@link hello} and {@linkcode b} */\nfunction a() {}\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 7,
+ "character": 10
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "function a(): void"
+ },
+ "JSDoc [hello](file:///a/file.ts#L1,10) and [`b`](file:///a/file.ts#L5,7)"
+ ],
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 9
+ },
+ "end": {
+ "line": 7,
+ "character": 10
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_goto_type_definition() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "interface A {\n a: string;\n}\n\nexport class B implements A {\n a = \"a\";\n log() {\n console.log(this.a);\n }\n}\n\nconst b = new B();\nb;\n",
+ }
+ }),
+ );
+ let (maybe_res, maybe_error) = client
+ .write_request::<_, _, Value>(
+ "textDocument/typeDefinition",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 12,
+ "character": 1
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([
+ {
+ "targetUri": "file:///a/file.ts",
+ "targetRange": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 9,
+ "character": 1
+ }
+ },
+ "targetSelectionRange": {
+ "start": {
+ "line": 4,
+ "character": 13
+ },
+ "end": {
+ "line": 4,
+ "character": 14
+ }
+ }
+ }
+ ]))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_call_hierarchy() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "function foo() {\n return false;\n}\n\nclass Bar {\n baz() {\n return foo();\n }\n}\n\nfunction main() {\n const bar = new Bar();\n bar.baz();\n}\n\nmain();"
+ }
+ }),
+ );
+ let (maybe_res, maybe_error) = client
+ .write_request(
+ "textDocument/prepareCallHierarchy",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 5,
+ "character": 3
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("prepare_call_hierarchy_response.json"))
+ );
+ let (maybe_res, maybe_error) = client
+ .write_request(
+ "callHierarchy/incomingCalls",
+ load_fixture("incoming_calls_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("incoming_calls_response.json"))
+ );
+ let (maybe_res, maybe_error) = client
+ .write_request(
+ "callHierarchy/outgoingCalls",
+ load_fixture("outgoing_calls_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_error.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("outgoing_calls_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_large_doc_changes() {
+ let mut client = init("initialize_params.json");
+ did_open(&mut client, load_fixture("did_open_params_large.json"));
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 444,
+ "character": 11
+ },
+ "end": {
+ "line": 444,
+ "character": 14
+ }
+ },
+ "text": "+++"
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 445,
+ "character": 4
+ },
+ "end": {
+ "line": 445,
+ "character": 4
+ }
+ },
+ "text": "// "
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 477,
+ "character": 4
+ },
+ "end": {
+ "line": 477,
+ "character": 9
+ }
+ },
+ "text": "error"
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 421,
+ "character": 30
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_res.is_some());
+ assert!(maybe_err.is_none());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 444,
+ "character": 6
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_res.is_some());
+ assert!(maybe_err.is_none());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 461,
+ "character": 34
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_res.is_some());
+ assert!(maybe_err.is_none());
+ shutdown(&mut client);
+
+ assert!(client.duration().as_millis() <= 15000);
+}
+
+#[test]
+fn lsp_document_symbol() {
+ let mut client = init("initialize_params.json");
+ did_open(&mut client, load_fixture("did_open_params_doc_symbol.json"));
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/documentSymbol",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("document_symbol_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_folding_range() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "// #region 1\n/*\n * Some comment\n */\nclass Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}\n// #endregion"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/foldingRange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([
+ {
+ "startLine": 0,
+ "endLine": 12,
+ "kind": "region"
+ },
+ {
+ "startLine": 1,
+ "endLine": 3,
+ "kind": "comment"
+ },
+ {
+ "startLine": 4,
+ "endLine": 10
+ },
+ {
+ "startLine": 5,
+ "endLine": 9
+ },
+ {
+ "startLine": 6,
+ "endLine": 7
+ }
+ ]))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_rename() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ // this should not rename in comments and strings
+ "text": "let variable = 'a'; // variable\nconsole.log(variable);\n\"variable\";\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/rename",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 4
+ },
+ "newName": "variable_modified"
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(load_fixture("rename_response.json")));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_selection_range() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "class Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/selectionRange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "positions": [
+ {
+ "line": 2,
+ "character": 8
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("selection_range_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_semantic_tokens() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ load_fixture("did_open_params_semantic_tokens.json"),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/semanticTokens/full",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "data": [
+ 0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15, 3, 10, 5, 0, 4, 1,
+ 6, 1, 0, 12, 7, 2, 16, 1, 8, 1, 7, 41, 0, 4, 1, 6, 0, 0, 2, 5, 11, 16,
+ 1, 9, 1, 7, 40, 3, 10, 4, 2, 1, 1, 11, 1, 9, 9, 1, 2, 3, 11, 1, 3, 6, 3,
+ 0, 1, 0, 15, 4, 2, 0, 1, 30, 1, 6, 9, 1, 2, 3, 11,1, 1, 9, 9, 9, 3, 0,
+ 16, 3, 0, 0, 1, 17, 12, 11, 3, 0, 24, 3, 0, 0, 0, 4, 9, 9, 2
+ ]
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/semanticTokens/range",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 0
+ }
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "data": [
+ 0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15, 3, 10, 5, 0, 4, 1,
+ 6, 1, 0, 12, 7, 2, 16, 1, 8, 1, 7, 41, 0, 4, 1, 6, 0, 0, 2, 5, 11, 16,
+ 1, 9, 1, 7, 40
+ ]
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_lens() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "class A {\n a = \"a\";\n\n b() {\n console.log(this.a);\n }\n\n c() {\n this.a = \"c\";\n }\n}\n\nconst a = new A();\na.b();\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(load_fixture("code_lens_response.json")));
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "codeLens/resolve",
+ json!({
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 6
+ },
+ "end": {
+ "line": 0,
+ "character": 7
+ }
+ },
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "source": "references"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_lens_resolve_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_lens_impl() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "interface A {\n b(): void;\n}\n\nclass B implements A {\n b() {\n console.log(\"b\");\n }\n}\n\ninterface C {\n c: string;\n}\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_lens_response_impl.json"))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "codeLens/resolve",
+ json!({
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 10
+ },
+ "end": {
+ "line": 0,
+ "character": 11
+ }
+ },
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "source": "implementations"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_lens_resolve_response_impl.json"))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "codeLens/resolve",
+ json!({
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 10
+ },
+ "end": {
+ "line": 10,
+ "character": 11
+ }
+ },
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "source": "implementations"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 10
+ },
+ "end": {
+ "line": 10,
+ "character": 11
+ }
+ },
+ "command": {
+ "title": "0 implementations",
+ "command": ""
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_lens_test() {
+ let mut client = init("initialize_params_code_lens_test.json");
+ did_open(
+ &mut client,
+ load_fixture("did_open_params_test_code_lens.json"),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_lens_response_test.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_lens_test_disabled() {
+ let mut client = init("initialize_params_code_lens_test_disabled.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ load_fixture("did_open_params_test_code_lens.json"),
+ )
+ .unwrap();
+
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client
+ .write_response(
+ id,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": false
+ }
+ }]),
+ )
+ .unwrap();
+
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (method, _) = client.read_notification::<Value>().unwrap();
+ assert_eq!(method, "textDocument/publishDiagnostics");
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!([])));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_lens_non_doc_nav_tree() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/references",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 3
+ },
+ "context": {
+ "includeDeclaration": true
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/virtualTextDocument",
+ json!({
+ "textDocument": {
+ "uri": "deno:/asset/lib.deno.shared_globals.d.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Vec<lsp::CodeLens>>(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "deno:/asset/lib.deno.shared_globals.d.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let res = maybe_res.unwrap();
+ assert!(res.len() > 50);
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, lsp::CodeLens>(
+ "codeLens/resolve",
+ json!({
+ "range": {
+ "start": {
+ "line": 416,
+ "character": 12
+ },
+ "end": {
+ "line": 416,
+ "character": 19
+ }
+ },
+ "data": {
+ "specifier": "asset:///lib.deno.shared_globals.d.ts",
+ "source": "references"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_nav_tree_updates() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "interface A {\n b(): void;\n}\n\nclass B implements A {\n b() {\n console.log(\"b\");\n }\n}\n\ninterface C {\n c: string;\n}\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_lens_response_impl.json"))
+ );
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 0
+ },
+ "end": {
+ "line": 13,
+ "character": 0
+ }
+ },
+ "text": ""
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeLens",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_lens_response_changed.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_signature_help() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "/**\n * Adds two numbers.\n * @param a This is a first number.\n * @param b This is a second number.\n */\nfunction add(a: number, b: number) {\n return a + b;\n}\n\nadd("
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/signatureHelp",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "character": 4,
+ "line": 9
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "(",
+ "isRetrigger": false
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "signatures": [
+ {
+ "label": "add(a: number, b: number): number",
+ "documentation": {
+ "kind": "markdown",
+ "value": "Adds two numbers."
+ },
+ "parameters": [
+ {
+ "label": "a: number",
+ "documentation": {
+ "kind": "markdown",
+ "value": "This is a first number."
+ }
+ },
+ {
+ "label": "b: number",
+ "documentation": {
+ "kind": "markdown",
+ "value": "This is a second number."
+ }
+ }
+ ]
+ }
+ ],
+ "activeSignature": 0,
+ "activeParameter": 0
+ }))
+ );
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 9,
+ "character": 4
+ },
+ "end": {
+ "line": 9,
+ "character": 4
+ }
+ },
+ "text": "123, "
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/signatureHelp",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "character": 8,
+ "line": 9
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "signatures": [
+ {
+ "label": "add(a: number, b: number): number",
+ "documentation": {
+ "kind": "markdown",
+ "value": "Adds two numbers."
+ },
+ "parameters": [
+ {
+ "label": "a: number",
+ "documentation": {
+ "kind": "markdown",
+ "value": "This is a first number."
+ }
+ },
+ {
+ "label": "b: number",
+ "documentation": {
+ "kind": "markdown",
+ "value": "This is a second number."
+ }
+ }
+ ]
+ }
+ ],
+ "activeSignature": 0,
+ "activeParameter": 1
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_actions() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export function a(): void {\n await Promise.resolve(\"a\");\n}\n\nexport function b(): void {\n await Promise.resolve(\"b\");\n}\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(load_fixture("code_action_response.json")));
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "codeAction/resolve",
+ load_fixture("code_action_resolve_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_resolve_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_actions_deno_cache() {
+ let mut session = TestSession::from_file("initialize_params.json");
+ let diagnostics = session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"https://deno.land/x/a/mod.ts\";\n\nconsole.log(a);\n"
+ }
+ }));
+ assert_eq!(
+ diagnostics.with_source("deno"),
+ load_fixture_as("diagnostics_deno_deps.json")
+ );
+
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_params_cache.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_response_cache.json"))
+ );
+ session.shutdown_and_exit();
+}
+
+#[test]
+fn lsp_code_actions_deno_cache_npm() {
+ let mut session = TestSession::from_file("initialize_params.json");
+ let diagnostics = session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import chalk from \"npm:chalk\";\n\nconsole.log(chalk.green);\n"
+ }
+ }));
+ assert_eq!(
+ diagnostics.with_source("deno"),
+ load_fixture_as("code_actions/cache_npm/diagnostics.json")
+ );
+
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_actions/cache_npm/cache_action.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_actions/cache_npm/cache_response.json"))
+ );
+ session.shutdown_and_exit();
+}
+
+#[test]
+fn lsp_code_actions_imports() {
+ let mut session = TestSession::from_file("initialize_params.json");
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file00.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"export interface MallardDuckConfigOptions extends DuckConfigOptions {
+ kind: "mallard";
+}
+
+export class MallardDuckConfig extends DuckConfig {
+ constructor(options: MallardDuckConfigOptions) {
+ super(options);
+ }
+}
+"#
+ }
+ }));
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"import { DuckConfigOptions } from "./file02.ts";
+
+export class DuckConfig {
+ readonly kind;
+ constructor(options: DuckConfigOptions) {
+ this.kind = options.kind;
+ }
+}
+"#
+ }
+ }));
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file02.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"export interface DuckConfigOptions {
+ kind: string;
+ quacks: boolean;
+}
+"#
+ }
+ }));
+
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_params_imports.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_response_imports.json"))
+ );
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request(
+ "codeAction/resolve",
+ load_fixture("code_action_resolve_params_imports.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_resolve_response_imports.json"))
+ );
+
+ session.shutdown_and_exit();
+}
+
+#[test]
+fn lsp_code_actions_refactor() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "var x: { a?: number; b?: string } = {};\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_params_refactor.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_response_refactor.json"))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "codeAction/resolve",
+ load_fixture("code_action_resolve_params_refactor.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_resolve_response_refactor.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_actions_refactor_no_disabled_support() {
+ let mut client = init("initialize_params_ca_no_disabled.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "interface A {\n a: string;\n}\n\ninterface B {\n b: string;\n}\n\nclass AB implements A, B {\n a = \"a\";\n b = \"b\";\n}\n\nnew AB().a;\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 14,
+ "character": 0
+ }
+ },
+ "context": {
+ "diagnostics": [],
+ "only": [
+ "refactor"
+ ]
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_response_no_disabled.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_actions_deadlock() {
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ load_fixture("did_open_params_large.json"),
+ )
+ .unwrap();
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client
+ .write_response(id, json!([{ "enable": true }]))
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/semanticTokens/full",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ read_diagnostics(&mut client);
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 444,
+ "character": 11
+ },
+ "end": {
+ "line": 444,
+ "character": 14
+ }
+ },
+ "text": "+++"
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 445,
+ "character": 4
+ },
+ "end": {
+ "line": 445,
+ "character": 4
+ }
+ },
+ "text": "// "
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 477,
+ "character": 4
+ },
+ "end": {
+ "line": 477,
+ "character": 9
+ }
+ },
+ "text": "error"
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ // diagnostics only trigger after changes have elapsed in a separate thread,
+ // so we need to delay the next messages a little bit to attempt to create a
+ // potential for a deadlock with the codeAction
+ std::thread::sleep(std::time::Duration::from_millis(50));
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 609,
+ "character": 33,
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/codeAction",
+ load_fixture("code_action_params_deadlock.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+
+ read_diagnostics(&mut client);
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_completions() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "Deno."
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 5
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "."
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert!(list.items.len() > 90);
+ } else {
+ panic!("unexpected response");
+ }
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ load_fixture("completion_resolve_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("completion_resolve_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_completions_private_fields() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"class Foo { #myProperty = "value"; constructor() { this.# } }"#
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 57
+ },
+ "context": {
+ "triggerKind": 1
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert_eq!(list.items.len(), 1);
+ let item = &list.items[0];
+ assert_eq!(item.label, "#myProperty");
+ assert!(!list.is_incomplete);
+ } else {
+ panic!("unexpected response");
+ }
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_completions_optional() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "interface A {\n b?: string;\n}\n\nconst o: A = {};\n\nfunction c(s: string) {}\n\nc(o.)"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ load_fixture("completion_request_params_optional.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "b?",
+ "kind": 5,
+ "sortText": "11",
+ "filterText": "b",
+ "insertText": "b",
+ "commitCharacters": [".", ",", ";", "("],
+ "data": {
+ "tsc": {
+ "specifier": "file:///a/file.ts",
+ "position": 79,
+ "name": "b",
+ "useCodeSnippet": false
+ }
+ }
+ }
+ ]
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ load_fixture("completion_resolve_params_optional.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "label": "b?",
+ "kind": 5,
+ "detail": "(property) A.b?: string | undefined",
+ "documentation": {
+ "kind": "markdown",
+ "value": ""
+ },
+ "sortText": "1",
+ "filterText": "b",
+ "insertText": "b"
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_completions_auto_import() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/b.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export const foo = \"foo\";\n",
+ }
+ }),
+ );
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export {};\n\n",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 2,
+ "character": 0,
+ },
+ "context": {
+ "triggerKind": 1,
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ if !list.items.iter().any(|item| item.label == "foo") {
+ panic!("completions items missing 'foo' symbol");
+ }
+ } else {
+ panic!("unexpected completion response");
+ }
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ json!({
+ "label": "foo",
+ "kind": 6,
+ "sortText": "￿16",
+ "commitCharacters": [
+ ".",
+ ",",
+ ";",
+ "("
+ ],
+ "data": {
+ "tsc": {
+ "specifier": "file:///a/file.ts",
+ "position": 12,
+ "name": "foo",
+ "source": "./b",
+ "data": {
+ "exportName": "foo",
+ "moduleSpecifier": "./b",
+ "fileName": "file:///a/b.ts"
+ },
+ "useCodeSnippet": false
+ }
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "label": "foo",
+ "kind": 6,
+ "detail": "const foo: \"foo\"",
+ "documentation": {
+ "kind": "markdown",
+ "value": ""
+ },
+ "sortText": "￿16",
+ "additionalTextEdits": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 0,
+ "character": 0
+ }
+ },
+ "newText": "import { foo } from \"./b.ts\";\n\n"
+ }
+ ]
+ }))
+ );
+}
+
+#[test]
+fn lsp_completions_snippet() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/a.tsx",
+ "languageId": "typescriptreact",
+ "version": 1,
+ "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return <A t\n}",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/a.tsx"
+ },
+ "position": {
+ "line": 5,
+ "character": 13,
+ },
+ "context": {
+ "triggerKind": 1,
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert_eq!(
+ json!(list),
+ json!({
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "type",
+ "kind": 5,
+ "sortText": "11",
+ "filterText": "type=\"$1\"",
+ "insertText": "type=\"$1\"",
+ "insertTextFormat": 2,
+ "commitCharacters": [
+ ".",
+ ",",
+ ";",
+ "("
+ ],
+ "data": {
+ "tsc": {
+ "specifier": "file:///a/a.tsx",
+ "position": 87,
+ "name": "type",
+ "useCodeSnippet": false
+ }
+ }
+ }
+ ]
+ })
+ );
+ } else {
+ panic!("unexpected completion response");
+ }
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ json!({
+ "label": "type",
+ "kind": 5,
+ "sortText": "11",
+ "filterText": "type=\"$1\"",
+ "insertText": "type=\"$1\"",
+ "insertTextFormat": 2,
+ "commitCharacters": [
+ ".",
+ ",",
+ ";",
+ "("
+ ],
+ "data": {
+ "tsc": {
+ "specifier": "file:///a/a.tsx",
+ "position": 87,
+ "name": "type",
+ "useCodeSnippet": false
+ }
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "label": "type",
+ "kind": 5,
+ "detail": "(property) type: string",
+ "documentation": {
+ "kind": "markdown",
+ "value": ""
+ },
+ "sortText": "11",
+ "filterText": "type=\"$1\"",
+ "insertText": "type=\"$1\"",
+ "insertTextFormat": 2
+ }))
+ );
+}
+
+#[test]
+fn lsp_completions_no_snippet() {
+ let mut client = init("initialize_params_no_snippet.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/a.tsx",
+ "languageId": "typescriptreact",
+ "version": 1,
+ "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return <A t\n}",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/a.tsx"
+ },
+ "position": {
+ "line": 5,
+ "character": 13,
+ },
+ "context": {
+ "triggerKind": 1,
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert_eq!(
+ json!(list),
+ json!({
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "type",
+ "kind": 5,
+ "sortText": "11",
+ "commitCharacters": [
+ ".",
+ ",",
+ ";",
+ "("
+ ],
+ "data": {
+ "tsc": {
+ "specifier": "file:///a/a.tsx",
+ "position": 87,
+ "name": "type",
+ "useCodeSnippet": false
+ }
+ }
+ }
+ ]
+ })
+ );
+ } else {
+ panic!("unexpected completion response");
+ }
+}
+
+#[test]
+fn lsp_completions_npm() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import cjsDefault from 'npm:@denotest/cjs-default-export';import chalk from 'npm:chalk';\n\n",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [
+ {
+ "uri": "npm:@denotest/cjs-default-export",
+ },
+ {
+ "uri": "npm:chalk",
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+
+ // check importing a cjs default import
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 0
+ },
+ "end": {
+ "line": 2,
+ "character": 0
+ }
+ },
+ "text": "cjsDefault."
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ read_diagnostics(&mut client);
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 2,
+ "character": 11
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "."
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert_eq!(list.items.len(), 3);
+ assert!(list.items.iter().any(|i| i.label == "default"));
+ assert!(list.items.iter().any(|i| i.label == "MyClass"));
+ } else {
+ panic!("unexpected response");
+ }
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ load_fixture("completions/npm/resolve_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("completions/npm/resolve_response.json"))
+ );
+
+ // now check chalk, which is esm
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "version": 3
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 0
+ },
+ "end": {
+ "line": 2,
+ "character": 11
+ }
+ },
+ "text": "chalk."
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ read_diagnostics(&mut client);
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 2,
+ "character": 6
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "."
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert!(list.items.iter().any(|i| i.label == "green"));
+ assert!(list.items.iter().any(|i| i.label == "red"));
+ } else {
+ panic!("unexpected response");
+ }
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_npm_specifier_unopened_file() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+
+ // create other.ts, which re-exports an npm specifier
+ client.deno_dir().write(
+ "other.ts",
+ "export { default as chalk } from 'npm:chalk@5';",
+ );
+
+ // cache the other.ts file to the DENO_DIR
+ let deno = deno_cmd_with_deno_dir(client.deno_dir())
+ .current_dir(client.deno_dir().path())
+ .arg("cache")
+ .arg("--quiet")
+ .arg("other.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.status.code(), Some(0));
+
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ assert!(stdout.is_empty());
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert!(stderr.is_empty());
+
+ // open main.ts, which imports other.ts (unopened)
+ let main_url =
+ ModuleSpecifier::from_file_path(client.deno_dir().path().join("main.ts"))
+ .unwrap();
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": main_url,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { chalk } from './other.ts';\n\n",
+ }
+ }),
+ );
+
+ client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": main_url,
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 0
+ },
+ "end": {
+ "line": 2,
+ "character": 0
+ }
+ },
+ "text": "chalk."
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ read_diagnostics(&mut client);
+
+ // now ensure completions work
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": main_url
+ },
+ "position": {
+ "line": 2,
+ "character": 6
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "."
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert_eq!(list.items.len(), 63);
+ assert!(list.items.iter().any(|i| i.label == "ansi256"));
+ } else {
+ panic!("unexpected response");
+ }
+}
+
+#[test]
+fn lsp_completions_registry() {
+ let _g = http_server();
+ let mut client = init("initialize_params_registry.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"http://localhost:4545/x/a@\""
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 46
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "@"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert_eq!(list.items.len(), 3);
+ } else {
+ panic!("unexpected response");
+ }
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ load_fixture("completion_resolve_params_registry.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("completion_resolve_response_registry.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_completions_registry_empty() {
+ let _g = http_server();
+ let mut client = init("initialize_params_registry.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"\""
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 20
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "\""
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("completion_request_response_empty.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_auto_discover_registry() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"http://localhost:4545/x/a@\""
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 46
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "@"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (method, maybe_res) = client.read_notification().unwrap();
+ assert_eq!(method, "deno/registryState");
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "origin": "http://localhost:4545",
+ "suggestions": true,
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_cache_location() {
+ let _g = http_server();
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params_registry.json"))
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("cache".to_string(), json!(".cache"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+ client.write_notification("initialized", json!({})).unwrap();
+ let mut session = TestSession::from_client(client);
+
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file_01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export const a = \"a\";\n",
+ }
+ }));
+ let diagnostics =
+ session.did_open(load_fixture("did_open_params_import_hover.json"));
+ assert_eq!(diagnostics.viewed().len(), 7);
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 0,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 19
+ },
+ "end":{
+ "line": 0,
+ "character": 62
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 7,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
+ },
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 19
+ },
+ "end": {
+ "line": 7,
+ "character": 53
+ }
+ }
+ }))
+ );
+ let cache_path = temp_dir.path().join(".cache");
+ assert!(cache_path.is_dir());
+ assert!(cache_path.join("gen").is_dir());
+ session.shutdown_and_exit();
+}
+
+/// Sets the TLS root certificate on startup, which allows the LSP to connect to
+/// the custom signed test server and be able to retrieve the registry config
+/// and cache files.
+#[test]
+fn lsp_tls_cert() {
+ let _g = http_server();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params_tls_cert.json"))
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(testdata_path()).unwrap());
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+ client.write_notification("initialized", json!({})).unwrap();
+ let mut session = TestSession::from_client(client);
+
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file_01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export const a = \"a\";\n",
+ }
+ }));
+ let diagnostics =
+ session.did_open(load_fixture("did_open_params_tls_cert.json"));
+ let diagnostics = diagnostics.viewed();
+ assert_eq!(diagnostics.len(), 7);
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 0,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: https&#8203;://localhost:5545/xTypeScriptTypes.js\n"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 19
+ },
+ "end":{
+ "line": 0,
+ "character": 63
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = session
+ .client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 7,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
+ },
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 19
+ },
+ "end": {
+ "line": 7,
+ "character": 53
+ }
+ }
+ }))
+ );
+ session.shutdown_and_exit();
+}
+
+#[test]
+fn lsp_diagnostics_warn_redirect() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"http://127.0.0.1:4545/x_deno_warning.js\";\n\nconsole.log(a)\n",
+ },
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [
+ {
+ "uri": "http://127.0.0.1:4545/x_deno_warning.js",
+ }
+ ],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let diagnostics = read_diagnostics(&mut client);
+ assert_eq!(
+ diagnostics.with_source("deno"),
+ lsp::PublishDiagnosticsParams {
+ uri: Url::parse("file:///a/file.ts").unwrap(),
+ diagnostics: vec![
+ lsp::Diagnostic {
+ range: lsp::Range {
+ start: lsp::Position {
+ line: 0,
+ character: 19
+ },
+ end: lsp::Position {
+ line: 0,
+ character: 60
+ }
+ },
+ severity: Some(lsp::DiagnosticSeverity::WARNING),
+ code: Some(lsp::NumberOrString::String("deno-warn".to_string())),
+ source: Some("deno".to_string()),
+ message: "foobar".to_string(),
+ ..Default::default()
+ },
+ lsp::Diagnostic {
+ range: lsp::Range {
+ start: lsp::Position {
+ line: 0,
+ character: 19
+ },
+ end: lsp::Position {
+ line: 0,
+ character: 60
+ }
+ },
+ severity: Some(lsp::DiagnosticSeverity::INFORMATION),
+ code: Some(lsp::NumberOrString::String("redirect".to_string())),
+ source: Some("deno".to_string()),
+ message: "The import of \"http://127.0.0.1:4545/x_deno_warning.js\" was redirected to \"http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js\".".to_string(),
+ data: Some(json!({"specifier": "http://127.0.0.1:4545/x_deno_warning.js", "redirect": "http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js"})),
+ ..Default::default()
+ }
+ ],
+ version: Some(1),
+ }
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_redirect_quick_fix() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"http://127.0.0.1:4545/x_deno_warning.js\";\n\nconsole.log(a)\n",
+ },
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [
+ {
+ "uri": "http://127.0.0.1:4545/x_deno_warning.js",
+ }
+ ],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let diagnostics = read_diagnostics(&mut client)
+ .with_source("deno")
+ .diagnostics;
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ json!(json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 19
+ },
+ "end": {
+ "line": 0,
+ "character": 60
+ }
+ },
+ "context": {
+ "diagnostics": diagnostics,
+ "only": [
+ "quickfix"
+ ]
+ }
+ })),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_redirect_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_diagnostics_deprecated() {
+ let mut client = init("initialize_params.json");
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "/** @deprecated */\nexport const a = \"a\";\n\na;\n",
+ },
+ }),
+ );
+ assert_eq!(
+ json!(diagnostics),
+ json!([
+ {
+ "uri": "file:///a/file.ts",
+ "diagnostics": [],
+ "version": 1
+ },
+ {
+ "uri": "file:///a/file.ts",
+ "diagnostics": [],
+ "version": 1
+ },
+ {
+ "uri": "file:///a/file.ts",
+ "diagnostics": [
+ {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 0
+ },
+ "end": {
+ "line": 3,
+ "character": 1
+ }
+ },
+ "severity": 4,
+ "code": 6385,
+ "source": "deno-ts",
+ "message": "'a' is deprecated.",
+ "relatedInformation": [],
+ "tags": [
+ 2
+ ]
+ }
+ ],
+ "version": 1
+ }
+ ])
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_diagnostics_deno_types() {
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ load_fixture("did_open_params_deno_types.json"),
+ )
+ .unwrap();
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client
+ .write_response(id, json!([{ "enable": true }]))
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/documentSymbol",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_res.is_some());
+ assert!(maybe_err.is_none());
+ let diagnostics = read_diagnostics(&mut client);
+ assert_eq!(diagnostics.viewed().len(), 5);
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_diagnostics_refresh_dependents() {
+ let mut session = TestSession::from_file("initialize_params.json");
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file_00.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export const a = \"a\";\n",
+ },
+ }));
+ session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file_01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export * from \"./file_00.ts\";\n",
+ },
+ }));
+ let diagnostics = session.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file_02.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { a, b } from \"./file_01.ts\";\n\nconsole.log(a, b);\n"
+ }
+ }));
+ assert_eq!(
+ json!(diagnostics.with_file_and_source("file:///a/file_02.ts", "deno-ts")),
+ json!({
+ "uri": "file:///a/file_02.ts",
+ "diagnostics": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 12
+ },
+ "end": {
+ "line": 0,
+ "character": 13
+ }
+ },
+ "severity": 1,
+ "code": 2305,
+ "source": "deno-ts",
+ "message": "Module '\"./file_01.ts\"' has no exported member 'b'."
+ }
+ ],
+ "version": 1
+ })
+ );
+
+ // fix the code causing the diagnostic
+ session
+ .client
+ .write_notification(
+ "textDocument/didChange",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file_00.ts",
+ "version": 2
+ },
+ "contentChanges": [
+ {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 0
+ },
+ "end": {
+ "line": 1,
+ "character": 0
+ }
+ },
+ "text": "export const b = \"b\";\n"
+ }
+ ]
+ }),
+ )
+ .unwrap();
+ let diagnostics = session.read_diagnostics();
+ assert_eq!(diagnostics.viewed().len(), 0); // no diagnostics now
+
+ session.shutdown_and_exit();
+ assert_eq!(session.client.queue_len(), 0);
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PerformanceAverage {
+ pub name: String,
+ pub count: u32,
+ pub average_duration: u32,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct PerformanceAverages {
+ averages: Vec<PerformanceAverage>,
+}
+
+#[test]
+fn lsp_performance() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Deno.args);\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, PerformanceAverages>("deno/performance", json!(null))
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(res) = maybe_res {
+ assert_eq!(res.averages.len(), 13);
+ } else {
+ panic!("unexpected result");
+ }
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_no_changes() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console;\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+ client.assert_no_notification("window/showMessage");
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_error() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console test test\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_mbc() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "const bar = '👍🇺🇸😃'\nconsole.log('hello deno')\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!(load_fixture("formatting_mbc_response.json")))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_exclude_with_config() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let deno_fmt_jsonc =
+ serde_json::to_vec_pretty(&load_fixture("deno.fmt.exclude.jsonc")).unwrap();
+ fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./deno.fmt.jsonc"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ let file_uri =
+ ModuleSpecifier::from_file_path(temp_dir.path().join("ignored.ts"))
+ .unwrap()
+ .to_string();
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": file_uri,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "function myFunc(){}"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": file_uri
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_exclude_default_config() {
+ let temp_dir = TempDir::new();
+ let workspace_root = temp_dir.path().canonicalize().unwrap();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let deno_jsonc =
+ serde_json::to_vec_pretty(&load_fixture("deno.fmt.exclude.jsonc")).unwrap();
+ fs::write(workspace_root.join("deno.jsonc"), deno_jsonc).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(workspace_root.clone()).unwrap());
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ let file_uri =
+ ModuleSpecifier::from_file_path(workspace_root.join("ignored.ts"))
+ .unwrap()
+ .to_string();
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": file_uri,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "function myFunc(){}"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": file_uri
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_json() {
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ // Also test out using a non-json file extension here.
+ // What should matter is the language identifier.
+ "uri": "file:///a/file.lock",
+ "languageId": "json",
+ "version": 1,
+ "text": "{\"key\":\"value\"}"
+ }
+ }),
+ )
+ .unwrap();
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.lock"
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": " "
+ },
+ {
+ "range": {
+ "start": { "line": 0, "character": 7 },
+ "end": { "line": 0, "character": 7 }
+ },
+ "newText": " "
+ },
+ {
+ "range": {
+ "start": { "line": 0, "character": 14 },
+ "end": { "line": 0, "character": 15 }
+ },
+ "newText": " }\n"
+ }
+ ]))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_json_no_diagnostics() {
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.json",
+ "languageId": "json",
+ "version": 1,
+ "text": "{\"key\":\"value\"}"
+ }
+ }),
+ )
+ .unwrap();
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/semanticTokens/full",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.json"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.json"
+ },
+ "position": {
+ "line": 0,
+ "character": 3
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_markdown() {
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.md",
+ "languageId": "markdown",
+ "version": 1,
+ "text": "# Hello World"
+ }
+ }),
+ )
+ .unwrap();
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.md"
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([
+ {
+ "range": {
+ "start": { "line": 0, "character": 1 },
+ "end": { "line": 0, "character": 3 }
+ },
+ "newText": ""
+ },
+ {
+ "range": {
+ "start": { "line": 0, "character": 15 },
+ "end": { "line": 0, "character": 15 }
+ },
+ "newText": "\n"
+ }
+ ]))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_format_with_config() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let deno_fmt_jsonc =
+ serde_json::to_vec_pretty(&load_fixture("deno.fmt.jsonc")).unwrap();
+ fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./deno.fmt.jsonc"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export async function someVeryLongFunctionName() {\nconst response = fetch(\"http://localhost:4545/some/non/existent/path.json\");\nconsole.log(response.text());\nconsole.log(\"finished!\")\n}"
+ }
+ }),
+ )
+ .unwrap();
+
+ // The options below should be ignored in favor of configuration from config file.
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/formatting",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "options": {
+ "tabSize": 2,
+ "insertSpaces": true
+ }
+ }),
+ )
+ .unwrap();
+
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([{
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 0
+ },
+ "end": {
+ "line": 1,
+ "character": 0
+ }
+ },
+ "newText": "\t"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 23
+ },
+ "end": {
+ "line": 1,
+ "character": 24
+ }
+ },
+ "newText": "\n\t\t'"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 73
+ },
+ "end": {
+ "line": 1,
+ "character": 74
+ }
+ },
+ "newText": "',\n\t"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 0
+ },
+ "end": {
+ "line": 2,
+ "character": 0
+ }
+ },
+ "newText": "\t"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 0
+ },
+ "end": {
+ "line": 3,
+ "character": 0
+ }
+ },
+ "newText": "\t"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 12
+ },
+ "end": {
+ "line": 3,
+ "character": 13
+ }
+ },
+ "newText": "'"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 22
+ },
+ "end": {
+ "line": 3,
+ "character": 24
+ }
+ },
+ "newText": "');"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 1
+ },
+ "end": {
+ "line": 4,
+ "character": 1
+ }
+ },
+ "newText": "\n"
+ }]
+ ))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_markdown_no_diagnostics() {
+ let mut client = init("initialize_params.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.md",
+ "languageId": "markdown",
+ "version": 1,
+ "text": "# Hello World"
+ }
+ }),
+ )
+ .unwrap();
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/semanticTokens/full",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.md"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.md"
+ },
+ "position": {
+ "line": 0,
+ "character": 3
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_configuration_did_change() {
+ let _g = http_server();
+ let mut client = init("initialize_params_did_config_change.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"http://localhost:4545/x/a@\""
+ }
+ }),
+ );
+ client
+ .write_notification(
+ "workspace/didChangeConfiguration",
+ json!({
+ "settings": {}
+ }),
+ )
+ .unwrap();
+ let (id, method, _) = client.read_request::<Value>().unwrap();
+ assert_eq!(method, "workspace/configuration");
+ client
+ .write_response(
+ id,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "implementations": true,
+ "references": true
+ },
+ "importMap": null,
+ "lint": true,
+ "suggest": {
+ "autoImports": true,
+ "completeFunctionCalls": false,
+ "names": true,
+ "paths": true,
+ "imports": {
+ "hosts": {
+ "http://localhost:4545/": true
+ }
+ }
+ },
+ "unstable": false
+ }]),
+ )
+ .unwrap();
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/completion",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 46
+ },
+ "context": {
+ "triggerKind": 2,
+ "triggerCharacter": "@"
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
+ assert!(!list.is_incomplete);
+ assert_eq!(list.items.len(), 3);
+ } else {
+ panic!("unexpected response");
+ }
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "completionItem/resolve",
+ load_fixture("completion_resolve_params_registry.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("completion_resolve_response_registry.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_workspace_symbol() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export class A {\n fieldA: string;\n fieldB: string;\n}\n",
+ }
+ }),
+ );
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file_01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export class B {\n fieldC: string;\n fieldD: string;\n}\n",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "workspace/symbol",
+ json!({
+ "query": "field"
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!([
+ {
+ "name": "fieldA",
+ "kind": 8,
+ "location": {
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 2
+ },
+ "end": {
+ "line": 1,
+ "character": 17
+ }
+ }
+ },
+ "containerName": "A"
+ },
+ {
+ "name": "fieldB",
+ "kind": 8,
+ "location": {
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 2
+ },
+ "end": {
+ "line": 2,
+ "character": 17
+ }
+ }
+ },
+ "containerName": "A"
+ },
+ {
+ "name": "fieldC",
+ "kind": 8,
+ "location": {
+ "uri": "file:///a/file_01.ts",
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 2
+ },
+ "end": {
+ "line": 1,
+ "character": 17
+ }
+ }
+ },
+ "containerName": "B"
+ },
+ {
+ "name": "fieldD",
+ "kind": 8,
+ "location": {
+ "uri": "file:///a/file_01.ts",
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 2
+ },
+ "end": {
+ "line": 2,
+ "character": 17
+ }
+ }
+ },
+ "containerName": "B"
+ }
+ ]))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_code_actions_ignore_lint() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "let message = 'Hello, Deno!';\nconsole.log(message);\n"
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_ignore_lint_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_ignore_lint_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+/// This test exercises updating an existing deno-lint-ignore-file comment.
+#[test]
+fn lsp_code_actions_update_ignore_lint() {
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text":
+"#!/usr/bin/env -S deno run
+// deno-lint-ignore-file camelcase
+let snake_case = 'Hello, Deno!';
+console.log(snake_case);
+",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_update_ignore_lint_params.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_update_ignore_lint_response.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_lint_with_config() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let deno_lint_jsonc =
+ serde_json::to_vec_pretty(&load_fixture("deno.lint.jsonc")).unwrap();
+ fs::write(temp_dir.path().join("deno.lint.jsonc"), deno_lint_jsonc).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./deno.lint.jsonc"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+ let mut session = TestSession::from_client(client);
+
+ let diagnostics = session.did_open(load_fixture("did_open_lint.json"));
+ let diagnostics = diagnostics.viewed();
+ assert_eq!(diagnostics.len(), 1);
+ assert_eq!(
+ diagnostics[0].code,
+ Some(lsp::NumberOrString::String("ban-untagged-todo".to_string()))
+ );
+ session.shutdown_and_exit();
+}
+
+#[test]
+fn lsp_lint_exclude_with_config() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let deno_lint_jsonc =
+ serde_json::to_vec_pretty(&load_fixture("deno.lint.exclude.jsonc"))
+ .unwrap();
+ fs::write(temp_dir.path().join("deno.lint.jsonc"), deno_lint_jsonc).unwrap();
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert("config".to_string(), json!("./deno.lint.jsonc"));
+ params.initialization_options = Some(Value::Object(map));
+ }
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": ModuleSpecifier::from_file_path(temp_dir.path().join("ignored.ts")).unwrap().to_string(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "// TODO: fixme\nexport async function non_camel_case() {\nconsole.log(\"finished!\")\n}"
+ }
+ }),
+ );
+ let diagnostics = diagnostics
+ .into_iter()
+ .flat_map(|x| x.diagnostics)
+ .collect::<Vec<_>>();
+ assert_eq!(diagnostics, Vec::new());
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_jsx_import_source_pragma() {
+ let _g = http_server();
+ let mut client = init("initialize_params.json");
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.tsx",
+ "languageId": "typescriptreact",
+ "version": 1,
+ "text":
+"/** @jsxImportSource http://localhost:4545/jsx */
+
+function A() {
+ return \"hello\";
+}
+
+export function B() {
+ return <A></A>;
+}
+",
+ }
+ }),
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.tsx",
+ },
+ "uris": [
+ {
+ "uri": "http://127.0.0.1:4545/jsx/jsx-runtime",
+ }
+ ],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.tsx"
+ },
+ "position": {
+ "line": 0,
+ "character": 25
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/jsx/jsx-runtime\n",
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 21
+ },
+ "end": {
+ "line": 0,
+ "character": 46
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[derive(Debug, Clone, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+struct TestData {
+ id: String,
+ label: String,
+ steps: Option<Vec<TestData>>,
+ range: Option<lsp::Range>,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+enum TestModuleNotificationKind {
+ Insert,
+ Replace,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct TestModuleNotificationParams {
+ text_document: lsp::TextDocumentIdentifier,
+ kind: TestModuleNotificationKind,
+ label: String,
+ tests: Vec<TestData>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct EnqueuedTestModule {
+ text_document: lsp::TextDocumentIdentifier,
+ ids: Vec<String>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct TestRunResponseParams {
+ enqueued: Vec<EnqueuedTestModule>,
+}
+
+#[test]
+fn lsp_testing_api() {
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+ let temp_dir = TempDir::new();
+
+ let root_specifier =
+ ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
+
+ let module_path = temp_dir.path().join("./test.ts");
+ let specifier = ModuleSpecifier::from_file_path(&module_path).unwrap();
+ let contents = r#"
+Deno.test({
+ name: "test a",
+ fn() {
+ console.log("test a");
+ }
+});
+"#;
+ fs::write(&module_path, contents).unwrap();
+ fs::write(temp_dir.path().join("./deno.jsonc"), r#"{}"#).unwrap();
+
+ params.root_uri = Some(root_specifier);
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": specifier,
+ "languageId": "typescript",
+ "version": 1,
+ "text": contents,
+ }
+ }),
+ )
+ .unwrap();
+
+ handle_configuration_request(
+ &mut client,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": true
+ }
+ }]),
+ );
+
+ for _ in 0..4 {
+ let result = client.read_notification::<Value>();
+ assert!(result.is_ok());
+ let (method, notification) = result.unwrap();
+ if method.as_str() == "deno/testModule" {
+ let params: TestModuleNotificationParams =
+ serde_json::from_value(notification.unwrap()).unwrap();
+ assert_eq!(params.text_document.uri, specifier);
+ assert_eq!(params.kind, TestModuleNotificationKind::Replace);
+ assert_eq!(params.label, "test.ts");
+ assert_eq!(params.tests.len(), 1);
+ let test = &params.tests[0];
+ assert_eq!(test.label, "test a");
+ assert!(test.steps.is_none());
+ assert_eq!(
+ test.range,
+ Some(lsp::Range {
+ start: lsp::Position {
+ line: 1,
+ character: 5,
+ },
+ end: lsp::Position {
+ line: 1,
+ character: 9,
+ }
+ })
+ );
+ }
+ }
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, TestRunResponseParams>(
+ "deno/testRun",
+ json!({
+ "id": 1,
+ "kind": "run",
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let res = maybe_res.unwrap();
+ assert_eq!(res.enqueued.len(), 1);
+ assert_eq!(res.enqueued[0].text_document.uri, specifier);
+ assert_eq!(res.enqueued[0].ids.len(), 1);
+ let id = res.enqueued[0].ids[0].clone();
+
+ let res = client.read_notification::<Value>();
+ assert!(res.is_ok());
+ let (method, notification) = res.unwrap();
+ assert_eq!(method, "deno/testRunProgress");
+ assert_eq!(
+ notification,
+ Some(json!({
+ "id": 1,
+ "message": {
+ "type": "started",
+ "test": {
+ "textDocument": {
+ "uri": specifier,
+ },
+ "id": id,
+ },
+ }
+ }))
+ );
+
+ let res = client.read_notification::<Value>();
+ assert!(res.is_ok());
+ let (method, notification) = res.unwrap();
+ assert_eq!(method, "deno/testRunProgress");
+ let notification_value = notification
+ .as_ref()
+ .unwrap()
+ .as_object()
+ .unwrap()
+ .get("message")
+ .unwrap()
+ .as_object()
+ .unwrap()
+ .get("value")
+ .unwrap()
+ .as_str()
+ .unwrap();
+ // deno test's output capturing flushes with a zero-width space in order to
+ // synchronize the output pipes. Occassionally this zero width space
+ // might end up in the output so strip it from the output comparison here.
+ assert_eq!(notification_value.replace('\u{200B}', ""), "test a\r\n");
+ assert_eq!(
+ notification,
+ Some(json!({
+ "id": 1,
+ "message": {
+ "type": "output",
+ "value": notification_value,
+ "test": {
+ "textDocument": {
+ "uri": specifier,
+ },
+ "id": id,
+ },
+ }
+ }))
+ );
+
+ let res = client.read_notification::<Value>();
+ assert!(res.is_ok());
+ let (method, notification) = res.unwrap();
+ assert_eq!(method, "deno/testRunProgress");
+ let notification = notification.unwrap();
+ let obj = notification.as_object().unwrap();
+ assert_eq!(obj.get("id"), Some(&json!(1)));
+ let message = obj.get("message").unwrap().as_object().unwrap();
+ match message.get("type").and_then(|v| v.as_str()) {
+ Some("passed") => {
+ assert_eq!(
+ message.get("test"),
+ Some(&json!({
+ "textDocument": {
+ "uri": specifier
+ },
+ "id": id,
+ }))
+ );
+ assert!(message.contains_key("duration"));
+
+ let res = client.read_notification::<Value>();
+ assert!(res.is_ok());
+ let (method, notification) = res.unwrap();
+ assert_eq!(method, "deno/testRunProgress");
+ assert_eq!(
+ notification,
+ Some(json!({
+ "id": 1,
+ "message": {
+ "type": "end",
+ }
+ }))
+ );
+ }
+ // sometimes on windows, the messages come out of order, but it actually is
+ // working, so if we do get the end before the passed, we will simply let
+ // the test pass
+ Some("end") => (),
+ _ => panic!("unexpected message {}", json!(notification)),
+ }
+
+ shutdown(&mut client);
+}
diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs
index 05c02ea1d..054a4e14a 100644
--- a/cli/tests/integration/mod.rs
+++ b/cli/tests/integration/mod.rs
@@ -29,3 +29,62 @@ macro_rules! itest_flaky(
}
}
);
+
+// These files have `_tests.rs` suffix to make it easier to tell which file is
+// the test (ex. `lint_tests.rs`) and which is the implementation (ex. `lint.rs`)
+// when both are open, especially for two tabs in VS Code
+
+#[path = "bench_tests.rs"]
+mod bench;
+#[path = "bundle_tests.rs"]
+mod bundle;
+#[path = "cache_tests.rs"]
+mod cache;
+#[path = "cert_tests.rs"]
+mod cert;
+#[path = "check_tests.rs"]
+mod check;
+#[path = "compile_tests.rs"]
+mod compile;
+#[path = "coverage_tests.rs"]
+mod coverage;
+#[path = "doc_tests.rs"]
+mod doc;
+#[path = "eval_tests.rs"]
+mod eval;
+#[path = "flags_tests.rs"]
+mod flags;
+#[path = "fmt_tests.rs"]
+mod fmt;
+#[path = "info_tests.rs"]
+mod info;
+#[path = "init_tests.rs"]
+mod init;
+#[path = "inspector_tests.rs"]
+mod inspector;
+#[path = "install_tests.rs"]
+mod install;
+#[path = "js_unit_tests.rs"]
+mod js_unit_tests;
+#[path = "lint_tests.rs"]
+mod lint;
+#[path = "lsp_tests.rs"]
+mod lsp;
+#[path = "npm_tests.rs"]
+mod npm;
+#[path = "repl_tests.rs"]
+mod repl;
+#[path = "run_tests.rs"]
+mod run;
+#[path = "task_tests.rs"]
+mod task;
+#[path = "test_tests.rs"]
+mod test;
+#[path = "upgrade_tests.rs"]
+mod upgrade;
+#[path = "vendor_tests.rs"]
+mod vendor;
+#[path = "watcher_tests.rs"]
+mod watcher;
+#[path = "worker_tests.rs"]
+mod worker;
diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs
new file mode 100644
index 000000000..f2bbbcd3c
--- /dev/null
+++ b/cli/tests/integration/npm_tests.rs
@@ -0,0 +1,1544 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use pretty_assertions::assert_eq;
+use std::process::Stdio;
+use test_util as util;
+use util::assert_contains;
+use util::env_vars_for_npm_tests;
+use util::env_vars_for_npm_tests_no_sync_download;
+use util::http_server;
+
+// NOTE: See how to make test npm packages at ./testdata/npm/README.md
+
+itest!(esm_module {
+ args: "run --allow-read --allow-env npm/esm/main.js",
+ output: "npm/esm/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(esm_module_eval {
+ args_vec: vec![
+ "eval",
+ "import chalk from 'npm:chalk@5'; console.log(chalk.green('chalk esm loads'));",
+ ],
+ output: "npm/esm/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(esm_module_deno_test {
+ args: "test --allow-read --allow-env --unstable npm/esm/test.js",
+ output: "npm/esm/test.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(esm_import_cjs_default {
+ args: "run --allow-read --allow-env --unstable --quiet --check=all npm/esm_import_cjs_default/main.ts",
+ output: "npm/esm_import_cjs_default/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_with_deps {
+ args: "run --allow-read --allow-env npm/cjs_with_deps/main.js",
+ output: "npm/cjs_with_deps/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_sub_path {
+ args: "run --allow-read npm/cjs_sub_path/main.js",
+ output: "npm/cjs_sub_path/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_local_global_decls {
+ args: "run --allow-read npm/cjs_local_global_decls/main.ts",
+ output: "npm/cjs_local_global_decls/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_reexport_collision {
+ args: "run -A --quiet npm/cjs_reexport_collision/main.ts",
+ output: "npm/cjs_reexport_collision/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_this_in_exports {
+ args: "run --allow-read --quiet npm/cjs_this_in_exports/main.js",
+ output: "npm/cjs_this_in_exports/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(translate_cjs_to_esm {
+ args: "run -A --quiet npm/translate_cjs_to_esm/main.js",
+ output: "npm/translate_cjs_to_esm/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(compare_globals {
+ args: "run --allow-read --unstable --check=all npm/compare_globals/main.ts",
+ output: "npm/compare_globals/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(conditional_exports {
+ args: "run --allow-read npm/conditional_exports/main.js",
+ output: "npm/conditional_exports/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(conditional_exports_node_modules_dir {
+ args:
+ "run --allow-read --node-modules-dir $TESTDATA/npm/conditional_exports/main.js",
+ output: "npm/conditional_exports/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ temp_cwd: true,
+ });
+
+itest!(dual_cjs_esm {
+ args: "run -A --quiet npm/dual_cjs_esm/main.ts",
+ output: "npm/dual_cjs_esm/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(child_process_fork_test {
+ args: "run -A --quiet npm/child_process_fork_test/main.ts",
+ output: "npm/child_process_fork_test/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_module_export_assignment {
+ args: "run -A --unstable --quiet --check=all npm/cjs_module_export_assignment/main.ts",
+ output: "npm/cjs_module_export_assignment/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(cjs_module_export_assignment_number {
+ args: "run -A --unstable --quiet --check=all npm/cjs_module_export_assignment_number/main.ts",
+ output: "npm/cjs_module_export_assignment_number/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(mixed_case_package_name_global_dir {
+ args: "run npm/mixed_case_package_name/global.ts",
+ output: "npm/mixed_case_package_name/global.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(mixed_case_package_name_local_dir {
+ args:
+ "run --node-modules-dir -A $TESTDATA/npm/mixed_case_package_name/local.ts",
+ output: "npm/mixed_case_package_name/local.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ temp_cwd: true,
+});
+
+// FIXME(bartlomieju): npm: specifiers are not handled in dynamic imports
+// at the moment
+// itest!(dynamic_import {
+// args: "run --allow-read --allow-env npm/dynamic_import/main.ts",
+// output: "npm/dynamic_import/main.out",
+// envs: env_vars_for_npm_tests(),
+// http_server: true,
+// });
+
+itest!(dynamic_import_reload_same_package {
+ args: "run -A --reload npm/dynamic_import_reload_same_package/main.ts",
+ output: "npm/dynamic_import_reload_same_package/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(env_var_re_export_dev {
+ args: "run --allow-read --allow-env --quiet npm/env_var_re_export/main.js",
+ output_str: Some("dev\n"),
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(env_var_re_export_prod {
+ args: "run --allow-read --allow-env --quiet npm/env_var_re_export/main.js",
+ output_str: Some("prod\n"),
+ envs: {
+ let mut vars = env_vars_for_npm_tests();
+ vars.push(("NODE_ENV".to_string(), "production".to_string()));
+ vars
+ },
+ http_server: true,
+});
+
+itest!(cached_only {
+ args: "run --cached-only npm/cached_only/main.ts",
+ output: "npm/cached_only/main.out",
+ envs: env_vars_for_npm_tests(),
+ exit_code: 1,
+});
+
+itest!(import_map {
+ args: "run --allow-read --allow-env --import-map npm/import_map/import_map.json npm/import_map/main.js",
+ output: "npm/import_map/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ });
+
+itest!(lock_file {
+ args: "run --allow-read --allow-env --lock npm/lock_file/lock.json npm/lock_file/main.js",
+ output: "npm/lock_file/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 10,
+ });
+
+itest!(sub_paths {
+ args: "run -A --quiet npm/sub_paths/main.jsx",
+ output: "npm/sub_paths/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(remote_npm_specifier {
+ args: "run --quiet npm/remote_npm_specifier/main.ts",
+ output: "npm/remote_npm_specifier/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(tarball_with_global_header {
+ args: "run -A --quiet npm/tarball_with_global_header/main.js",
+ output: "npm/tarball_with_global_header/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(nonexistent_file {
+ args: "run -A --quiet npm/nonexistent_file/main.js",
+ output: "npm/nonexistent_file/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(invalid_package_name {
+ args: "run -A --quiet npm/invalid_package_name/main.js",
+ output: "npm/invalid_package_name/main.out",
+ envs: env_vars_for_npm_tests(),
+ exit_code: 1,
+});
+
+itest!(require_json {
+ args: "run -A --quiet npm/require_json/main.js",
+ output: "npm/require_json/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(error_version_after_subpath {
+ args: "run -A --quiet npm/error_version_after_subpath/main.js",
+ output: "npm/error_version_after_subpath/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(deno_cache {
+ args: "cache --reload npm:chalk npm:mkdirp",
+ output: "npm/deno_cache.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(check_all {
+ args: "check --all npm/check_errors/main.ts",
+ output: "npm/check_errors/main_all.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(check_local {
+ args: "check npm/check_errors/main.ts",
+ output: "npm/check_errors/main_local.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(types_general {
+ args: "check --quiet npm/types/main.ts",
+ output: "npm/types/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(types_ambient_module {
+ args: "check --quiet npm/types_ambient_module/main.ts",
+ output: "npm/types_ambient_module/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(types_ambient_module_import_map {
+ args: "check --quiet --import-map=npm/types_ambient_module/import_map.json npm/types_ambient_module/main_import_map.ts",
+ output: "npm/types_ambient_module/main_import_map.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(no_types_cjs {
+ args: "check --quiet npm/no_types_cjs/main.ts",
+ output_str: Some(""),
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(no_types_in_conditional_exports {
+ args: "run --check --unstable npm/no_types_in_conditional_exports/main.ts",
+ output: "npm/no_types_in_conditional_exports/main.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(types_entry_value_not_exists {
+ args: "check --all npm/types_entry_value_not_exists/main.ts",
+ output: "npm/types_entry_value_not_exists/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(types_exports_import_types {
+ args: "check --all npm/types_exports_import_types/main.ts",
+ output: "npm/types_exports_import_types/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(types_no_types_entry {
+ args: "check --all npm/types_no_types_entry/main.ts",
+ output: "npm/types_no_types_entry/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(typescript_file_in_package {
+ args: "run npm/typescript_file_in_package/main.ts",
+ output: "npm/typescript_file_in_package/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(permissions_outside_package {
+ args: "run --allow-read npm/permissions_outside_package/main.ts",
+ output: "npm/permissions_outside_package/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+#[test]
+fn parallel_downloading() {
+ let (out, _err) = util::run_and_collect_output_with_args(
+ true,
+ vec![
+ "run",
+ "--allow-read",
+ "--allow-env",
+ "npm/cjs_with_deps/main.js",
+ ],
+ None,
+ // don't use the sync env var
+ Some(env_vars_for_npm_tests_no_sync_download()),
+ true,
+ );
+ assert!(out.contains("chalk cjs loads"));
+}
+
+#[test]
+fn cached_only_after_first_run() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("npm/cached_only_after_first_run/main1.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(stderr, "Download");
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--cached-only")
+ .arg("npm/cached_only_after_first_run/main2.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(
+ stderr,
+ "An npm specifier not found in cache: \"ansi-styles\", --cached-only is specified."
+ );
+ assert!(stdout.is_empty());
+ assert!(!output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--cached-only")
+ .arg("npm/cached_only_after_first_run/main1.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert!(output.status.success());
+ assert!(stderr.is_empty());
+ assert_contains!(stdout, "createChalk: chalk");
+}
+
+#[test]
+fn reload_flag() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("npm/reload/main.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(stderr, "Download");
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--reload")
+ .arg("npm/reload/main.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(stderr, "Download");
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--reload=npm:")
+ .arg("npm/reload/main.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(stderr, "Download");
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--reload=npm:chalk")
+ .arg("npm/reload/main.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(stderr, "Download");
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--reload=npm:foobar")
+ .arg("npm/reload/main.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert!(stderr.is_empty());
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+}
+
+#[test]
+fn no_npm_after_first_run() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--no-npm")
+ .arg("npm/no_npm_after_first_run/main1.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(
+ stderr,
+ "Following npm specifiers were requested: \"chalk@5\"; but --no-npm is specified."
+ );
+ assert!(stdout.is_empty());
+ assert!(!output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("npm/no_npm_after_first_run/main1.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(stderr, "Download");
+ assert_contains!(stdout, "createChalk: chalk");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--no-npm")
+ .arg("npm/no_npm_after_first_run/main1.ts")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ assert_contains!(
+ stderr,
+ "Following npm specifiers were requested: \"chalk@5\"; but --no-npm is specified."
+ );
+ assert!(stdout.is_empty());
+ assert!(!output.status.success());
+}
+
+#[test]
+fn deno_run_cjs_module() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(deno_dir.path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--allow-write")
+ .arg("npm:mkdirp@1.0.4")
+ .arg("test_dir")
+ .env("NO_COLOR", "1")
+ .envs(env_vars_for_npm_tests())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ assert!(deno_dir.path().join("test_dir").exists());
+}
+
+itest!(deno_run_cowsay {
+ args: "run -A --quiet npm:cowsay@1.5.0 Hello",
+ output: "npm/deno_run_cowsay.out",
+ envs: env_vars_for_npm_tests_no_sync_download(),
+ http_server: true,
+});
+
+itest!(deno_run_cowsay_explicit {
+ args: "run -A --quiet npm:cowsay@1.5.0/cowsay Hello",
+ output: "npm/deno_run_cowsay.out",
+ envs: env_vars_for_npm_tests_no_sync_download(),
+ http_server: true,
+});
+
+itest!(deno_run_cowthink {
+ args: "run -A --quiet npm:cowsay@1.5.0/cowthink Hello",
+ output: "npm/deno_run_cowthink.out",
+ envs: env_vars_for_npm_tests_no_sync_download(),
+ http_server: true,
+});
+
+itest!(deno_run_bin_esm {
+ args: "run -A --quiet npm:@denotest/bin/cli-esm this is a test",
+ output: "npm/deno_run_esm.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(deno_run_bin_no_ext {
+ args: "run -A --quiet npm:@denotest/bin/cli-no-ext this is a test",
+ output: "npm/deno_run_no_ext.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(deno_run_bin_cjs {
+ args: "run -A --quiet npm:@denotest/bin/cli-cjs this is a test",
+ output: "npm/deno_run_cjs.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(deno_run_non_existent {
+ args: "run npm:mkdirp@0.5.125",
+ output: "npm/deno_run_non_existent.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(builtin_module_module {
+ args: "run --allow-read --quiet npm/builtin_module_module/main.js",
+ output: "npm/builtin_module_module/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(node_modules_dir_require_added_node_modules_folder {
+ args:
+ "run --node-modules-dir -A --quiet $TESTDATA/npm/require_added_nm_folder/main.js",
+ output: "npm/require_added_nm_folder/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ exit_code: 0,
+ temp_cwd: true,
+});
+
+itest!(node_modules_dir_with_deps {
+ args: "run --allow-read --allow-env --node-modules-dir $TESTDATA/npm/cjs_with_deps/main.js",
+ output: "npm/cjs_with_deps/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ temp_cwd: true,
+});
+
+itest!(node_modules_dir_yargs {
+ args: "run --allow-read --allow-env --node-modules-dir $TESTDATA/npm/cjs_yargs/main.js",
+ output: "npm/cjs_yargs/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ temp_cwd: true,
+});
+
+#[test]
+fn node_modules_dir_cache() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(deno_dir.path())
+ .arg("cache")
+ .arg("--node-modules-dir")
+ .arg("--quiet")
+ .arg(util::testdata_path().join("npm/dual_cjs_esm/main.ts"))
+ .envs(env_vars_for_npm_tests())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ let node_modules = deno_dir.path().join("node_modules");
+ assert!(node_modules
+ .join(
+ ".deno/@denotest+dual-cjs-esm@1.0.0/node_modules/@denotest/dual-cjs-esm"
+ )
+ .exists());
+ assert!(node_modules.join("@denotest/dual-cjs-esm").exists());
+
+ // now try deleting the folder with the package source in the npm cache dir
+ let package_global_cache_dir = deno_dir
+ .path()
+ .join("npm")
+ .join("localhost_4545")
+ .join("npm")
+ .join("registry")
+ .join("@denotest")
+ .join("dual-cjs-esm")
+ .join("1.0.0");
+ assert!(package_global_cache_dir.exists());
+ std::fs::remove_dir_all(&package_global_cache_dir).unwrap();
+
+ // run the output, and it shouldn't bother recreating the directory
+ // because it already has everything cached locally in the node_modules folder
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(deno_dir.path())
+ .arg("run")
+ .arg("--node-modules-dir")
+ .arg("--quiet")
+ .arg("-A")
+ .arg(util::testdata_path().join("npm/dual_cjs_esm/main.ts"))
+ .envs(env_vars_for_npm_tests())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ // this won't exist, but actually the parent directory
+ // will because it still re-downloads the registry information
+ assert!(!package_global_cache_dir.exists());
+}
+
+#[test]
+fn ensure_registry_files_local() {
+ // ensures the registry files all point at local tarballs
+ let registry_dir_path = util::testdata_path().join("npm").join("registry");
+ for entry in std::fs::read_dir(&registry_dir_path).unwrap() {
+ let entry = entry.unwrap();
+ if entry.metadata().unwrap().is_dir() {
+ let registry_json_path = registry_dir_path
+ .join(entry.file_name())
+ .join("registry.json");
+ if registry_json_path.exists() {
+ let file_text = std::fs::read_to_string(&registry_json_path).unwrap();
+ if file_text.contains("https://registry.npmjs.org/") {
+ panic!(
+ "file {} contained a reference to the npm registry",
+ registry_json_path.display(),
+ );
+ }
+ }
+ }
+ }
+}
+
+itest!(compile_errors {
+ args: "compile -A --quiet npm/cached_only/main.ts",
+ output_str: Some("error: npm specifiers have not yet been implemented for this sub command (https://github.com/denoland/deno/issues/15960). Found: npm:chalk@5\n"),
+ exit_code: 1,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ });
+
+itest!(bundle_errors {
+ args: "bundle --quiet npm/esm/main.js",
+ output_str: Some("error: npm specifiers have not yet been implemented for this sub command (https://github.com/denoland/deno/issues/15960). Found: npm:chalk@5\n"),
+ exit_code: 1,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ });
+
+itest!(info_chalk_display {
+ args: "info --quiet npm/cjs_with_deps/main.js",
+ output: "npm/cjs_with_deps/main_info.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(info_chalk_display_node_modules_dir {
+ args: "info --quiet --node-modules-dir $TESTDATA/npm/cjs_with_deps/main.js",
+ output: "npm/cjs_with_deps/main_info.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ temp_cwd: true,
+});
+
+itest!(info_chalk_json {
+ args: "info --quiet --json npm/cjs_with_deps/main.js",
+ output: "npm/cjs_with_deps/main_info_json.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(info_chalk_json_node_modules_dir {
+ args:
+ "info --quiet --node-modules-dir --json $TESTDATA/npm/cjs_with_deps/main.js",
+ output: "npm/cjs_with_deps/main_info_json.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+ temp_cwd: true,
+});
+
+itest!(info_cli_chalk_display {
+ args: "info --quiet npm:chalk@4",
+ output: "npm/info/chalk.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(info_cli_chalk_json {
+ args: "info --quiet --json npm:chalk@4",
+ output: "npm/info/chalk_json.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+#[test]
+fn lock_file_missing_top_level_package() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+ let temp_dir = util::TempDir::new();
+
+ // write empty config file
+ temp_dir.write("deno.json", "{}");
+
+ // Lock file that is automatically picked up has been intentionally broken,
+ // by removing "cowsay" package from it. This test ensures that npm resolver
+ // snapshot can be successfully hydrated in such situation
+ let lock_file_content = r#"{
+ "version": "2",
+ "remote": {},
+ "npm": {
+ "specifiers": { "cowsay": "cowsay@1.5.0" },
+ "packages": {
+ "ansi-regex@3.0.1": {
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dependencies": {}
+ },
+ "ansi-regex@5.0.1": {
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dependencies": {}
+ },
+ "ansi-styles@4.3.0": {
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": { "color-convert": "color-convert@2.0.1" }
+ },
+ "camelcase@5.3.1": {
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dependencies": {}
+ },
+ "cliui@6.0.0": {
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dependencies": {
+ "string-width": "string-width@4.2.3",
+ "strip-ansi": "strip-ansi@6.0.1",
+ "wrap-ansi": "wrap-ansi@6.2.0"
+ }
+ },
+ "color-convert@2.0.1": {
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": { "color-name": "color-name@1.1.4" }
+ },
+ "color-name@1.1.4": {
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dependencies": {}
+ },
+ "decamelize@1.2.0": {
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dependencies": {}
+ },
+ "emoji-regex@8.0.0": {
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dependencies": {}
+ },
+ "find-up@4.1.0": {
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dependencies": {
+ "locate-path": "locate-path@5.0.0",
+ "path-exists": "path-exists@4.0.0"
+ }
+ },
+ "get-caller-file@2.0.5": {
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dependencies": {}
+ },
+ "get-stdin@8.0.0": {
+ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
+ "dependencies": {}
+ },
+ "is-fullwidth-code-point@2.0.0": {
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dependencies": {}
+ },
+ "is-fullwidth-code-point@3.0.0": {
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dependencies": {}
+ },
+ "locate-path@5.0.0": {
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dependencies": { "p-locate": "p-locate@4.1.0" }
+ },
+ "p-limit@2.3.0": {
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dependencies": { "p-try": "p-try@2.2.0" }
+ },
+ "p-locate@4.1.0": {
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dependencies": { "p-limit": "p-limit@2.3.0" }
+ },
+ "p-try@2.2.0": {
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dependencies": {}
+ },
+ "path-exists@4.0.0": {
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dependencies": {}
+ },
+ "require-directory@2.1.1": {
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dependencies": {}
+ },
+ "require-main-filename@2.0.0": {
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dependencies": {}
+ },
+ "set-blocking@2.0.0": {
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dependencies": {}
+ },
+ "string-width@2.1.1": {
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dependencies": {
+ "is-fullwidth-code-point": "is-fullwidth-code-point@2.0.0",
+ "strip-ansi": "strip-ansi@4.0.0"
+ }
+ },
+ "string-width@4.2.3": {
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "emoji-regex@8.0.0",
+ "is-fullwidth-code-point": "is-fullwidth-code-point@3.0.0",
+ "strip-ansi": "strip-ansi@6.0.1"
+ }
+ },
+ "strip-ansi@4.0.0": {
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dependencies": { "ansi-regex": "ansi-regex@3.0.1" }
+ },
+ "strip-ansi@6.0.1": {
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": { "ansi-regex": "ansi-regex@5.0.1" }
+ },
+ "strip-final-newline@2.0.0": {
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dependencies": {}
+ },
+ "which-module@2.0.0": {
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dependencies": {}
+ },
+ "wrap-ansi@6.2.0": {
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dependencies": {
+ "ansi-styles": "ansi-styles@4.3.0",
+ "string-width": "string-width@4.2.3",
+ "strip-ansi": "strip-ansi@6.0.1"
+ }
+ },
+ "y18n@4.0.3": {
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dependencies": {}
+ },
+ "yargs-parser@18.1.3": {
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dependencies": {
+ "camelcase": "camelcase@5.3.1",
+ "decamelize": "decamelize@1.2.0"
+ }
+ },
+ "yargs@15.4.1": {
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dependencies": {
+ "cliui": "cliui@6.0.0",
+ "decamelize": "decamelize@1.2.0",
+ "find-up": "find-up@4.1.0",
+ "get-caller-file": "get-caller-file@2.0.5",
+ "require-directory": "require-directory@2.1.1",
+ "require-main-filename": "require-main-filename@2.0.0",
+ "set-blocking": "set-blocking@2.0.0",
+ "string-width": "string-width@4.2.3",
+ "which-module": "which-module@2.0.0",
+ "y18n": "y18n@4.0.3",
+ "yargs-parser": "yargs-parser@18.1.3"
+ }
+ }
+ }
+ }
+ }
+ "#;
+ temp_dir.write("deno.lock", lock_file_content);
+ let main_contents = r#"
+ import cowsay from "npm:cowsay";
+ console.log(cowsay);
+ "#;
+ temp_dir.write("main.ts", main_contents);
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--quiet")
+ .arg("--lock")
+ .arg("deno.lock")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(!output.status.success());
+
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert_eq!(
+ stderr,
+ "error: failed reading lockfile 'deno.lock'\n\nCaused by:\n the lockfile is corrupt. You can recreate it with --lock-write\n"
+ );
+}
+
+#[test]
+fn lock_file_lock_write() {
+ // https://github.com/denoland/deno/issues/16666
+ // Ensure that --lock-write still adds npm packages to the lockfile
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+ let temp_dir = util::TempDir::new();
+
+ // write empty config file
+ temp_dir.write("deno.json", "{}");
+
+ // write a lock file with borked integrity
+ let lock_file_content = r#"{
+ "version": "2",
+ "remote": {},
+ "npm": {
+ "specifiers": { "cowsay@1.5.0": "cowsay@1.5.0" },
+ "packages": {
+ "ansi-regex@3.0.1": {
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dependencies": {}
+ },
+ "ansi-regex@5.0.1": {
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dependencies": {}
+ },
+ "ansi-styles@4.3.0": {
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": { "color-convert": "color-convert@2.0.1" }
+ },
+ "camelcase@5.3.1": {
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dependencies": {}
+ },
+ "cliui@6.0.0": {
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dependencies": {
+ "string-width": "string-width@4.2.3",
+ "strip-ansi": "strip-ansi@6.0.1",
+ "wrap-ansi": "wrap-ansi@6.2.0"
+ }
+ },
+ "color-convert@2.0.1": {
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": { "color-name": "color-name@1.1.4" }
+ },
+ "color-name@1.1.4": {
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dependencies": {}
+ },
+ "cowsay@1.5.0": {
+ "integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
+ "dependencies": {
+ "get-stdin": "get-stdin@8.0.0",
+ "string-width": "string-width@2.1.1",
+ "strip-final-newline": "strip-final-newline@2.0.0",
+ "yargs": "yargs@15.4.1"
+ }
+ },
+ "decamelize@1.2.0": {
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dependencies": {}
+ },
+ "emoji-regex@8.0.0": {
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dependencies": {}
+ },
+ "find-up@4.1.0": {
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dependencies": {
+ "locate-path": "locate-path@5.0.0",
+ "path-exists": "path-exists@4.0.0"
+ }
+ },
+ "get-caller-file@2.0.5": {
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dependencies": {}
+ },
+ "get-stdin@8.0.0": {
+ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
+ "dependencies": {}
+ },
+ "is-fullwidth-code-point@2.0.0": {
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dependencies": {}
+ },
+ "is-fullwidth-code-point@3.0.0": {
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dependencies": {}
+ },
+ "locate-path@5.0.0": {
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dependencies": { "p-locate": "p-locate@4.1.0" }
+ },
+ "p-limit@2.3.0": {
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dependencies": { "p-try": "p-try@2.2.0" }
+ },
+ "p-locate@4.1.0": {
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dependencies": { "p-limit": "p-limit@2.3.0" }
+ },
+ "p-try@2.2.0": {
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dependencies": {}
+ },
+ "path-exists@4.0.0": {
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dependencies": {}
+ },
+ "require-directory@2.1.1": {
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dependencies": {}
+ },
+ "require-main-filename@2.0.0": {
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dependencies": {}
+ },
+ "set-blocking@2.0.0": {
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dependencies": {}
+ },
+ "string-width@2.1.1": {
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dependencies": {
+ "is-fullwidth-code-point": "is-fullwidth-code-point@2.0.0",
+ "strip-ansi": "strip-ansi@4.0.0"
+ }
+ },
+ "string-width@4.2.3": {
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "emoji-regex@8.0.0",
+ "is-fullwidth-code-point": "is-fullwidth-code-point@3.0.0",
+ "strip-ansi": "strip-ansi@6.0.1"
+ }
+ },
+ "strip-ansi@4.0.0": {
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dependencies": { "ansi-regex": "ansi-regex@3.0.1" }
+ },
+ "strip-ansi@6.0.1": {
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": { "ansi-regex": "ansi-regex@5.0.1" }
+ },
+ "strip-final-newline@2.0.0": {
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dependencies": {}
+ },
+ "which-module@2.0.0": {
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dependencies": {}
+ },
+ "wrap-ansi@6.2.0": {
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dependencies": {
+ "ansi-styles": "ansi-styles@4.3.0",
+ "string-width": "string-width@4.2.3",
+ "strip-ansi": "strip-ansi@6.0.1"
+ }
+ },
+ "y18n@4.0.3": {
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dependencies": {}
+ },
+ "yargs-parser@18.1.3": {
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dependencies": {
+ "camelcase": "camelcase@5.3.1",
+ "decamelize": "decamelize@1.2.0"
+ }
+ },
+ "yargs@15.4.1": {
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dependencies": {
+ "cliui": "cliui@6.0.0",
+ "decamelize": "decamelize@1.2.0",
+ "find-up": "find-up@4.1.0",
+ "get-caller-file": "get-caller-file@2.0.5",
+ "require-directory": "require-directory@2.1.1",
+ "require-main-filename": "require-main-filename@2.0.0",
+ "set-blocking": "set-blocking@2.0.0",
+ "string-width": "string-width@4.2.3",
+ "which-module": "which-module@2.0.0",
+ "y18n": "y18n@4.0.3",
+ "yargs-parser": "yargs-parser@18.1.3"
+ }
+ }
+ }
+ }
+}
+"#;
+ temp_dir.write("deno.lock", lock_file_content);
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("cache")
+ .arg("--lock-write")
+ .arg("--quiet")
+ .arg("npm:cowsay@1.5.0")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.status.code(), Some(0));
+
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ assert!(stdout.is_empty());
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert!(stderr.is_empty());
+ assert_eq!(
+ lock_file_content,
+ std::fs::read_to_string(temp_dir.path().join("deno.lock")).unwrap()
+ );
+}
+
+#[test]
+fn auto_discover_lock_file() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+ let temp_dir = util::TempDir::new();
+
+ // write empty config file
+ temp_dir.write("deno.json", "{}");
+
+ // write a lock file with borked integrity
+ let lock_file_content = r#"{
+ "version": "2",
+ "remote": {},
+ "npm": {
+ "specifiers": { "@denotest/bin": "@denotest/bin@1.0.0" },
+ "packages": {
+ "@denotest/bin@1.0.0": {
+ "integrity": "sha512-foobar",
+ "dependencies": {}
+ }
+ }
+ }
+ }"#;
+ temp_dir.write("deno.lock", lock_file_content);
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("-A")
+ .arg("npm:@denotest/bin/cli-esm")
+ .arg("test")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(!output.status.success());
+ assert_eq!(output.status.code(), Some(10));
+
+ let stderr = String::from_utf8(output.stderr).unwrap();
+ assert!(stderr.contains(
+ "Integrity check failed for npm package: \"@denotest/bin@1.0.0\""
+ ));
+}
+
+#[test]
+fn peer_deps_with_copied_folders_and_lockfile() {
+ let _server = http_server();
+
+ let deno_dir = util::new_deno_dir();
+ let temp_dir = util::TempDir::new();
+
+ // write empty config file
+ temp_dir.write("deno.json", "{}");
+ let test_folder_path = test_util::testdata_path()
+ .join("npm")
+ .join("peer_deps_with_copied_folders");
+ let main_contents =
+ std::fs::read_to_string(test_folder_path.join("main.ts")).unwrap();
+ temp_dir.write("./main.ts", main_contents);
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ let expected_output =
+ std::fs::read_to_string(test_folder_path.join("main.out")).unwrap();
+
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output);
+
+ assert!(temp_dir.path().join("deno.lock").exists());
+ let grandchild_path = deno_dir
+ .path()
+ .join("npm")
+ .join("localhost_4545")
+ .join("npm")
+ .join("registry")
+ .join("@denotest")
+ .join("peer-dep-test-grandchild");
+ assert!(grandchild_path.join("1.0.0").exists());
+ assert!(grandchild_path.join("1.0.0_1").exists()); // copy folder, which is hardlinked
+
+ // run again
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), "1\n2\n");
+ assert!(output.status.success());
+
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--reload")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output);
+ assert!(output.status.success());
+
+ // now run with local node modules
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--node-modules-dir")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), "1\n2\n");
+ assert!(output.status.success());
+
+ let deno_folder = temp_dir.path().join("node_modules").join(".deno");
+ assert!(deno_folder
+ .join("@denotest+peer-dep-test-grandchild@1.0.0")
+ .exists());
+ assert!(deno_folder
+ .join("@denotest+peer-dep-test-grandchild@1.0.0_1")
+ .exists()); // copy folder
+
+ // now again run with local node modules
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--node-modules-dir")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), "1\n2\n");
+
+ // now ensure it works with reloading
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--node-modules-dir")
+ .arg("--reload")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert!(output.status.success());
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output);
+
+ // now ensure it works with reloading and no lockfile
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(temp_dir.path())
+ .arg("run")
+ .arg("--node-modules-dir")
+ .arg("--no-lock")
+ .arg("--reload")
+ .arg("-A")
+ .arg("main.ts")
+ .envs(env_vars_for_npm_tests())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output,);
+ assert!(output.status.success());
+}
+
+itest!(info_peer_deps {
+ args: "info --quiet npm/peer_deps_with_copied_folders/main.ts",
+ output: "npm/peer_deps_with_copied_folders/main_info.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(info_peer_deps_json {
+ args: "info --quiet --json npm/peer_deps_with_copied_folders/main.ts",
+ output: "npm/peer_deps_with_copied_folders/main_info_json.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(create_require {
+ args: "run --reload --allow-read npm/create_require/main.ts",
+ output: "npm/create_require/main.out",
+ exit_code: 0,
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs
new file mode 100644
index 000000000..df31ee98b
--- /dev/null
+++ b/cli/tests/integration/repl_tests.rs
@@ -0,0 +1,959 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use test_util as util;
+use test_util::assert_contains;
+use test_util::assert_ends_with;
+use test_util::assert_not_contains;
+
+#[test]
+fn pty_multiline() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("(\n1 + 2\n)");
+ console.write_line("{\nfoo: \"foo\"\n}");
+ console.write_line("`\nfoo\n`");
+ console.write_line("`\n\\`\n`");
+ console.write_line("'{'");
+ console.write_line("'('");
+ console.write_line("'['");
+ console.write_line("/{/");
+ console.write_line("/\\(/");
+ console.write_line("/\\[/");
+ console.write_line("console.log(\"{test1} abc {test2} def {{test3}}\".match(/{([^{].+?)}/));");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, '3');
+ assert_contains!(output, "{ foo: \"foo\" }");
+ assert_contains!(output, "\"\\nfoo\\n\"");
+ assert_contains!(output, "\"\\n`\\n\"");
+ assert_contains!(output, "\"{\"");
+ assert_contains!(output, "\"(\"");
+ assert_contains!(output, "\"[\"");
+ assert_contains!(output, "/{/");
+ assert_contains!(output, "/\\(/");
+ assert_contains!(output, "/\\[/");
+ assert_contains!(output, "[ \"{test1}\", \"test1\" ]");
+ });
+}
+
+#[test]
+fn pty_null() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("null");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "null");
+ });
+}
+
+#[test]
+fn pty_unpaired_braces() {
+ for right_brace in &[")", "]", "}"] {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line(right_brace);
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "Expression expected");
+ });
+ }
+}
+
+#[test]
+fn pty_bad_input() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("'\\u{1f3b5}'[0]");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "Unterminated string literal");
+ });
+}
+
+#[test]
+fn pty_syntax_error_input() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("('\\u')");
+ console.write_line("'");
+ console.write_line("[{'a'}];");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(
+ output,
+ "Bad character escape sequence, expected 4 hex characters"
+ );
+ assert_contains!(output, "Unterminated string constant");
+ assert_contains!(output, "Expected a semicolon");
+ });
+}
+
+#[test]
+fn pty_complete_symbol() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("Symbol.it\t");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "Symbol(Symbol.iterator)");
+ });
+}
+
+#[test]
+fn pty_complete_declarations() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("class MyClass {}");
+ console.write_line("My\t");
+ console.write_line("let myVar;");
+ console.write_line("myV\t");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "> MyClass");
+ assert_contains!(output, "> myVar");
+ });
+}
+
+#[test]
+fn pty_complete_primitives() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("let func = function test(){}");
+ console.write_line("func.appl\t");
+ console.write_line("let str = ''");
+ console.write_line("str.leng\t");
+ console.write_line("false.valueO\t");
+ console.write_line("5n.valueO\t");
+ console.write_line("let num = 5");
+ console.write_line("num.toStrin\t");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "> func.apply");
+ assert_contains!(output, "> str.length");
+ assert_contains!(output, "> 5n.valueOf");
+ assert_contains!(output, "> false.valueOf");
+ assert_contains!(output, "> num.toString");
+ });
+}
+
+#[test]
+fn pty_complete_expression() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_text("Deno.\t\t");
+ console.write_text("y");
+ console.write_line("");
+ console.write_line("close();");
+ let output = console.read_all_output();
+ assert_contains!(output, "Display all");
+ assert_contains!(output, "core");
+ assert_contains!(output, "args");
+ assert_contains!(output, "exit");
+ assert_contains!(output, "symlink");
+ assert_contains!(output, "permissions");
+ });
+}
+
+#[test]
+fn pty_complete_imports() {
+ util::with_pty(&["repl", "-A"], |mut console| {
+ // single quotes
+ console.write_line("import './run/001_hel\t'");
+ // double quotes
+ console.write_line("import { output } from \"./run/045_out\t\"");
+ console.write_line("output('testing output');");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "Hello World");
+ assert_contains!(
+ output,
+ // on windows, could any (it's flaky)
+ "\ntesting output",
+ "testing output\u{1b}",
+ "\r\n\u{1b}[?25htesting output",
+ );
+ });
+
+ // ensure when the directory changes that the suggestions come from the cwd
+ util::with_pty(&["repl", "-A"], |mut console| {
+ console.write_line("Deno.chdir('./subdir');");
+ console.write_line("import '../run/001_hel\t'");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "Hello World");
+ });
+}
+
+#[test]
+fn pty_complete_imports_no_panic_empty_specifier() {
+ // does not panic when tabbing when empty
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("import '\t';");
+ console.write_line("close();");
+ });
+}
+
+#[test]
+fn pty_ignore_symbols() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("Array.Symbol\t");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_contains!(output, "undefined");
+ assert_not_contains!(
+ output,
+ "Uncaught TypeError: Array.Symbol is not a function"
+ );
+ });
+}
+
+#[test]
+fn pty_assign_global_this() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("globalThis = 42;");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ assert_not_contains!(output, "panicked");
+ });
+}
+
+#[test]
+fn pty_emoji() {
+ // windows was having issues displaying this
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line(r#"console.log('\u{1F995}');"#);
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ // only one for the output (since input is escaped)
+ let emoji_count = output.chars().filter(|c| *c == '🦕').count();
+ assert_eq!(emoji_count, 1);
+ });
+}
+
+#[test]
+fn console_log() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["console.log('hello')", "'world'"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "hello\nundefined\n\"world\"\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn object_literal() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["{}", "{ foo: 'bar' }"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "{}\n{ foo: \"bar\" }\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn block_expression() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["{};", "{\"\"}"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "undefined\n\"\"\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn await_resolve() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["await Promise.resolve('done')"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "\"done\"\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn await_timeout() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["await new Promise((r) => setTimeout(r, 0, 'done'))"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "\"done\"\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn let_redeclaration() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["let foo = 0;", "foo", "let foo = 1;", "foo"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "undefined\n0\nundefined\n1\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn repl_cwd() {
+ let (_out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["Deno.cwd()"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert!(err.is_empty());
+}
+
+#[test]
+fn typescript() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ "function add(a: number, b: number) { return a + b }",
+ "const result: number = add(1, 2) as number;",
+ "result",
+ ]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "undefined\nundefined\n3\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn typescript_declarations() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ "namespace Test { export enum Values { A, B, C } }",
+ "Test.Values.A",
+ "Test.Values.C",
+ "interface MyInterface { prop: string; }",
+ "type MyTypeAlias = string;",
+ ]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ let expected_end_text = "undefined\n0\n2\nundefined\nundefined\n";
+ assert_ends_with!(out, expected_end_text);
+ assert!(err.is_empty());
+}
+
+#[test]
+fn typescript_decorators() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ "function dec(target) { target.prototype.test = () => 2; }",
+ "@dec class Test {}",
+ "new Test().test()",
+ ]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "undefined\n[Function: Test]\n2\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eof() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["1 + 2"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "3\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn strict() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ "let a = {};",
+ "Object.preventExtensions(a);",
+ "a.c = 1;",
+ ]),
+ None,
+ false,
+ );
+ assert_contains!(
+ out,
+ "Uncaught TypeError: Cannot add property c, object is not extensible"
+ );
+ assert!(err.is_empty());
+}
+
+#[test]
+fn close_command() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["close()", "'ignored'"]),
+ None,
+ false,
+ );
+
+ assert_not_contains!(out, "ignored");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn function() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["Deno.writeFileSync"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "[Function: writeFileSync]\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+#[ignore]
+fn multiline() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["(\n1 + 2\n)"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "3\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn import() {
+ let (out, _) = util::run_and_collect_output_with_args(
+ true,
+ vec![],
+ Some(vec!["import('./subdir/auto_print_hello.ts')"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_contains!(out, "hello!\n");
+}
+
+#[test]
+fn import_declarations() {
+ let (out, _) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--allow-read"],
+ Some(vec!["import './subdir/auto_print_hello.ts';"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_contains!(out, "hello!\n");
+}
+
+#[test]
+fn exports_stripped() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["export default 5;", "export class Test {}"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_contains!(out, "5\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn call_eval_unterminated() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["eval('{')"]),
+ None,
+ false,
+ );
+ assert_contains!(out, "Unexpected end of input");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn unpaired_braces() {
+ for right_brace in &[")", "]", "}"] {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![right_brace]),
+ None,
+ false,
+ );
+ assert_contains!(out, "Expression expected");
+ assert!(err.is_empty());
+ }
+}
+
+#[test]
+fn reference_error() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["not_a_variable"]),
+ None,
+ false,
+ );
+ assert_contains!(out, "not_a_variable is not defined");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn syntax_error() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ "syntax error",
+ "2", // ensure it keeps accepting input after
+ ]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "parse error: Expected ';', '}' or <eof> at 1:8\n2\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn syntax_error_jsx() {
+ // JSX is not supported in the REPL
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["const element = <div />;"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_contains!(out, "Expression expected");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn type_error() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["console()"]),
+ None,
+ false,
+ );
+ assert_contains!(out, "console is not a function");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn variable() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["var a = 123;", "a"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "undefined\n123\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn lexical_scoped_variable() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["let a = 123;", "a"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "undefined\n123\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn missing_deno_dir() {
+ use std::fs::{read_dir, remove_dir_all};
+ const DENO_DIR: &str = "nonexistent";
+ let test_deno_dir = test_util::testdata_path().join(DENO_DIR);
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["1"]),
+ Some(vec![
+ ("DENO_DIR".to_owned(), DENO_DIR.to_owned()),
+ ("NO_COLOR".to_owned(), "1".to_owned()),
+ ]),
+ false,
+ );
+ assert!(read_dir(&test_deno_dir).is_ok());
+ remove_dir_all(&test_deno_dir).unwrap();
+ assert_ends_with!(out, "1\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn save_last_eval() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["1", "_"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "1\n1\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn save_last_thrown() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["throw 1", "_error"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(out, "Uncaught 1\n1\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn assign_underscore() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["_ = 1", "2", "_"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert_ends_with!(
+ out,
+ "Last evaluation result is no longer saved to _.\n1\n2\n1\n"
+ );
+ assert!(err.is_empty());
+}
+
+#[test]
+fn assign_underscore_error() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["_error = 1", "throw 2", "_error"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ println!("{}", out);
+ assert_ends_with!(
+ out,
+ "Last thrown error is no longer saved to _error.\n1\nUncaught 2\n1\n"
+ );
+ assert!(err.is_empty());
+}
+
+#[test]
+fn custom_inspect() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ r#"const o = {
+ [Symbol.for("Deno.customInspect")]() {
+ throw new Error('Oops custom inspect error');
+ },
+ };"#,
+ "o",
+ ]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+
+ assert_contains!(out, "Oops custom inspect error");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eval_flag_valid_input() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--eval", "const t = 10;"],
+ Some(vec!["t * 500;"]),
+ None,
+ false,
+ );
+ assert_contains!(out, "5000");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eval_flag_parse_error() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--eval", "const %"],
+ Some(vec!["250 * 10"]),
+ None,
+ false,
+ );
+ assert_contains!(
+ test_util::strip_ansi_codes(&out),
+ "Error in --eval flag: parse error: Unexpected token `%`."
+ );
+ assert_contains!(out, "2500"); // should not prevent input
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eval_flag_runtime_error() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--eval", "throw new Error('Testing')"],
+ Some(vec!["250 * 10"]),
+ None,
+ false,
+ );
+ assert_contains!(out, "Error in --eval flag: Uncaught Error: Testing");
+ assert_contains!(out, "2500"); // should not prevent input
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eval_file_flag_valid_input() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--eval-file=./run/001_hello.js"],
+ None,
+ None,
+ false,
+ );
+ assert_contains!(out, "Hello World");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eval_file_flag_call_defined_function() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--eval-file=./tsc/d.ts"],
+ Some(vec!["v4()"]),
+ None,
+ false,
+ );
+ assert_contains!(out, "hello");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn eval_file_flag_http_input() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--eval-file=http://127.0.0.1:4545/tsc/d.ts"],
+ Some(vec!["v4()"]),
+ None,
+ true,
+ );
+ assert_contains!(out, "hello");
+ assert!(err.contains("Download"));
+}
+
+#[test]
+fn eval_file_flag_multiple_files() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--allow-read", "--eval-file=http://127.0.0.1:4545/repl/import_type.ts,./tsc/d.ts,http://127.0.0.1:4545/type_definitions/foo.js"],
+ Some(vec!["b.method1=v4", "b.method1()+foo.toUpperCase()"]),
+ None,
+ true,
+ );
+ assert_contains!(out, "helloFOO");
+ assert_contains!(err, "Download");
+}
+
+#[test]
+fn pty_clear_function() {
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("console.log('hello');");
+ console.write_line("clear();");
+ console.write_line("const clear = 1234 + 2000;");
+ console.write_line("clear;");
+ console.write_line("close();");
+
+ let output = console.read_all_output();
+ if cfg!(windows) {
+ // Windows will overwrite what's in the console buffer before
+ // we read from it. It contains this string repeated many times
+ // to clear the screen.
+ assert_contains!(output, "\r\n\u{1b}[K\r\n\u{1b}[K\r\n\u{1b}[K");
+ } else {
+ assert_contains!(output, "hello");
+ assert_contains!(output, "[1;1H");
+ }
+ assert_contains!(output, "undefined");
+ assert_contains!(output, "const clear = 1234 + 2000;");
+ assert_contains!(output, "3234");
+ });
+}
+
+#[test]
+fn pty_tab_handler() {
+ // If the last character is **not** whitespace, we show the completions
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("a\t\t");
+ console.write_line("close();");
+ let output = console.read_all_output();
+ assert_contains!(output, "addEventListener");
+ assert_contains!(output, "alert");
+ assert_contains!(output, "atob");
+ });
+ // If the last character is whitespace, we just insert a tab
+ util::with_pty(&["repl"], |mut console| {
+ console.write_line("a; \t\t"); // last character is whitespace
+ console.write_line("close();");
+ let output = console.read_all_output();
+ assert_not_contains!(output, "addEventListener");
+ assert_not_contains!(output, "alert");
+ assert_not_contains!(output, "atob");
+ });
+}
+
+#[test]
+fn repl_report_error() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec![
+ r#"console.log(1); reportError(new Error("foo")); console.log(2);"#,
+ ]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+
+ // TODO(nayeemrmn): The REPL should report event errors and rejections.
+ assert_contains!(out, "1\n2\nundefined\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn pty_aggregate_error() {
+ let (out, err) = util::run_and_collect_output(
+ true,
+ "repl",
+ Some(vec!["await Promise.any([])"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+
+ assert_contains!(out, "AggregateError");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn repl_with_quiet_flag() {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--quiet"],
+ Some(vec!["await Promise.resolve('done')"]),
+ Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
+ false,
+ );
+ assert!(!out.contains("Deno"));
+ assert!(!out.contains("exit using ctrl+d, ctrl+c, or close()"));
+ assert_ends_with!(out, "\"done\"\n");
+ assert!(err.is_empty());
+}
+
+#[test]
+fn npm_packages() {
+ let mut env_vars = util::env_vars_for_npm_tests();
+ env_vars.push(("NO_COLOR".to_owned(), "1".to_owned()));
+
+ {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--quiet", "--allow-read", "--allow-env"],
+ Some(vec![
+ r#"import chalk from "npm:chalk";"#,
+ "chalk.red('hel' + 'lo')",
+ ]),
+ Some(env_vars.clone()),
+ true,
+ );
+
+ assert_contains!(out, "hello");
+ assert!(err.is_empty());
+ }
+
+ {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--quiet", "--allow-read", "--allow-env"],
+ Some(vec![
+ r#"const chalk = await import("npm:chalk");"#,
+ "chalk.default.red('hel' + 'lo')",
+ ]),
+ Some(env_vars.clone()),
+ true,
+ );
+
+ assert_contains!(out, "hello");
+ assert!(err.is_empty());
+ }
+
+ {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--quiet", "--allow-read", "--allow-env"],
+ Some(vec![r#"export {} from "npm:chalk";"#]),
+ Some(env_vars.clone()),
+ true,
+ );
+
+ assert_contains!(out, "Module {");
+ assert_contains!(out, "Chalk: [Function: Chalk],");
+ assert!(err.is_empty());
+ }
+
+ {
+ let (out, err) = util::run_and_collect_output_with_args(
+ true,
+ vec!["repl", "--quiet", "--allow-read", "--allow-env"],
+ Some(vec![r#"import foo from "npm:asdfawe52345asdf""#]),
+ Some(env_vars),
+ true,
+ );
+
+ assert_contains!(
+ out,
+ "error: npm package 'asdfawe52345asdf' does not exist"
+ );
+ assert!(err.is_empty());
+ }
+}
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
new file mode 100644
index 000000000..149aad0b3
--- /dev/null
+++ b/cli/tests/integration/run_tests.rs
@@ -0,0 +1,3724 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::url;
+use deno_runtime::deno_fetch::reqwest;
+use std::io::Read;
+use std::io::Write;
+use std::process::Command;
+use std::process::Stdio;
+use test_util as util;
+use test_util::TempDir;
+use tokio::task::LocalSet;
+use trust_dns_client::serialize::txt::Lexer;
+use trust_dns_client::serialize::txt::Parser;
+use util::assert_contains;
+
+itest!(stdout_write_all {
+ args: "run --quiet run/stdout_write_all.ts",
+ output: "run/stdout_write_all.out",
+});
+
+itest!(stdin_read_all {
+ args: "run --quiet run/stdin_read_all.ts",
+ output: "run/stdin_read_all.out",
+ input: Some("01234567890123456789012345678901234567890123456789"),
+});
+
+itest!(stdout_write_sync_async {
+ args: "run --quiet run/stdout_write_sync_async.ts",
+ output: "run/stdout_write_sync_async.out",
+});
+
+itest!(_001_hello {
+ args: "run --reload run/001_hello.js",
+ output: "run/001_hello.js.out",
+});
+
+itest!(_002_hello {
+ args: "run --quiet --reload run/002_hello.ts",
+ output: "run/002_hello.ts.out",
+});
+
+itest!(_003_relative_import {
+ args: "run --quiet --reload run/003_relative_import.ts",
+ output: "run/003_relative_import.ts.out",
+});
+
+itest!(_004_set_timeout {
+ args: "run --quiet --reload run/004_set_timeout.ts",
+ output: "run/004_set_timeout.ts.out",
+});
+
+itest!(_005_more_imports {
+ args: "run --quiet --reload run/005_more_imports.ts",
+ output: "run/005_more_imports.ts.out",
+});
+
+itest!(_006_url_imports {
+ args: "run --quiet --reload run/006_url_imports.ts",
+ output: "run/006_url_imports.ts.out",
+ http_server: true,
+});
+
+itest!(_012_async {
+ args: "run --quiet --reload run/012_async.ts",
+ output: "run/012_async.ts.out",
+});
+
+itest!(_013_dynamic_import {
+ args: "run --quiet --reload --allow-read run/013_dynamic_import.ts",
+ output: "run/013_dynamic_import.ts.out",
+});
+
+itest!(_014_duplicate_import {
+ args: "run --quiet --reload --allow-read run/014_duplicate_import.ts ",
+ output: "run/014_duplicate_import.ts.out",
+});
+
+itest!(_015_duplicate_parallel_import {
+ args:
+ "run --quiet --reload --allow-read run/015_duplicate_parallel_import.js",
+ output: "run/015_duplicate_parallel_import.js.out",
+});
+
+itest!(_016_double_await {
+ args: "run --quiet --allow-read --reload run/016_double_await.ts",
+ output: "run/016_double_await.ts.out",
+});
+
+itest!(_017_import_redirect {
+ args: "run --quiet --reload run/017_import_redirect.ts",
+ output: "run/017_import_redirect.ts.out",
+});
+
+itest!(_017_import_redirect_nocheck {
+ args: "run --quiet --reload --no-check run/017_import_redirect.ts",
+ output: "run/017_import_redirect.ts.out",
+});
+
+itest!(_017_import_redirect_info {
+ args: "info --quiet --reload run/017_import_redirect.ts",
+ output: "run/017_import_redirect_info.out",
+});
+
+itest!(_018_async_catch {
+ args: "run --quiet --reload run/018_async_catch.ts",
+ output: "run/018_async_catch.ts.out",
+});
+
+itest!(_019_media_types {
+ args: "run --reload run/019_media_types.ts",
+ output: "run/019_media_types.ts.out",
+ http_server: true,
+});
+
+itest!(_020_json_modules {
+ args: "run --reload run/020_json_modules.ts",
+ output: "run/020_json_modules.ts.out",
+ exit_code: 1,
+});
+
+itest!(_021_mjs_modules {
+ args: "run --quiet --reload run/021_mjs_modules.ts",
+ output: "run/021_mjs_modules.ts.out",
+});
+
+itest!(_023_no_ext {
+ args: "run --reload --check run/023_no_ext",
+ output: "run/023_no_ext.out",
+});
+
+// TODO(lucacasonato): remove --unstable when permissions goes stable
+itest!(_025_hrtime {
+ args: "run --quiet --allow-hrtime --unstable --reload run/025_hrtime.ts",
+ output: "run/025_hrtime.ts.out",
+});
+
+itest!(_025_reload_js_type_error {
+ args: "run --quiet --reload run/025_reload_js_type_error.js",
+ output: "run/025_reload_js_type_error.js.out",
+});
+
+itest!(_026_redirect_javascript {
+ args: "run --quiet --reload run/026_redirect_javascript.js",
+ output: "run/026_redirect_javascript.js.out",
+ http_server: true,
+});
+
+itest!(_027_redirect_typescript {
+ args: "run --quiet --reload run/027_redirect_typescript.ts",
+ output: "run/027_redirect_typescript.ts.out",
+ http_server: true,
+});
+
+itest!(_028_args {
+ args:
+ "run --quiet --reload run/028_args.ts --arg1 val1 --arg2=val2 -- arg3 arg4",
+ output: "run/028_args.ts.out",
+});
+
+itest!(_033_import_map {
+ args:
+ "run --quiet --reload --import-map=import_maps/import_map.json import_maps/test.ts",
+ output: "run/033_import_map.out",
+});
+
+itest!(_033_import_map_remote {
+ args:
+ "run --quiet --reload --import-map=http://127.0.0.1:4545/import_maps/import_map_remote.json --unstable import_maps/test_remote.ts",
+ output: "run/033_import_map_remote.out",
+ http_server: true,
+});
+
+itest!(onload {
+ args: "run --quiet --reload run/onload/main.ts",
+ output: "run/onload/main.out",
+});
+
+itest!(_035_cached_only_flag {
+ args: "run --reload --check --cached-only http://127.0.0.1:4545/run/019_media_types.ts",
+ output: "run/035_cached_only_flag.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(_038_checkjs {
+ // checking if JS file is run through TS compiler
+ args:
+ "run --reload --config run/checkjs.tsconfig.json --check run/038_checkjs.js",
+ exit_code: 1,
+ output: "run/038_checkjs.js.out",
+});
+
+itest!(_042_dyn_import_evalcontext {
+ args: "run --quiet --allow-read --reload run/042_dyn_import_evalcontext.ts",
+ output: "run/042_dyn_import_evalcontext.ts.out",
+});
+
+itest!(_044_bad_resource {
+ args: "run --quiet --reload --allow-read run/044_bad_resource.ts",
+ output: "run/044_bad_resource.ts.out",
+ exit_code: 1,
+});
+
+// TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
+itest!(_045_proxy {
+ args: "run -L debug --unstable --allow-net --allow-env --allow-run --allow-read --reload --quiet run/045_proxy_test.ts",
+ output: "run/045_proxy_test.ts.out",
+ http_server: true,
+});
+
+itest!(_046_tsx {
+ args: "run --quiet --reload run/046_jsx_test.tsx",
+ output: "run/046_jsx_test.tsx.out",
+});
+
+itest!(_047_jsx {
+ args: "run --quiet --reload run/047_jsx_test.jsx",
+ output: "run/047_jsx_test.jsx.out",
+});
+
+itest!(_048_media_types_jsx {
+ args: "run --reload run/048_media_types_jsx.ts",
+ output: "run/048_media_types_jsx.ts.out",
+ http_server: true,
+});
+
+itest!(_052_no_remote_flag {
+ args:
+ "run --reload --check --no-remote http://127.0.0.1:4545/run/019_media_types.ts",
+ output: "run/052_no_remote_flag.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(_056_make_temp_file_write_perm {
+ args:
+ "run --quiet --allow-read --allow-write=./subdir/ run/056_make_temp_file_write_perm.ts",
+ output: "run/056_make_temp_file_write_perm.out",
+});
+
+itest!(_058_tasks_microtasks_close {
+ args: "run --quiet run/058_tasks_microtasks_close.ts",
+ output: "run/058_tasks_microtasks_close.ts.out",
+});
+
+itest!(_059_fs_relative_path_perm {
+ args: "run run/059_fs_relative_path_perm.ts",
+ output: "run/059_fs_relative_path_perm.ts.out",
+ exit_code: 1,
+});
+
+itest!(_070_location {
+ args: "run --location https://foo/bar?baz#bat run/070_location.ts",
+ output: "run/070_location.ts.out",
+});
+
+itest!(_071_location_unset {
+ args: "run run/071_location_unset.ts",
+ output: "run/071_location_unset.ts.out",
+});
+
+itest!(_072_location_relative_fetch {
+ args: "run --location http://127.0.0.1:4545/ --allow-net run/072_location_relative_fetch.ts",
+ output: "run/072_location_relative_fetch.ts.out",
+ http_server: true,
+});
+
+// tests the beforeunload event
+itest!(beforeunload_event {
+ args: "run run/before_unload.js",
+ output: "run/before_unload.js.out",
+});
+
+// tests the serialization of webstorage (both localStorage and sessionStorage)
+itest!(webstorage_serialization {
+ args: "run run/webstorage/serialization.ts",
+ output: "run/webstorage/serialization.ts.out",
+});
+
+// tests to ensure that when `--location` is set, all code shares the same
+// localStorage cache based on the origin of the location URL.
+#[test]
+fn webstorage_location_shares_origin() {
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--location")
+ .arg("https://example.com/a.ts")
+ .arg("run/webstorage/fixture.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 0 }\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--location")
+ .arg("https://example.com/b.ts")
+ .arg("run/webstorage/logger.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
+}
+
+// test to ensure that when a --config file is set, but no --location, that
+// storage persists against unique configuration files.
+#[test]
+fn webstorage_config_file() {
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--config")
+ .arg("run/webstorage/config_a.jsonc")
+ .arg("run/webstorage/fixture.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 0 }\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--config")
+ .arg("run/webstorage/config_b.jsonc")
+ .arg("run/webstorage/logger.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 0 }\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--config")
+ .arg("run/webstorage/config_a.jsonc")
+ .arg("run/webstorage/logger.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
+}
+
+// tests to ensure `--config` does not effect persisted storage when a
+// `--location` is provided.
+#[test]
+fn webstorage_location_precedes_config() {
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--location")
+ .arg("https://example.com/a.ts")
+ .arg("--config")
+ .arg("run/webstorage/config_a.jsonc")
+ .arg("run/webstorage/fixture.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 0 }\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--location")
+ .arg("https://example.com/b.ts")
+ .arg("--config")
+ .arg("run/webstorage/config_b.jsonc")
+ .arg("run/webstorage/logger.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
+}
+
+// test to ensure that when there isn't a configuration or location, that the
+// main module is used to determine how to persist storage data.
+#[test]
+fn webstorage_main_module() {
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("run/webstorage/fixture.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 0 }\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("run/webstorage/logger.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 0 }\n");
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("run/webstorage/fixture.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
+}
+
+itest!(_075_import_local_query_hash {
+ args: "run run/075_import_local_query_hash.ts",
+ output: "run/075_import_local_query_hash.ts.out",
+});
+
+itest!(_077_fetch_empty {
+ args: "run -A run/077_fetch_empty.ts",
+ output: "run/077_fetch_empty.ts.out",
+ exit_code: 1,
+});
+
+itest!(_078_unload_on_exit {
+ args: "run run/078_unload_on_exit.ts",
+ output: "run/078_unload_on_exit.ts.out",
+ exit_code: 1,
+});
+
+itest!(_079_location_authentication {
+ args:
+ "run --location https://foo:bar@baz/qux run/079_location_authentication.ts",
+ output: "run/079_location_authentication.ts.out",
+});
+
+itest!(_081_location_relative_fetch_redirect {
+ args: "run --location http://127.0.0.1:4546/ --allow-net run/081_location_relative_fetch_redirect.ts",
+ output: "run/081_location_relative_fetch_redirect.ts.out",
+ http_server: true,
+ });
+
+itest!(_082_prepare_stack_trace_throw {
+ args: "run run/082_prepare_stack_trace_throw.js",
+ output: "run/082_prepare_stack_trace_throw.js.out",
+ exit_code: 1,
+});
+
+#[test]
+fn _083_legacy_external_source_map() {
+ let _g = util::http_server();
+ let deno_dir = TempDir::new();
+ let module_url = url::Url::parse(
+ "http://localhost:4545/run/083_legacy_external_source_map.ts",
+ )
+ .unwrap();
+ // Write a faulty old external source map.
+ let faulty_map_path = deno_dir.path().join("gen/http/localhost_PORT4545/9576bd5febd0587c5c4d88d57cb3ac8ebf2600c529142abe3baa9a751d20c334.js.map");
+ std::fs::create_dir_all(faulty_map_path.parent().unwrap()).unwrap();
+ std::fs::write(faulty_map_path, "{\"version\":3,\"file\":\"\",\"sourceRoot\":\"\",\"sources\":[\"http://localhost:4545/083_legacy_external_source_map.ts\"],\"names\":[],\"mappings\":\";AAAA,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC\"}").unwrap();
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(module_url.to_string())
+ .output()
+ .unwrap();
+ // Before https://github.com/denoland/deno/issues/6965 was fixed, the faulty
+ // old external source map would cause a panic while formatting the error
+ // and the exit code would be 101. The external source map should be ignored
+ // in favor of the inline one.
+ assert_eq!(output.status.code(), Some(1));
+ let out = std::str::from_utf8(&output.stdout).unwrap();
+ assert_eq!(out, "");
+}
+
+itest!(dynamic_import_async_error {
+ args: "run --allow-read run/dynamic_import_async_error/main.ts",
+ output: "run/dynamic_import_async_error/main.out",
+});
+
+itest!(dynamic_import_already_rejected {
+ args: "run --allow-read run/dynamic_import_already_rejected/main.ts",
+ output: "run/dynamic_import_already_rejected/main.out",
+});
+
+itest!(no_check_imports_not_used_as_values {
+ args: "run --config run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json --no-check run/no_check_imports_not_used_as_values/main.ts",
+ output: "run/no_check_imports_not_used_as_values/main.out",
+ });
+
+itest!(_088_dynamic_import_already_evaluating {
+ args: "run --allow-read run/088_dynamic_import_already_evaluating.ts",
+ output: "run/088_dynamic_import_already_evaluating.ts.out",
+});
+
+// TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
+itest!(_089_run_allow_list {
+ args: "run --unstable --allow-run=curl run/089_run_allow_list.ts",
+ output: "run/089_run_allow_list.ts.out",
+});
+
+#[test]
+fn _090_run_permissions_request() {
+ let args = "run --quiet run/090_run_permissions_request.ts";
+ use util::PtyData::*;
+ util::test_pty2(args, vec![
+ Output("⚠️ ️Deno requests run access to \"ls\". Run again with --allow-run to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
+ Input("y\n"),
+ Output("⚠️ ️Deno requests run access to \"cat\". Run again with --allow-run to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
+ Input("n\n"),
+ Output("granted\r\n"),
+ Output("prompt\r\n"),
+ Output("denied\r\n"),
+ ]);
+}
+
+itest!(_091_use_define_for_class_fields {
+ args: "run --check run/091_use_define_for_class_fields.ts",
+ output: "run/091_use_define_for_class_fields.ts.out",
+ exit_code: 1,
+});
+
+itest!(_092_import_map_unmapped_bare_specifier {
+ args: "run --import-map import_maps/import_map.json run/092_import_map_unmapped_bare_specifier.ts",
+ output: "run/092_import_map_unmapped_bare_specifier.ts.out",
+ exit_code: 1,
+});
+
+itest!(js_import_detect {
+ args: "run --quiet --reload run/js_import_detect.ts",
+ output: "run/js_import_detect.ts.out",
+ exit_code: 0,
+});
+
+itest!(blob_gc_finalization {
+ args: "run run/blob_gc_finalization.js",
+ output: "run/blob_gc_finalization.js.out",
+ exit_code: 0,
+});
+
+itest!(fetch_response_finalization {
+ args:
+ "run --v8-flags=--expose-gc --allow-net run/fetch_response_finalization.js",
+ output: "run/fetch_response_finalization.js.out",
+ http_server: true,
+ exit_code: 0,
+});
+
+itest!(import_type {
+ args: "run --reload run/import_type.ts",
+ output: "run/import_type.ts.out",
+});
+
+itest!(import_type_no_check {
+ args: "run --reload --no-check run/import_type.ts",
+ output: "run/import_type.ts.out",
+});
+
+itest!(private_field_presence {
+ args: "run --reload run/private_field_presence.ts",
+ output: "run/private_field_presence.ts.out",
+});
+
+itest!(private_field_presence_no_check {
+ args: "run --reload --no-check run/private_field_presence.ts",
+ output: "run/private_field_presence.ts.out",
+});
+
+// TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
+itest!(lock_write_fetch {
+ args:
+ "run --quiet --allow-read --allow-write --allow-env --allow-run --unstable run/lock_write_fetch/main.ts",
+ output: "run/lock_write_fetch/main.out",
+ http_server: true,
+ exit_code: 0,
+});
+
+itest!(lock_check_ok {
+ args:
+ "run --lock=run/lock_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
+ output: "run/003_relative_import.ts.out",
+ http_server: true,
+});
+
+itest!(lock_check_ok2 {
+ args: "run --lock=run/lock_check_ok2.json run/019_media_types.ts",
+ output: "run/019_media_types.ts.out",
+ http_server: true,
+});
+
+itest!(lock_dynamic_imports {
+ args: "run --lock=run/lock_dynamic_imports.json --allow-read --allow-net http://127.0.0.1:4545/run/013_dynamic_import.ts",
+ output: "run/lock_dynamic_imports.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(lock_check_err {
+ args: "run --lock=run/lock_check_err.json http://127.0.0.1:4545/run/003_relative_import.ts",
+ output: "run/lock_check_err.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(lock_check_err2 {
+ args: "run --lock=run/lock_check_err2.json run/019_media_types.ts",
+ output: "run/lock_check_err2.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(config_file_lock_path {
+ args: "run --config=run/config_file_lock_path.json run/019_media_types.ts",
+ output: "run/config_file_lock_path.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(lock_flag_overrides_config_file_lock_path {
+ args: "run --lock=run/lock_check_ok2.json --config=run/config_file_lock_path.json run/019_media_types.ts",
+ output: "run/019_media_types.ts.out",
+ http_server: true,
+ });
+
+itest!(lock_v2_check_ok {
+ args:
+ "run --lock=run/lock_v2_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
+ output: "run/003_relative_import.ts.out",
+ http_server: true,
+});
+
+itest!(lock_v2_check_ok2 {
+ args: "run --lock=run/lock_v2_check_ok2.json run/019_media_types.ts",
+ output: "run/019_media_types.ts.out",
+ http_server: true,
+});
+
+itest!(lock_v2_dynamic_imports {
+ args: "run --lock=run/lock_v2_dynamic_imports.json --allow-read --allow-net http://127.0.0.1:4545/run/013_dynamic_import.ts",
+ output: "run/lock_v2_dynamic_imports.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(lock_v2_check_err {
+ args: "run --lock=run/lock_v2_check_err.json http://127.0.0.1:4545/run/003_relative_import.ts",
+ output: "run/lock_v2_check_err.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(lock_v2_check_err2 {
+ args: "run --lock=run/lock_v2_check_err2.json run/019_media_types.ts",
+ output: "run/lock_v2_check_err2.out",
+ exit_code: 10,
+ http_server: true,
+});
+
+itest!(lock_only_http_and_https {
+ args: "run --lock=run/lock_only_http_and_https/deno.lock run/lock_only_http_and_https/main.ts",
+ output: "run/lock_only_http_and_https/main.out",
+ http_server: true,
+});
+
+itest!(mts_dmts_mjs {
+ args: "run subdir/import.mts",
+ output: "run/mts_dmts_mjs.out",
+});
+
+itest!(mts_dmts_mjs_no_check {
+ args: "run --no-check subdir/import.mts",
+ output: "run/mts_dmts_mjs.out",
+});
+
+itest!(async_error {
+ exit_code: 1,
+ args: "run --reload run/async_error.ts",
+ output: "run/async_error.ts.out",
+});
+
+itest!(config {
+ args:
+ "run --reload --config run/config/tsconfig.json --check run/config/main.ts",
+ output: "run/config/main.out",
+});
+
+itest!(config_types {
+ args:
+ "run --reload --quiet --config run/config_types/tsconfig.json run/config_types/main.ts",
+ output: "run/config_types/main.out",
+});
+
+itest!(config_types_remote {
+ http_server: true,
+ args: "run --reload --quiet --config run/config_types/remote.tsconfig.json run/config_types/main.ts",
+ output: "run/config_types/main.out",
+ });
+
+itest!(empty_typescript {
+ args: "run --reload --check run/empty.ts",
+ output_str: Some("Check file:[WILDCARD]/run/empty.ts\n"),
+});
+
+itest!(error_001 {
+ args: "run --reload run/error_001.ts",
+ exit_code: 1,
+ output: "run/error_001.ts.out",
+});
+
+itest!(error_002 {
+ args: "run --reload run/error_002.ts",
+ exit_code: 1,
+ output: "run/error_002.ts.out",
+});
+
+itest!(error_003_typescript {
+ args: "run --reload --check run/error_003_typescript.ts",
+ exit_code: 1,
+ output: "run/error_003_typescript.ts.out",
+});
+
+// Supposing that we've already attempted to run error_003_typescript.ts
+// we want to make sure that JS wasn't emitted. Running again without reload flag
+// should result in the same output.
+// https://github.com/denoland/deno/issues/2436
+itest!(error_003_typescript2 {
+ args: "run --check run/error_003_typescript.ts",
+ exit_code: 1,
+ output: "run/error_003_typescript.ts.out",
+});
+
+itest!(error_004_missing_module {
+ args: "run --reload run/error_004_missing_module.ts",
+ exit_code: 1,
+ output: "run/error_004_missing_module.ts.out",
+});
+
+itest!(error_005_missing_dynamic_import {
+ args:
+ "run --reload --allow-read --quiet run/error_005_missing_dynamic_import.ts",
+ exit_code: 1,
+ output: "run/error_005_missing_dynamic_import.ts.out",
+});
+
+itest!(error_006_import_ext_failure {
+ args: "run --reload run/error_006_import_ext_failure.ts",
+ exit_code: 1,
+ output: "run/error_006_import_ext_failure.ts.out",
+});
+
+itest!(error_007_any {
+ args: "run --reload run/error_007_any.ts",
+ exit_code: 1,
+ output: "run/error_007_any.ts.out",
+});
+
+itest!(error_008_checkjs {
+ args: "run --reload run/error_008_checkjs.js",
+ exit_code: 1,
+ output: "run/error_008_checkjs.js.out",
+});
+
+itest!(error_009_extensions_error {
+ args: "run run/error_009_extensions_error.js",
+ output: "run/error_009_extensions_error.js.out",
+ exit_code: 1,
+});
+
+itest!(error_011_bad_module_specifier {
+ args: "run --reload run/error_011_bad_module_specifier.ts",
+ exit_code: 1,
+ output: "run/error_011_bad_module_specifier.ts.out",
+});
+
+itest!(error_012_bad_dynamic_import_specifier {
+ args: "run --reload --check run/error_012_bad_dynamic_import_specifier.ts",
+ exit_code: 1,
+ output: "run/error_012_bad_dynamic_import_specifier.ts.out",
+});
+
+itest!(error_013_missing_script {
+ args: "run --reload missing_file_name",
+ exit_code: 1,
+ output: "run/error_013_missing_script.out",
+});
+
+itest!(error_014_catch_dynamic_import_error {
+ args:
+ "run --reload --allow-read run/error_014_catch_dynamic_import_error.js",
+ output: "run/error_014_catch_dynamic_import_error.js.out",
+});
+
+itest!(error_015_dynamic_import_permissions {
+ args: "run --reload --quiet run/error_015_dynamic_import_permissions.js",
+ output: "run/error_015_dynamic_import_permissions.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+// We have an allow-net flag but not allow-read, it should still result in error.
+itest!(error_016_dynamic_import_permissions2 {
+ args: "run --reload --allow-net run/error_016_dynamic_import_permissions2.js",
+ output: "run/error_016_dynamic_import_permissions2.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(error_017_hide_long_source_ts {
+ args: "run --reload --check run/error_017_hide_long_source_ts.ts",
+ output: "run/error_017_hide_long_source_ts.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_018_hide_long_source_js {
+ args: "run run/error_018_hide_long_source_js.js",
+ output: "run/error_018_hide_long_source_js.js.out",
+ exit_code: 1,
+});
+
+itest!(error_019_stack_function {
+ args: "run run/error_019_stack_function.ts",
+ output: "run/error_019_stack_function.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_020_stack_constructor {
+ args: "run run/error_020_stack_constructor.ts",
+ output: "run/error_020_stack_constructor.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_021_stack_method {
+ args: "run run/error_021_stack_method.ts",
+ output: "run/error_021_stack_method.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_022_stack_custom_error {
+ args: "run run/error_022_stack_custom_error.ts",
+ output: "run/error_022_stack_custom_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_023_stack_async {
+ args: "run run/error_023_stack_async.ts",
+ output: "run/error_023_stack_async.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_024_stack_promise_all {
+ args: "run run/error_024_stack_promise_all.ts",
+ output: "run/error_024_stack_promise_all.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_025_tab_indent {
+ args: "run run/error_025_tab_indent",
+ output: "run/error_025_tab_indent.out",
+ exit_code: 1,
+});
+
+itest!(error_026_remote_import_error {
+ args: "run run/error_026_remote_import_error.ts",
+ output: "run/error_026_remote_import_error.ts.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(error_for_await {
+ args: "run --reload --check run/error_for_await.ts",
+ output: "run/error_for_await.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_missing_module_named_import {
+ args: "run --reload run/error_missing_module_named_import.ts",
+ output: "run/error_missing_module_named_import.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_no_check {
+ args: "run --reload --no-check run/error_no_check.ts",
+ output: "run/error_no_check.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_syntax {
+ args: "run --reload run/error_syntax.js",
+ exit_code: 1,
+ output: "run/error_syntax.js.out",
+});
+
+itest!(error_syntax_empty_trailing_line {
+ args: "run --reload run/error_syntax_empty_trailing_line.mjs",
+ exit_code: 1,
+ output: "run/error_syntax_empty_trailing_line.mjs.out",
+});
+
+itest!(error_type_definitions {
+ args: "run --reload --check run/error_type_definitions.ts",
+ exit_code: 1,
+ output: "run/error_type_definitions.ts.out",
+});
+
+itest!(error_local_static_import_from_remote_ts {
+ args: "run --reload http://localhost:4545/run/error_local_static_import_from_remote.ts",
+ exit_code: 1,
+ http_server: true,
+ output: "run/error_local_static_import_from_remote.ts.out",
+ });
+
+itest!(error_local_static_import_from_remote_js {
+ args: "run --reload http://localhost:4545/run/error_local_static_import_from_remote.js",
+ exit_code: 1,
+ http_server: true,
+ output: "run/error_local_static_import_from_remote.js.out",
+ });
+
+itest!(exit_error42 {
+ exit_code: 42,
+ args: "run --quiet --reload run/exit_error42.ts",
+ output: "run/exit_error42.ts.out",
+});
+
+itest!(set_exit_code_0 {
+ args: "run --no-check --unstable run/set_exit_code_0.ts",
+ output_str: Some(""),
+ exit_code: 0,
+});
+
+itest!(set_exit_code_1 {
+ args: "run --no-check --unstable run/set_exit_code_1.ts",
+ output_str: Some(""),
+ exit_code: 42,
+});
+
+itest!(set_exit_code_2 {
+ args: "run --no-check --unstable run/set_exit_code_2.ts",
+ output_str: Some(""),
+ exit_code: 42,
+});
+
+itest!(op_exit_op_set_exit_code_in_worker {
+ args: "run --no-check --unstable --allow-read run/op_exit_op_set_exit_code_in_worker.ts",
+ exit_code: 21,
+ output_str: Some(""),
+});
+
+itest!(deno_exit_tampering {
+ args: "run --no-check --unstable run/deno_exit_tampering.ts",
+ output_str: Some(""),
+ exit_code: 42,
+});
+
+itest!(heapstats {
+ args: "run --quiet --unstable --v8-flags=--expose-gc run/heapstats.js",
+ output: "run/heapstats.js.out",
+});
+
+itest!(finalization_registry {
+ args:
+ "run --quiet --unstable --v8-flags=--expose-gc run/finalization_registry.js",
+ output: "run/finalization_registry.js.out",
+});
+
+itest!(https_import {
+ args: "run --quiet --reload --cert tls/RootCA.pem run/https_import.ts",
+ output: "run/https_import.ts.out",
+ http_server: true,
+});
+
+itest!(if_main {
+ args: "run --quiet --reload run/if_main.ts",
+ output: "run/if_main.ts.out",
+});
+
+itest!(import_meta {
+ args: "run --quiet --reload --import-map=run/import_meta/importmap.json run/import_meta/main.ts",
+ output: "run/import_meta/main.out",
+});
+
+itest!(main_module {
+ args: "run --quiet --allow-read --reload run/main_module/main.ts",
+ output: "run/main_module/main.out",
+});
+
+itest!(no_check {
+ args: "run --quiet --reload --no-check run/006_url_imports.ts",
+ output: "run/006_url_imports.ts.out",
+ http_server: true,
+});
+
+itest!(no_check_decorators {
+ args: "run --quiet --reload --no-check run/no_check_decorators.ts",
+ output: "run/no_check_decorators.ts.out",
+});
+
+itest!(check_remote {
+ args: "run --quiet --reload --check=all run/no_check_remote.ts",
+ output: "run/no_check_remote.ts.disabled.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(no_check_remote {
+ args: "run --quiet --reload --no-check=remote run/no_check_remote.ts",
+ output: "run/no_check_remote.ts.enabled.out",
+ http_server: true,
+});
+
+itest!(runtime_decorators {
+ args: "run --quiet --reload --no-check run/runtime_decorators.ts",
+ output: "run/runtime_decorators.ts.out",
+});
+
+itest!(seed_random {
+ args: "run --seed=100 run/seed_random.js",
+ output: "run/seed_random.js.out",
+});
+
+itest!(type_definitions {
+ args: "run --reload run/type_definitions.ts",
+ output: "run/type_definitions.ts.out",
+});
+
+itest!(type_definitions_for_export {
+ args: "run --reload --check run/type_definitions_for_export.ts",
+ output: "run/type_definitions_for_export.ts.out",
+ exit_code: 1,
+});
+
+itest!(type_directives_01 {
+ args: "run --reload --check=all -L debug run/type_directives_01.ts",
+ output: "run/type_directives_01.ts.out",
+ http_server: true,
+});
+
+itest!(type_directives_02 {
+ args: "run --reload --check=all -L debug run/type_directives_02.ts",
+ output: "run/type_directives_02.ts.out",
+});
+
+itest!(type_directives_js_main {
+ args: "run --reload -L debug run/type_directives_js_main.js",
+ output: "run/type_directives_js_main.js.out",
+ exit_code: 0,
+});
+
+itest!(type_directives_redirect {
+ args: "run --reload --check run/type_directives_redirect.ts",
+ output: "run/type_directives_redirect.ts.out",
+ http_server: true,
+});
+
+itest!(type_headers_deno_types {
+ args: "run --reload --check run/type_headers_deno_types.ts",
+ output: "run/type_headers_deno_types.ts.out",
+ http_server: true,
+});
+
+itest!(ts_type_imports {
+ args: "run --reload --check run/ts_type_imports.ts",
+ output: "run/ts_type_imports.ts.out",
+ exit_code: 1,
+});
+
+itest!(ts_decorators {
+ args: "run --reload --check run/ts_decorators.ts",
+ output: "run/ts_decorators.ts.out",
+});
+
+itest!(ts_type_only_import {
+ args: "run --reload --check run/ts_type_only_import.ts",
+ output: "run/ts_type_only_import.ts.out",
+});
+
+itest!(swc_syntax_error {
+ args: "run --reload --check run/swc_syntax_error.ts",
+ output: "run/swc_syntax_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(unbuffered_stderr {
+ args: "run --reload run/unbuffered_stderr.ts",
+ output: "run/unbuffered_stderr.ts.out",
+});
+
+itest!(unbuffered_stdout {
+ args: "run --quiet --reload run/unbuffered_stdout.ts",
+ output: "run/unbuffered_stdout.ts.out",
+});
+
+itest!(v8_flags_run {
+ args: "run --v8-flags=--expose-gc run/v8_flags.js",
+ output: "run/v8_flags.js.out",
+});
+
+itest!(v8_flags_unrecognized {
+ args: "repl --v8-flags=--foo,bar,--trace-gc,-baz",
+ output: "run/v8_flags_unrecognized.out",
+ exit_code: 1,
+});
+
+itest!(v8_help {
+ args: "repl --v8-flags=--help",
+ output: "run/v8_help.out",
+});
+
+itest!(unsupported_dynamic_import_scheme {
+ args: "eval import('xxx:')",
+ output: "run/unsupported_dynamic_import_scheme.out",
+ exit_code: 1,
+});
+
+itest!(wasm {
+ args: "run --quiet run/wasm.ts",
+ output: "run/wasm.ts.out",
+});
+
+itest!(wasm_shared {
+ args: "run --quiet run/wasm_shared.ts",
+ output: "run/wasm_shared.out",
+});
+
+itest!(wasm_async {
+ args: "run run/wasm_async.js",
+ output: "run/wasm_async.out",
+});
+
+itest!(wasm_unreachable {
+ args: "run --allow-read run/wasm_unreachable.js",
+ output: "run/wasm_unreachable.out",
+ exit_code: 1,
+});
+
+itest!(wasm_url {
+ args: "run --quiet --allow-net=localhost:4545 run/wasm_url.js",
+ output: "run/wasm_url.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(weakref {
+ args: "run --quiet --reload run/weakref.ts",
+ output: "run/weakref.ts.out",
+});
+
+itest!(top_level_await_order {
+ args: "run --allow-read run/top_level_await/order.js",
+ output: "run/top_level_await/order.out",
+});
+
+itest!(top_level_await_loop {
+ args: "run --allow-read run/top_level_await/loop.js",
+ output: "run/top_level_await/loop.out",
+});
+
+itest!(top_level_await_circular {
+ args: "run --allow-read run/top_level_await/circular.js",
+ output: "run/top_level_await/circular.out",
+ exit_code: 1,
+});
+
+// Regression test for https://github.com/denoland/deno/issues/11238.
+itest!(top_level_await_nested {
+ args: "run --allow-read run/top_level_await/nested/main.js",
+ output: "run/top_level_await/nested.out",
+});
+
+itest!(top_level_await_unresolved {
+ args: "run run/top_level_await/unresolved.js",
+ output: "run/top_level_await/unresolved.out",
+ exit_code: 1,
+});
+
+itest!(top_level_await {
+ args: "run --allow-read run/top_level_await/top_level_await.js",
+ output: "run/top_level_await/top_level_await.out",
+});
+
+itest!(top_level_await_ts {
+ args: "run --quiet --allow-read run/top_level_await/top_level_await.ts",
+ output: "run/top_level_await/top_level_await.out",
+});
+
+itest!(top_level_for_await {
+ args: "run --quiet run/top_level_await/top_level_for_await.js",
+ output: "run/top_level_await/top_level_for_await.out",
+});
+
+itest!(top_level_for_await_ts {
+ args: "run --quiet run/top_level_await/top_level_for_await.ts",
+ output: "run/top_level_await/top_level_for_await.out",
+});
+
+itest!(unstable_disabled {
+ args: "run --reload --check run/unstable.ts",
+ exit_code: 1,
+ output: "run/unstable_disabled.out",
+});
+
+itest!(unstable_enabled {
+ args: "run --quiet --reload --unstable run/unstable.ts",
+ output: "run/unstable_enabled.out",
+});
+
+itest!(unstable_disabled_js {
+ args: "run --reload run/unstable.js",
+ output: "run/unstable_disabled_js.out",
+});
+
+itest!(unstable_enabled_js {
+ args: "run --quiet --reload --unstable run/unstable.ts",
+ output: "run/unstable_enabled_js.out",
+});
+
+itest!(unstable_worker {
+ args: "run --reload --unstable --quiet --allow-read run/unstable_worker.ts",
+ output: "run/unstable_worker.ts.out",
+});
+
+itest!(import_compression {
+ args: "run --quiet --reload --allow-net run/import_compression/main.ts",
+ output: "run/import_compression/main.out",
+ http_server: true,
+});
+
+itest!(disallow_http_from_https_js {
+ args: "run --quiet --reload --cert tls/RootCA.pem https://localhost:5545/run/disallow_http_from_https.js",
+ output: "run/disallow_http_from_https_js.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(disallow_http_from_https_ts {
+ args: "run --quiet --reload --cert tls/RootCA.pem https://localhost:5545/run/disallow_http_from_https.ts",
+ output: "run/disallow_http_from_https_ts.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(dynamic_import_conditional {
+ args: "run --quiet --reload run/dynamic_import_conditional.js",
+ output: "run/dynamic_import_conditional.js.out",
+});
+
+itest!(tsx_imports {
+ args: "run --reload --check run/tsx_imports/tsx_imports.ts",
+ output: "run/tsx_imports/tsx_imports.ts.out",
+});
+
+itest!(fix_dynamic_import_errors {
+ args: "run --reload run/fix_dynamic_import_errors.js",
+ output: "run/fix_dynamic_import_errors.js.out",
+});
+
+itest!(fix_emittable_skipped {
+ args: "run --reload run/fix_emittable_skipped.js",
+ output: "run/fix_emittable_skipped.ts.out",
+});
+
+itest!(fix_js_import_js {
+ args: "run --quiet --reload run/fix_js_import_js.ts",
+ output: "run/fix_js_import_js.ts.out",
+});
+
+itest!(fix_js_imports {
+ args: "run --quiet --reload run/fix_js_imports.ts",
+ output: "run/fix_js_imports.ts.out",
+});
+
+itest!(fix_tsc_file_exists {
+ args: "run --quiet --reload tsc/test.js",
+ output: "run/fix_tsc_file_exists.out",
+});
+
+itest!(fix_worker_dispatchevent {
+ args: "run --quiet --reload run/fix_worker_dispatchevent.ts",
+ output: "run/fix_worker_dispatchevent.ts.out",
+});
+
+itest!(es_private_fields {
+ args: "run --quiet --reload run/es_private_fields.js",
+ output: "run/es_private_fields.js.out",
+});
+
+itest!(cjs_imports {
+ args: "run --quiet --reload run/cjs_imports/main.ts",
+ output: "run/cjs_imports/main.out",
+});
+
+itest!(ts_import_from_js {
+ args: "run --quiet --reload run/ts_import_from_js/main.js",
+ output: "run/ts_import_from_js/main.out",
+ http_server: true,
+});
+
+itest!(jsx_import_from_ts {
+ args: "run --quiet --reload run/jsx_import_from_ts.ts",
+ output: "run/jsx_import_from_ts.ts.out",
+});
+
+itest!(jsx_import_source_pragma {
+ args: "run --reload run/jsx_import_source_pragma.tsx",
+ output: "run/jsx_import_source.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_with_config {
+ args:
+ "run --reload --config jsx/deno-jsx.jsonc --no-lock run/jsx_import_source_pragma.tsx",
+ output: "run/jsx_import_source.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_with_dev_config {
+ args:
+ "run --reload --config jsx/deno-jsxdev.jsonc --no-lock run/jsx_import_source_pragma.tsx",
+ output: "run/jsx_import_source_dev.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_no_pragma {
+ args:
+ "run --reload --config jsx/deno-jsx.jsonc --no-lock run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_no_pragma_dev {
+ args: "run --reload --config jsx/deno-jsxdev.jsonc --no-lock run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_dev.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_import_map {
+ args: "run --reload --import-map jsx/import-map.json run/jsx_import_source_pragma_import_map.tsx",
+ output: "run/jsx_import_source_import_map.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_import_map_dev {
+ args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsxdev-import-map.jsonc run/jsx_import_source_pragma_import_map.tsx",
+ output: "run/jsx_import_source_import_map_dev.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_import_map {
+ args: "run --reload --import-map jsx/import-map.json --no-lock --config jsx/deno-jsx-import-map.jsonc run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_import_map.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_import_map_dev {
+ args: "run --reload --import-map jsx/import-map.json --no-lock --config jsx/deno-jsxdev-import-map.jsonc run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_import_map_dev.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_import_map_scoped {
+ args: "run --reload --import-map jsx/import-map-scoped.json --no-lock --config jsx/deno-jsx-import-map.jsonc subdir/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_import_map.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_import_map_scoped_dev {
+ args: "run --reload --import-map jsx/import-map-scoped.json --no-lock --config jsx/deno-jsxdev-import-map.jsonc subdir/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_import_map_dev.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_no_check {
+ args: "run --reload --no-check run/jsx_import_source_pragma.tsx",
+ output: "run/jsx_import_source.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_with_config_no_check {
+ args: "run --reload --config jsx/deno-jsx.jsonc --no-lock --no-check run/jsx_import_source_pragma.tsx",
+ output: "run/jsx_import_source.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_no_pragma_no_check {
+ args:
+ "run --reload --config jsx/deno-jsx.jsonc --no-lock --no-check run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_pragma_import_map_no_check {
+ args: "run --reload --import-map jsx/import-map.json --no-check run/jsx_import_source_pragma_import_map.tsx",
+ output: "run/jsx_import_source_import_map.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_import_map_no_check {
+ args: "run --reload --import-map jsx/import-map.json --no-lock --config jsx/deno-jsx-import-map.jsonc --no-check run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_import_map.out",
+ http_server: true,
+});
+
+itest!(jsx_import_source_error {
+ args: "run --config jsx/deno-jsx-error.jsonc --check run/jsx_import_source_no_pragma.tsx",
+ output: "run/jsx_import_source_error.out",
+ exit_code: 1,
+});
+
+// TODO(#11128): Flaky. Re-enable later.
+// itest!(single_compile_with_reload {
+// args: "run --relcert/oad --allow-read run/single_compile_with_reload.ts",
+// output: "run/single_compile_with_reload.ts.out",
+// });
+
+itest!(proto_exploit {
+ args: "run run/proto_exploit.js",
+ output: "run/proto_exploit.js.out",
+});
+
+itest!(reference_types {
+ args: "run --reload --quiet run/reference_types.ts",
+ output: "run/reference_types.ts.out",
+});
+
+itest!(references_types_remote {
+ http_server: true,
+ args: "run --reload --quiet run/reference_types_remote.ts",
+ output: "run/reference_types_remote.ts.out",
+});
+
+itest!(reference_types_error {
+ args:
+ "run --config run/checkjs.tsconfig.json --check run/reference_types_error.js",
+ output: "run/reference_types_error.js.out",
+ exit_code: 1,
+});
+
+itest!(reference_types_error_no_check {
+ args: "run --no-check run/reference_types_error.js",
+ output_str: Some(""),
+});
+
+itest!(import_data_url_error_stack {
+ args: "run --quiet --reload run/import_data_url_error_stack.ts",
+ output: "run/import_data_url_error_stack.ts.out",
+ exit_code: 1,
+});
+
+itest!(import_data_url_import_relative {
+ args: "run --quiet --reload run/import_data_url_import_relative.ts",
+ output: "run/import_data_url_import_relative.ts.out",
+ exit_code: 1,
+});
+
+itest!(import_data_url_import_map {
+ args: "run --quiet --reload --import-map import_maps/import_map.json run/import_data_url.ts",
+ output: "run/import_data_url.ts.out",
+ });
+
+itest!(import_data_url_imports {
+ args: "run --quiet --reload run/import_data_url_imports.ts",
+ output: "run/import_data_url_imports.ts.out",
+ http_server: true,
+});
+
+itest!(import_data_url_jsx {
+ args: "run --quiet --reload run/import_data_url_jsx.ts",
+ output: "run/import_data_url_jsx.ts.out",
+});
+
+itest!(import_data_url {
+ args: "run --quiet --reload run/import_data_url.ts",
+ output: "run/import_data_url.ts.out",
+});
+
+itest!(import_dynamic_data_url {
+ args: "run --quiet --reload run/import_dynamic_data_url.ts",
+ output: "run/import_dynamic_data_url.ts.out",
+});
+
+itest!(import_blob_url_error_stack {
+ args: "run --quiet --reload run/import_blob_url_error_stack.ts",
+ output: "run/import_blob_url_error_stack.ts.out",
+ exit_code: 1,
+});
+
+itest!(import_blob_url_import_relative {
+ args: "run --quiet --reload run/import_blob_url_import_relative.ts",
+ output: "run/import_blob_url_import_relative.ts.out",
+ exit_code: 1,
+});
+
+itest!(import_blob_url_imports {
+ args:
+ "run --quiet --reload --allow-net=localhost:4545 run/import_blob_url_imports.ts",
+ output: "run/import_blob_url_imports.ts.out",
+ http_server: true,
+});
+
+itest!(import_blob_url_jsx {
+ args: "run --quiet --reload run/import_blob_url_jsx.ts",
+ output: "run/import_blob_url_jsx.ts.out",
+});
+
+itest!(import_blob_url {
+ args: "run --quiet --reload run/import_blob_url.ts",
+ output: "run/import_blob_url.ts.out",
+});
+
+itest!(import_file_with_colon {
+ args: "run --quiet --reload run/import_file_with_colon.ts",
+ output: "run/import_file_with_colon.ts.out",
+ http_server: true,
+});
+
+itest!(import_extensionless {
+ args: "run --quiet --reload run/import_extensionless.ts",
+ output: "run/import_extensionless.ts.out",
+ http_server: true,
+});
+
+itest!(classic_workers_event_loop {
+ args:
+ "run --enable-testing-features-do-not-use run/classic_workers_event_loop.js",
+ output: "run/classic_workers_event_loop.js.out",
+});
+
+// FIXME(bartlomieju): disabled, because this test is very flaky on CI
+// itest!(local_sources_not_cached_in_memory {
+// args: "run --allow-read --allow-write run/no_mem_cache.js",
+// output: "run/no_mem_cache.js.out",
+// });
+
+// This test checks that inline source map data is used. It uses a hand crafted
+// source map that maps to a file that exists, but is not loaded into the module
+// graph (inline_js_source_map_2.ts) (because there are no direct dependencies).
+// Source line is not remapped because no inline source contents are included in
+// the sourcemap and the file is not present in the dependency graph.
+itest!(inline_js_source_map_2 {
+ args: "run --quiet run/inline_js_source_map_2.js",
+ output: "run/inline_js_source_map_2.js.out",
+ exit_code: 1,
+});
+
+// This test checks that inline source map data is used. It uses a hand crafted
+// source map that maps to a file that exists, but is not loaded into the module
+// graph (inline_js_source_map_2.ts) (because there are no direct dependencies).
+// Source line remapped using th inline source contents that are included in the
+// inline source map.
+itest!(inline_js_source_map_2_with_inline_contents {
+ args: "run --quiet run/inline_js_source_map_2_with_inline_contents.js",
+ output: "run/inline_js_source_map_2_with_inline_contents.js.out",
+ exit_code: 1,
+});
+
+// This test checks that inline source map data is used. It uses a hand crafted
+// source map that maps to a file that exists, and is loaded into the module
+// graph because of a direct import statement (inline_js_source_map.ts). The
+// source map was generated from an earlier version of this file, where the throw
+// was not commented out. The source line is remapped using source contents that
+// from the module graph.
+itest!(inline_js_source_map_with_contents_from_graph {
+ args: "run --quiet run/inline_js_source_map_with_contents_from_graph.js",
+ output: "run/inline_js_source_map_with_contents_from_graph.js.out",
+ exit_code: 1,
+ http_server: true,
+});
+
+// This test ensures that a descriptive error is shown when we're unable to load
+// the import map. Even though this tests only the `run` subcommand, we can be sure
+// that the error message is similar for other subcommands as they all use
+// `program_state.maybe_import_map` to access the import map underneath.
+itest!(error_import_map_unable_to_load {
+ args: "run --import-map=import_maps/does_not_exist.json import_maps/test.ts",
+ output: "run/error_import_map_unable_to_load.out",
+ exit_code: 1,
+});
+
+// Test that setting `self` in the main thread to some other value doesn't break
+// the world.
+itest!(replace_self {
+ args: "run run/replace_self.js",
+ output: "run/replace_self.js.out",
+});
+
+itest!(worker_event_handler_test {
+ args: "run --quiet --reload --allow-read run/worker_event_handler_test.js",
+ output: "run/worker_event_handler_test.js.out",
+});
+
+itest!(worker_close_race {
+ args: "run --quiet --reload --allow-read run/worker_close_race.js",
+ output: "run/worker_close_race.js.out",
+});
+
+itest!(worker_drop_handle_race {
+ args: "run --quiet --reload --allow-read run/worker_drop_handle_race.js",
+ output: "run/worker_drop_handle_race.js.out",
+ exit_code: 1,
+});
+
+itest!(worker_drop_handle_race_terminate {
+ args: "run --unstable run/worker_drop_handle_race_terminate.js",
+ output: "run/worker_drop_handle_race_terminate.js.out",
+});
+
+itest!(worker_close_nested {
+ args: "run --quiet --reload --allow-read run/worker_close_nested.js",
+ output: "run/worker_close_nested.js.out",
+});
+
+itest!(worker_message_before_close {
+ args: "run --quiet --reload --allow-read run/worker_message_before_close.js",
+ output: "run/worker_message_before_close.js.out",
+});
+
+itest!(worker_close_in_wasm_reactions {
+ args:
+ "run --quiet --reload --allow-read run/worker_close_in_wasm_reactions.js",
+ output: "run/worker_close_in_wasm_reactions.js.out",
+});
+
+itest!(shebang_tsc {
+ args: "run --quiet --check run/shebang.ts",
+ output: "run/shebang.ts.out",
+});
+
+itest!(shebang_swc {
+ args: "run --quiet run/shebang.ts",
+ output: "run/shebang.ts.out",
+});
+
+itest!(shebang_with_json_imports_tsc {
+ args: "run --quiet import_assertions/json_with_shebang.ts",
+ output: "import_assertions/json_with_shebang.ts.out",
+ exit_code: 1,
+});
+
+itest!(shebang_with_json_imports_swc {
+ args: "run --quiet --no-check import_assertions/json_with_shebang.ts",
+ output: "import_assertions/json_with_shebang.ts.out",
+ exit_code: 1,
+});
+
+#[test]
+fn no_validate_asm() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("run/no_validate_asm.js")
+ .stderr(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert!(output.stderr.is_empty());
+ assert!(output.stdout.is_empty());
+}
+
+#[test]
+fn exec_path() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--allow-read")
+ .arg("run/exec_path.ts")
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
+ let actual = std::fs::canonicalize(std::path::Path::new(stdout_str)).unwrap();
+ let expected = std::fs::canonicalize(util::deno_exe_path()).unwrap();
+ assert_eq!(expected, actual);
+}
+
+#[cfg(windows)]
+// Clippy suggests to remove the `NoStd` prefix from all variants. I disagree.
+#[allow(clippy::enum_variant_names)]
+enum WinProcConstraints {
+ NoStdIn,
+ NoStdOut,
+ NoStdErr,
+}
+
+#[cfg(windows)]
+fn run_deno_script_constrained(
+ script_path: std::path::PathBuf,
+ constraints: WinProcConstraints,
+) -> Result<(), i64> {
+ let file_path = "assets/DenoWinRunner.ps1";
+ let constraints = match constraints {
+ WinProcConstraints::NoStdIn => "1",
+ WinProcConstraints::NoStdOut => "2",
+ WinProcConstraints::NoStdErr => "4",
+ };
+ let deno_exe_path = util::deno_exe_path()
+ .into_os_string()
+ .into_string()
+ .unwrap();
+
+ let deno_script_path = script_path.into_os_string().into_string().unwrap();
+
+ let args = vec![&deno_exe_path[..], &deno_script_path[..], constraints];
+ util::run_powershell_script_file(file_path, args)
+}
+
+#[cfg(windows)]
+#[test]
+fn should_not_panic_on_no_stdin() {
+ let output = run_deno_script_constrained(
+ util::testdata_path().join("echo.ts"),
+ WinProcConstraints::NoStdIn,
+ );
+ output.unwrap();
+}
+
+#[cfg(windows)]
+#[test]
+fn should_not_panic_on_no_stdout() {
+ let output = run_deno_script_constrained(
+ util::testdata_path().join("echo.ts"),
+ WinProcConstraints::NoStdOut,
+ );
+ output.unwrap();
+}
+
+#[cfg(windows)]
+#[test]
+fn should_not_panic_on_no_stderr() {
+ let output = run_deno_script_constrained(
+ util::testdata_path().join("echo.ts"),
+ WinProcConstraints::NoStdErr,
+ );
+ output.unwrap();
+}
+
+#[cfg(not(windows))]
+#[test]
+fn should_not_panic_on_undefined_home_environment_variable() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("echo.ts")
+ .env_remove("HOME")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[test]
+fn should_not_panic_on_undefined_deno_dir_environment_variable() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("echo.ts")
+ .env_remove("DENO_DIR")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[cfg(not(windows))]
+#[test]
+fn should_not_panic_on_undefined_deno_dir_and_home_environment_variables() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("echo.ts")
+ .env_remove("DENO_DIR")
+ .env_remove("HOME")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+#[test]
+fn rust_log() {
+ // Without RUST_LOG the stderr is empty.
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("run/001_hello.js")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert!(output.stderr.is_empty());
+
+ // With RUST_LOG the stderr is not empty.
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("run/001_hello.js")
+ .env("RUST_LOG", "debug")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert!(!output.stderr.is_empty());
+}
+
+#[test]
+fn dont_cache_on_check_fail() {
+ let deno_dir = util::new_deno_dir();
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check=all")
+ .arg("--reload")
+ .arg("run/error_003_typescript.ts")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert!(!output.stderr.is_empty());
+
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
+ let output = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check=all")
+ .arg("run/error_003_typescript.ts")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert!(!output.stderr.is_empty());
+}
+
+mod permissions {
+ use test_util as util;
+
+ // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
+ #[test]
+ fn with_allow() {
+ for permission in &util::PERMISSION_VARIANTS {
+ let status = util::deno_cmd()
+ .current_dir(&util::testdata_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg(format!("--allow-{0}", permission))
+ .arg("run/permission_test.ts")
+ .arg(format!("{0}Required", permission))
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ }
+ }
+
+ // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
+ #[test]
+ fn without_allow() {
+ for permission in &util::PERMISSION_VARIANTS {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ &format!(
+ "run --unstable run/permission_test.ts {0}Required",
+ permission
+ ),
+ None,
+ None,
+ false,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+ }
+
+ #[test]
+ fn rw_inside_project_dir() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ for permission in &PERMISSION_VARIANTS {
+ let status = util::deno_cmd()
+ .current_dir(&util::testdata_path())
+ .arg("run")
+ .arg(format!(
+ "--allow-{0}={1}",
+ permission,
+ util::testdata_path()
+ .into_os_string()
+ .into_string()
+ .unwrap()
+ ))
+ .arg("run/complex_permissions_test.ts")
+ .arg(permission)
+ .arg("run/complex_permissions_test.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ }
+ }
+
+ #[test]
+ fn rw_outside_test_dir() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ for permission in &PERMISSION_VARIANTS {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ &format!(
+ "run --allow-{0}={1} run/complex_permissions_test.ts {0} {2}",
+ permission,
+ util::testdata_path()
+ .into_os_string()
+ .into_string()
+ .unwrap(),
+ util::root_path()
+ .join("Cargo.toml")
+ .into_os_string()
+ .into_string()
+ .unwrap(),
+ ),
+ None,
+ None,
+ false,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+ }
+
+ #[test]
+ fn rw_inside_test_dir() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ for permission in &PERMISSION_VARIANTS {
+ let status = util::deno_cmd()
+ .current_dir(&util::testdata_path())
+ .arg("run")
+ .arg(format!(
+ "--allow-{0}={1}",
+ permission,
+ util::testdata_path()
+ .into_os_string()
+ .into_string()
+ .unwrap()
+ ))
+ .arg("run/complex_permissions_test.ts")
+ .arg(permission)
+ .arg("run/complex_permissions_test.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ }
+ }
+
+ #[test]
+ fn rw_outside_test_and_js_dir() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ let test_dir = util::testdata_path()
+ .into_os_string()
+ .into_string()
+ .unwrap();
+ let js_dir = util::root_path()
+ .join("js")
+ .into_os_string()
+ .into_string()
+ .unwrap();
+ for permission in &PERMISSION_VARIANTS {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ &format!(
+ "run --allow-{0}={1},{2} run/complex_permissions_test.ts {0} {3}",
+ permission,
+ test_dir,
+ js_dir,
+ util::root_path()
+ .join("Cargo.toml")
+ .into_os_string()
+ .into_string()
+ .unwrap(),
+ ),
+ None,
+ None,
+ false,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+ }
+
+ #[test]
+ fn rw_inside_test_and_js_dir() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ let test_dir = util::testdata_path()
+ .into_os_string()
+ .into_string()
+ .unwrap();
+ let js_dir = util::root_path()
+ .join("js")
+ .into_os_string()
+ .into_string()
+ .unwrap();
+ for permission in &PERMISSION_VARIANTS {
+ let status = util::deno_cmd()
+ .current_dir(&util::testdata_path())
+ .arg("run")
+ .arg(format!("--allow-{0}={1},{2}", permission, test_dir, js_dir))
+ .arg("run/complex_permissions_test.ts")
+ .arg(permission)
+ .arg("run/complex_permissions_test.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ }
+ }
+
+ #[test]
+ fn rw_relative() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ for permission in &PERMISSION_VARIANTS {
+ let status = util::deno_cmd()
+ .current_dir(&util::testdata_path())
+ .arg("run")
+ .arg(format!("--allow-{0}=.", permission))
+ .arg("run/complex_permissions_test.ts")
+ .arg(permission)
+ .arg("run/complex_permissions_test.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ }
+ }
+
+ #[test]
+ fn rw_no_prefix() {
+ const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
+ for permission in &PERMISSION_VARIANTS {
+ let status = util::deno_cmd()
+ .current_dir(&util::testdata_path())
+ .arg("run")
+ .arg(format!("--allow-{0}=tls/../", permission))
+ .arg("run/complex_permissions_test.ts")
+ .arg(permission)
+ .arg("run/complex_permissions_test.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ }
+ }
+
+ #[test]
+ fn net_fetch_allow_localhost_4545() {
+ let (_, err) = util::run_and_collect_output(
+ true,
+ "run --allow-net=localhost:4545 run/complex_permissions_test.ts netFetch http://localhost:4545/",
+ None,
+ None,
+ true,
+ );
+ assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_fetch_allow_deno_land() {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ "run --allow-net=deno.land run/complex_permissions_test.ts netFetch http://localhost:4545/",
+ None,
+ None,
+ true,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_fetch_localhost_4545_fail() {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ "run --allow-net=localhost:4545 run/complex_permissions_test.ts netFetch http://localhost:4546/",
+ None,
+ None,
+ true,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_fetch_localhost() {
+ let (_, err) = util::run_and_collect_output(
+ true,
+ "run --allow-net=localhost run/complex_permissions_test.ts netFetch http://localhost:4545/ http://localhost:4546/ http://localhost:4547/",
+ None,
+ None,
+ true,
+ );
+ assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_connect_allow_localhost_ip_4555() {
+ let (_, err) = util::run_and_collect_output(
+ true,
+ "run --allow-net=127.0.0.1:4545 run/complex_permissions_test.ts netConnect 127.0.0.1:4545",
+ None,
+ None,
+ true,
+ );
+ assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_connect_allow_deno_land() {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ "run --allow-net=deno.land run/complex_permissions_test.ts netConnect 127.0.0.1:4546",
+ None,
+ None,
+ true,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_connect_allow_localhost_ip_4545_fail() {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ "run --allow-net=127.0.0.1:4545 run/complex_permissions_test.ts netConnect 127.0.0.1:4546",
+ None,
+ None,
+ true,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_connect_allow_localhost_ip() {
+ let (_, err) = util::run_and_collect_output(
+ true,
+ "run --allow-net=127.0.0.1 run/complex_permissions_test.ts netConnect 127.0.0.1:4545 127.0.0.1:4546 127.0.0.1:4547",
+ None,
+ None,
+ true,
+ );
+ assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_listen_allow_localhost_4555() {
+ let (_, err) = util::run_and_collect_output(
+ true,
+ "run --allow-net=localhost:4558 run/complex_permissions_test.ts netListen localhost:4558",
+ None,
+ None,
+ false,
+ );
+ assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_listen_allow_deno_land() {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ "run --allow-net=deno.land run/complex_permissions_test.ts netListen localhost:4545",
+ None,
+ None,
+ false,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_listen_allow_localhost_4555_fail() {
+ let (_, err) = util::run_and_collect_output(
+ false,
+ "run --allow-net=localhost:4555 run/complex_permissions_test.ts netListen localhost:4556",
+ None,
+ None,
+ false,
+ );
+ assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn net_listen_allow_localhost() {
+ // Port 4600 is chosen to not colide with those used by
+ // target/debug/test_server
+ let (_, err) = util::run_and_collect_output(
+ true,
+ "run --allow-net=localhost run/complex_permissions_test.ts netListen localhost:4600",
+ None,
+ None,
+ false,
+ );
+ assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
+ }
+
+ #[test]
+ fn _061_permissions_request() {
+ let args = "run --quiet run/061_permissions_request.ts";
+ use util::PtyData::*;
+ util::test_pty2(args, vec![
+ Output("⚠️ ️Deno requests read access to \"foo\". Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)] "),
+ Input("y\n"),
+ Output("⚠️ ️Deno requests read access to \"bar\". Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
+ Input("n\n"),
+ Output("granted\r\n"),
+ Output("prompt\r\n"),
+ Output("denied\r\n"),
+ ]);
+ }
+
+ #[test]
+ fn _062_permissions_request_global() {
+ let args = "run --quiet run/062_permissions_request_global.ts";
+ use util::PtyData::*;
+ util::test_pty2(args, vec![
+ Output("⚠️ ️Deno requests read access. Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)] "),
+ Input("y\n"),
+ Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
+ Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
+ Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
+ ]);
+ }
+
+ itest!(_063_permissions_revoke {
+ args: "run --allow-read=foo,bar run/063_permissions_revoke.ts",
+ output: "run/063_permissions_revoke.ts.out",
+ });
+
+ itest!(_064_permissions_revoke_global {
+ args: "run --allow-read=foo,bar run/064_permissions_revoke_global.ts",
+ output: "run/064_permissions_revoke_global.ts.out",
+ });
+
+ #[test]
+ fn _066_prompt() {
+ let args = "run --quiet --unstable run/066_prompt.ts";
+ use util::PtyData::*;
+ util::test_pty2(
+ args,
+ vec![
+ Output("What is your name? [Jane Doe] "),
+ Input("John Doe\n"),
+ Output("Your name is John Doe.\r\n"),
+ Output("What is your name? [Jane Doe] "),
+ Input("\n"),
+ Output("Your name is Jane Doe.\r\n"),
+ Output("Prompt "),
+ Input("foo\n"),
+ Output("Your input is foo.\r\n"),
+ Output("Question 0 [y/N] "),
+ Input("Y\n"),
+ Output("Your answer is true\r\n"),
+ Output("Question 1 [y/N] "),
+ Input("N\n"),
+ Output("Your answer is false\r\n"),
+ Output("Question 2 [y/N] "),
+ Input("yes\n"),
+ Output("Your answer is false\r\n"),
+ Output("Confirm [y/N] "),
+ Input("\n"),
+ Output("Your answer is false\r\n"),
+ Output("What is Windows EOL? "),
+ Input("windows\n"),
+ Output("Your answer is \"windows\"\r\n"),
+ Output("Hi [Enter] "),
+ Input("\n"),
+ Output("Alert [Enter] "),
+ Input("\n"),
+ Output("The end of test\r\n"),
+ Output("What is EOF? "),
+ Input("\n"),
+ Output("Your answer is null\r\n"),
+ ],
+ );
+ }
+
+ itest!(dynamic_import_permissions_remote_remote {
+ args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_remote_remote.ts",
+ output: "dynamic_import/permissions_remote_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+ itest!(dynamic_import_permissions_data_remote {
+ args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_data_remote.ts",
+ output: "dynamic_import/permissions_data_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+ itest!(dynamic_import_permissions_blob_remote {
+ args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_blob_remote.ts",
+ output: "dynamic_import/permissions_blob_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+ itest!(dynamic_import_permissions_data_local {
+ args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_data_local.ts",
+ output: "dynamic_import/permissions_data_local.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+ itest!(dynamic_import_permissions_blob_local {
+ args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_blob_local.ts",
+ output: "dynamic_import/permissions_blob_local.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+}
+
+itest!(tls_starttls {
+ args: "run --quiet --reload --allow-net --allow-read --unstable --cert tls/RootCA.pem run/tls_starttls.js",
+ output: "run/tls.out",
+});
+
+itest!(tls_connecttls {
+ args: "run --quiet --reload --allow-net --allow-read --cert tls/RootCA.pem run/tls_connecttls.js",
+ output: "run/tls.out",
+});
+
+itest!(byte_order_mark {
+ args: "run --no-check run/byte_order_mark.ts",
+ output: "run/byte_order_mark.out",
+});
+
+#[test]
+fn issue9750() {
+ use util::PtyData::*;
+ util::test_pty2(
+ "run --prompt run/issue9750.js",
+ vec![
+ Output("Enter 'yy':\r\n"),
+ Input("yy\n"),
+ Output("⚠️ ️Deno requests env access. Run again with --allow-env to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
+ Input("n\n"),
+ Output("⚠️ ️Deno requests env access to \"SECRET\". Run again with --allow-env to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
+ Input("n\n"),
+ Output("error: Uncaught (in promise) PermissionDenied: Requires env access to \"SECRET\", run again with the --allow-env flag\r\n"),
+ ],
+ );
+}
+
+// Regression test for https://github.com/denoland/deno/issues/11451.
+itest!(dom_exception_formatting {
+ args: "run run/dom_exception_formatting.ts",
+ output: "run/dom_exception_formatting.ts.out",
+ exit_code: 1,
+});
+
+itest!(long_data_url_formatting {
+ args: "run run/long_data_url_formatting.ts",
+ output: "run/long_data_url_formatting.ts.out",
+ exit_code: 1,
+});
+
+itest!(eval_context_throw_dom_exception {
+ args: "run run/eval_context_throw_dom_exception.js",
+ output: "run/eval_context_throw_dom_exception.js.out",
+});
+
+#[test]
+#[cfg(unix)]
+fn navigator_language_unix() {
+ let (res, _) = util::run_and_collect_output(
+ true,
+ "run navigator_language.ts",
+ None,
+ Some(vec![("LC_ALL".to_owned(), "pl_PL".to_owned())]),
+ false,
+ );
+ assert_eq!(res, "pl-PL\n")
+}
+
+#[test]
+fn navigator_language() {
+ let (res, _) = util::run_and_collect_output(
+ true,
+ "run navigator_language.ts",
+ None,
+ None,
+ false,
+ );
+ assert!(!res.is_empty())
+}
+
+#[test]
+#[cfg(unix)]
+fn navigator_languages_unix() {
+ let (res, _) = util::run_and_collect_output(
+ true,
+ "run navigator_languages.ts",
+ None,
+ Some(vec![
+ ("LC_ALL".to_owned(), "pl_PL".to_owned()),
+ ("NO_COLOR".to_owned(), "1".to_owned()),
+ ]),
+ false,
+ );
+ assert_eq!(res, "[ \"pl-PL\" ]\n")
+}
+
+#[test]
+fn navigator_languages() {
+ let (res, _) = util::run_and_collect_output(
+ true,
+ "run navigator_languages.ts",
+ None,
+ None,
+ false,
+ );
+ assert!(!res.is_empty())
+}
+
+/// Regression test for https://github.com/denoland/deno/issues/12740.
+#[test]
+fn issue12740() {
+ let mod_dir = TempDir::new();
+ let mod1_path = mod_dir.path().join("mod1.ts");
+ let mod2_path = mod_dir.path().join("mod2.ts");
+ let mut deno_cmd = util::deno_cmd();
+ std::fs::write(&mod1_path, "").unwrap();
+ let status = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&mod1_path)
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ std::fs::write(&mod1_path, "export { foo } from \"./mod2.ts\";").unwrap();
+ std::fs::write(mod2_path, "(").unwrap();
+ let status = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(&mod1_path)
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(!status.success());
+}
+
+/// Regression test for https://github.com/denoland/deno/issues/12807.
+#[test]
+fn issue12807() {
+ let mod_dir = TempDir::new();
+ let mod1_path = mod_dir.path().join("mod1.ts");
+ let mod2_path = mod_dir.path().join("mod2.ts");
+ let mut deno_cmd = util::deno_cmd();
+ // With a fresh `DENO_DIR`, run a module with a dependency and a type error.
+ std::fs::write(&mod1_path, "import './mod2.ts'; Deno.exit('0');").unwrap();
+ std::fs::write(mod2_path, "console.log('Hello, world!');").unwrap();
+ let status = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg(&mod1_path)
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(!status.success());
+ // Fix the type error and run again.
+ std::fs::write(&mod1_path, "import './mod2.ts'; Deno.exit(0);").unwrap();
+ let status = deno_cmd
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg(&mod1_path)
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+itest!(issue_13562 {
+ args: "run run/issue13562.ts",
+ output: "run/issue13562.ts.out",
+});
+
+itest!(import_assertions_static_import {
+ args: "run --allow-read import_assertions/static_import.ts",
+ output: "import_assertions/static_import.out",
+});
+
+itest!(import_assertions_static_export {
+ args: "run --allow-read import_assertions/static_export.ts",
+ output: "import_assertions/static_export.out",
+});
+
+itest!(import_assertions_static_error {
+ args: "run --allow-read import_assertions/static_error.ts",
+ output: "import_assertions/static_error.out",
+ exit_code: 1,
+});
+
+itest!(import_assertions_dynamic_import {
+ args: "run --allow-read import_assertions/dynamic_import.ts",
+ output: "import_assertions/dynamic_import.out",
+});
+
+itest!(import_assertions_dynamic_error {
+ args: "run --allow-read import_assertions/dynamic_error.ts",
+ output: "import_assertions/dynamic_error.out",
+ exit_code: 1,
+});
+
+itest!(import_assertions_type_check {
+ args: "run --allow-read --check import_assertions/type_check.ts",
+ output: "import_assertions/type_check.out",
+ exit_code: 1,
+});
+
+itest!(delete_window {
+ args: "run run/delete_window.js",
+ output_str: Some("true\n"),
+});
+
+itest!(colors_without_global_this {
+ args: "run run/colors_without_globalThis.js",
+ output_str: Some("true\n"),
+});
+
+itest!(config_auto_discovered_for_local_script {
+ args: "run --quiet run/with_config/frontend_work.ts",
+ output_str: Some("ok\n"),
+});
+
+itest!(no_config_auto_discovery_for_local_script {
+ args: "run --quiet --no-config --check run/with_config/frontend_work.ts",
+ output: "run/with_config/no_auto_discovery.out",
+ exit_code: 1,
+});
+
+itest!(config_not_auto_discovered_for_remote_script {
+ args: "run --quiet http://127.0.0.1:4545/run/with_config/server_side_work.ts",
+ output_str: Some("ok\n"),
+ http_server: true,
+});
+
+itest!(wasm_streaming_panic_test {
+ args: "run run/wasm_streaming_panic_test.js",
+ output: "run/wasm_streaming_panic_test.js.out",
+ exit_code: 1,
+});
+
+// Regression test for https://github.com/denoland/deno/issues/13897.
+itest!(fetch_async_error_stack {
+ args: "run --quiet -A run/fetch_async_error_stack.ts",
+ output: "run/fetch_async_error_stack.ts.out",
+ exit_code: 1,
+});
+
+itest!(unstable_ffi_1 {
+ args: "run run/ffi/unstable_ffi_1.js",
+ output: "run/ffi/unstable_ffi_1.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_2 {
+ args: "run run/ffi/unstable_ffi_2.js",
+ output: "run/ffi/unstable_ffi_2.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_3 {
+ args: "run run/ffi/unstable_ffi_3.js",
+ output: "run/ffi/unstable_ffi_3.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_4 {
+ args: "run run/ffi/unstable_ffi_4.js",
+ output: "run/ffi/unstable_ffi_4.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_5 {
+ args: "run run/ffi/unstable_ffi_5.js",
+ output: "run/ffi/unstable_ffi_5.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_6 {
+ args: "run run/ffi/unstable_ffi_6.js",
+ output: "run/ffi/unstable_ffi_6.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_7 {
+ args: "run run/ffi/unstable_ffi_7.js",
+ output: "run/ffi/unstable_ffi_7.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_8 {
+ args: "run run/ffi/unstable_ffi_8.js",
+ output: "run/ffi/unstable_ffi_8.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_9 {
+ args: "run run/ffi/unstable_ffi_9.js",
+ output: "run/ffi/unstable_ffi_9.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_10 {
+ args: "run run/ffi/unstable_ffi_10.js",
+ output: "run/ffi/unstable_ffi_10.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_11 {
+ args: "run run/ffi/unstable_ffi_11.js",
+ output: "run/ffi/unstable_ffi_11.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_12 {
+ args: "run run/ffi/unstable_ffi_12.js",
+ output: "run/ffi/unstable_ffi_12.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_13 {
+ args: "run run/ffi/unstable_ffi_13.js",
+ output: "run/ffi/unstable_ffi_13.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_14 {
+ args: "run run/ffi/unstable_ffi_14.js",
+ output: "run/ffi/unstable_ffi_14.js.out",
+ exit_code: 70,
+});
+
+itest!(unstable_ffi_15 {
+ args: "run run/ffi/unstable_ffi_15.js",
+ output: "run/ffi/unstable_ffi_15.js.out",
+ exit_code: 70,
+});
+
+itest!(future_check2 {
+ args: "run --check run/future_check.ts",
+ output: "run/future_check2.out",
+});
+
+itest!(event_listener_error {
+ args: "run --quiet run/event_listener_error.ts",
+ output: "run/event_listener_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(event_listener_error_handled {
+ args: "run --quiet run/event_listener_error_handled.ts",
+ output: "run/event_listener_error_handled.ts.out",
+});
+
+// https://github.com/denoland/deno/pull/14159#issuecomment-1092285446
+itest!(event_listener_error_immediate_exit {
+ args: "run --quiet run/event_listener_error_immediate_exit.ts",
+ output: "run/event_listener_error_immediate_exit.ts.out",
+ exit_code: 1,
+});
+
+// https://github.com/denoland/deno/pull/14159#issuecomment-1092285446
+itest!(event_listener_error_immediate_exit_worker {
+ args:
+ "run --quiet --unstable -A run/event_listener_error_immediate_exit_worker.ts",
+ output: "run/event_listener_error_immediate_exit_worker.ts.out",
+ exit_code: 1,
+});
+
+itest!(set_timeout_error {
+ args: "run --quiet run/set_timeout_error.ts",
+ output: "run/set_timeout_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(set_timeout_error_handled {
+ args: "run --quiet run/set_timeout_error_handled.ts",
+ output: "run/set_timeout_error_handled.ts.out",
+});
+
+itest!(aggregate_error {
+ args: "run --quiet run/aggregate_error.ts",
+ output: "run/aggregate_error.out",
+ exit_code: 1,
+});
+
+itest!(complex_error {
+ args: "run --quiet run/complex_error.ts",
+ output: "run/complex_error.ts.out",
+ exit_code: 1,
+});
+
+// Regression test for https://github.com/denoland/deno/issues/16340.
+itest!(error_with_errors_prop {
+ args: "run --quiet run/error_with_errors_prop.js",
+ output: "run/error_with_errors_prop.js.out",
+ exit_code: 1,
+});
+
+// Regression test for https://github.com/denoland/deno/issues/12143.
+itest!(js_root_with_ts_check {
+ args: "run --quiet --check run/js_root_with_ts_check.js",
+ output: "run/js_root_with_ts_check.js.out",
+ exit_code: 1,
+});
+
+#[test]
+fn check_local_then_remote() {
+ let _http_guard = util::http_server();
+ let deno_dir = util::new_deno_dir();
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg("run/remote_type_error/main.ts")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check=all")
+ .arg("run/remote_type_error/main.ts")
+ .env("NO_COLOR", "1")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ assert_contains!(stderr, "Type 'string' is not assignable to type 'number'.");
+}
+
+// Regression test for https://github.com/denoland/deno/issues/15163
+itest!(check_js_points_to_ts {
+ args: "run --quiet --check --config run/checkjs.tsconfig.json run/check_js_points_to_ts/test.js",
+ output: "run/check_js_points_to_ts/test.js.out",
+ exit_code: 1,
+});
+
+itest!(no_prompt_flag {
+ args: "run --quiet --unstable --no-prompt run/no_prompt.ts",
+ output_str: Some(""),
+});
+
+#[test]
+fn deno_no_prompt_environment_variable() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("run/no_prompt.ts")
+ .env("DENO_NO_PROMPT", "1")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+}
+
+itest!(report_error {
+ args: "run --quiet run/report_error.ts",
+ output: "run/report_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(report_error_handled {
+ args: "run --quiet run/report_error_handled.ts",
+ output: "run/report_error_handled.ts.out",
+});
+
+// Regression test for https://github.com/denoland/deno/issues/15513.
+itest!(report_error_end_of_program {
+ args: "run --quiet run/report_error_end_of_program.ts",
+ output: "run/report_error_end_of_program.ts.out",
+ exit_code: 1,
+});
+
+itest!(queue_microtask_error {
+ args: "run --quiet run/queue_microtask_error.ts",
+ output: "run/queue_microtask_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(queue_microtask_error_handled {
+ args: "run --quiet run/queue_microtask_error_handled.ts",
+ output: "run/queue_microtask_error_handled.ts.out",
+});
+
+itest!(spawn_stdout_inherit {
+ args: "run --quiet --unstable -A run/spawn_stdout_inherit.ts",
+ output: "run/spawn_stdout_inherit.ts.out",
+});
+
+itest!(error_name_non_string {
+ args: "run --quiet run/error_name_non_string.js",
+ output: "run/error_name_non_string.js.out",
+ exit_code: 1,
+});
+
+itest!(custom_inspect_url {
+ args: "run run/custom_inspect_url.js",
+ output: "run/custom_inspect_url.js.out",
+});
+
+itest!(config_json_import {
+ args: "run --quiet -c jsx/deno-jsx.json run/config_json_import.ts",
+ output: "run/config_json_import.ts.out",
+ http_server: true,
+});
+
+#[test]
+fn running_declaration_files() {
+ let temp_dir = TempDir::new();
+ let files = vec!["file.d.ts", "file.d.cts", "file.d.mts"];
+
+ for file in files {
+ temp_dir.write(file, "");
+ let mut deno_cmd = util::deno_cmd_with_deno_dir(&temp_dir);
+ let output = deno_cmd
+ .current_dir(temp_dir.path())
+ .args(["run", file])
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ }
+}
+
+itest!(test_and_bench_are_noops_in_run {
+ args: "run run/test_and_bench_in_run.js",
+ output_str: Some(""),
+});
+
+itest!(followup_dyn_import_resolved {
+ args: "run --unstable --allow-read run/followup_dyn_import_resolves/main.ts",
+ output: "run/followup_dyn_import_resolves/main.ts.out",
+});
+
+itest!(unhandled_rejection {
+ args: "run --check run/unhandled_rejection.ts",
+ output: "run/unhandled_rejection.ts.out",
+});
+
+itest!(unhandled_rejection_sync_error {
+ args: "run --check run/unhandled_rejection_sync_error.ts",
+ output: "run/unhandled_rejection_sync_error.ts.out",
+});
+
+// Regression test for https://github.com/denoland/deno/issues/15661
+itest!(unhandled_rejection_dynamic_import {
+ args: "run --allow-read run/unhandled_rejection_dynamic_import/main.ts",
+ output: "run/unhandled_rejection_dynamic_import/main.ts.out",
+ exit_code: 1,
+});
+
+// Regression test for https://github.com/denoland/deno/issues/16909
+itest!(unhandled_rejection_dynamic_import2 {
+ args: "run --allow-read run/unhandled_rejection_dynamic_import2/main.ts",
+ output: "run/unhandled_rejection_dynamic_import2/main.ts.out",
+});
+
+itest!(nested_error {
+ args: "run run/nested_error.ts",
+ output: "run/nested_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(node_env_var_allowlist {
+ args: "run --unstable --no-prompt run/node_env_var_allowlist.ts",
+ output: "run/node_env_var_allowlist.ts.out",
+ exit_code: 1,
+});
+
+#[test]
+fn cache_test() {
+ let _g = util::http_server();
+ let deno_dir = TempDir::new();
+ let module_url =
+ url::Url::parse("http://localhost:4545/run/006_url_imports.ts").unwrap();
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("cache")
+ .arg("--check=all")
+ .arg("-L")
+ .arg("debug")
+ .arg(module_url.to_string())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+
+ let prg = util::deno_exe_path();
+ let output = Command::new(prg)
+ .env("DENO_DIR", deno_dir.path())
+ .env("HTTP_PROXY", "http://nil")
+ .env("NO_COLOR", "1")
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(module_url.to_string())
+ .output()
+ .expect("Failed to spawn script");
+
+ let str_output = std::str::from_utf8(&output.stdout).unwrap();
+
+ let module_output_path =
+ util::testdata_path().join("run/006_url_imports.ts.out");
+ let mut module_output = String::new();
+ let mut module_output_file = std::fs::File::open(module_output_path).unwrap();
+ module_output_file
+ .read_to_string(&mut module_output)
+ .unwrap();
+
+ assert_eq!(module_output, str_output);
+}
+
+#[test]
+fn cache_invalidation_test() {
+ let deno_dir = TempDir::new();
+ let fixture_path = deno_dir.path().join("fixture.ts");
+ {
+ let mut file = std::fs::File::create(fixture_path.clone())
+ .expect("could not create fixture");
+ file
+ .write_all(b"console.log(\"42\");")
+ .expect("could not write fixture");
+ }
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(fixture_path.to_str().unwrap())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+ let actual = std::str::from_utf8(&output.stdout).unwrap();
+ assert_eq!(actual, "42\n");
+ {
+ let mut file = std::fs::File::create(fixture_path.clone())
+ .expect("could not create fixture");
+ file
+ .write_all(b"console.log(\"43\");")
+ .expect("could not write fixture");
+ }
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(fixture_path.to_str().unwrap())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+ let actual = std::str::from_utf8(&output.stdout).unwrap();
+ assert_eq!(actual, "43\n");
+}
+
+#[test]
+fn cache_invalidation_test_no_check() {
+ let deno_dir = TempDir::new();
+ let fixture_path = deno_dir.path().join("fixture.ts");
+ {
+ let mut file = std::fs::File::create(fixture_path.clone())
+ .expect("could not create fixture");
+ file
+ .write_all(b"console.log(\"42\");")
+ .expect("could not write fixture");
+ }
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--no-check")
+ .arg(fixture_path.to_str().unwrap())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+ let actual = std::str::from_utf8(&output.stdout).unwrap();
+ assert_eq!(actual, "42\n");
+ {
+ let mut file = std::fs::File::create(fixture_path.clone())
+ .expect("could not create fixture");
+ file
+ .write_all(b"console.log(\"43\");")
+ .expect("could not write fixture");
+ }
+ let output = Command::new(util::deno_exe_path())
+ .env("DENO_DIR", deno_dir.path())
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--no-check")
+ .arg(fixture_path.to_str().unwrap())
+ .output()
+ .expect("Failed to spawn script");
+ assert!(output.status.success());
+ let actual = std::str::from_utf8(&output.stdout).unwrap();
+ assert_eq!(actual, "43\n");
+}
+
+#[test]
+fn ts_dependency_recompilation() {
+ let t = TempDir::new();
+ let ats = t.path().join("a.ts");
+
+ std::fs::write(
+ &ats,
+ "
+ import { foo } from \"./b.ts\";
+
+ function print(str: string): void {
+ console.log(str);
+ }
+
+ print(foo);",
+ )
+ .unwrap();
+
+ let bts = t.path().join("b.ts");
+ std::fs::write(
+ &bts,
+ "
+ export const foo = \"foo\";",
+ )
+ .unwrap();
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--check")
+ .arg(&ats)
+ .output()
+ .expect("failed to spawn script");
+
+ let stdout_output = std::str::from_utf8(&output.stdout).unwrap().trim();
+ let stderr_output = std::str::from_utf8(&output.stderr).unwrap().trim();
+
+ assert!(stdout_output.ends_with("foo"));
+ assert!(stderr_output.starts_with("Check"));
+
+ // Overwrite contents of b.ts and run again
+ std::fs::write(
+ &bts,
+ "
+ export const foo = 5;",
+ )
+ .expect("error writing file");
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--check")
+ .arg(&ats)
+ .output()
+ .expect("failed to spawn script");
+
+ let stdout_output = std::str::from_utf8(&output.stdout).unwrap().trim();
+ let stderr_output = std::str::from_utf8(&output.stderr).unwrap().trim();
+
+ // error: TS2345 [ERROR]: Argument of type '5' is not assignable to parameter of type 'string'.
+ assert!(stderr_output.contains("TS2345"));
+ assert!(!output.status.success());
+ assert!(stdout_output.is_empty());
+}
+
+#[test]
+fn basic_auth_tokens() {
+ let _g = util::http_server();
+
+ let output = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("run")
+ .arg("http://127.0.0.1:4554/run/001_hello.js")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+
+ assert!(!output.status.success());
+
+ let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
+ assert!(stdout_str.is_empty());
+
+ let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim();
+ eprintln!("{}", stderr_str);
+
+ assert!(stderr_str
+ .contains("Module not found \"http://127.0.0.1:4554/run/001_hello.js\"."));
+
+ let output = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("run")
+ .arg("http://127.0.0.1:4554/run/001_hello.js")
+ .env("DENO_AUTH_TOKENS", "testuser123:testpassabc@127.0.0.1:4554")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+
+ let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim();
+ eprintln!("{}", stderr_str);
+
+ assert!(output.status.success());
+
+ let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
+ assert_eq!(util::strip_ansi_codes(stdout_str), "Hello World");
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn test_resolve_dns() {
+ use std::net::SocketAddr;
+ use std::str::FromStr;
+ use std::sync::Arc;
+ use std::time::Duration;
+ use tokio::net::TcpListener;
+ use tokio::net::UdpSocket;
+ use tokio::sync::oneshot;
+ use trust_dns_server::authority::Catalog;
+ use trust_dns_server::authority::ZoneType;
+ use trust_dns_server::proto::rr::Name;
+ use trust_dns_server::store::in_memory::InMemoryAuthority;
+ use trust_dns_server::ServerFuture;
+
+ const DNS_PORT: u16 = 4553;
+
+ // Setup DNS server for testing
+ async fn run_dns_server(tx: oneshot::Sender<()>) {
+ let zone_file = std::fs::read_to_string(
+ util::testdata_path().join("run/resolve_dns.zone.in"),
+ )
+ .unwrap();
+ let lexer = Lexer::new(&zone_file);
+ let records = Parser::new().parse(
+ lexer,
+ Some(Name::from_str("example.com").unwrap()),
+ None,
+ );
+ if records.is_err() {
+ panic!("failed to parse: {:?}", records.err())
+ }
+ let (origin, records) = records.unwrap();
+ let authority = Box::new(Arc::new(
+ InMemoryAuthority::new(origin, records, ZoneType::Primary, false)
+ .unwrap(),
+ ));
+ let mut catalog: Catalog = Catalog::new();
+ catalog.upsert(Name::root().into(), authority);
+
+ let mut server_fut = ServerFuture::new(catalog);
+ let socket_addr = SocketAddr::from(([127, 0, 0, 1], DNS_PORT));
+ let tcp_listener = TcpListener::bind(socket_addr).await.unwrap();
+ let udp_socket = UdpSocket::bind(socket_addr).await.unwrap();
+ server_fut.register_socket(udp_socket);
+ server_fut.register_listener(tcp_listener, Duration::from_secs(2));
+
+ // Notifies that the DNS server is ready
+ tx.send(()).unwrap();
+
+ server_fut.block_until_done().await.unwrap();
+ }
+
+ let (ready_tx, ready_rx) = oneshot::channel();
+ let dns_server_fut = run_dns_server(ready_tx);
+ let handle = tokio::spawn(dns_server_fut);
+
+ // Waits for the DNS server to be ready
+ ready_rx.await.unwrap();
+
+ // Pass: `--allow-net`
+ {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--check")
+ .arg("--allow-net")
+ .arg("run/resolve_dns.ts")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ let err = String::from_utf8_lossy(&output.stderr);
+ let out = String::from_utf8_lossy(&output.stdout);
+ println!("{}", err);
+ assert!(output.status.success());
+ assert!(err.starts_with("Check file"));
+
+ let expected = std::fs::read_to_string(
+ util::testdata_path().join("run/resolve_dns.ts.out"),
+ )
+ .unwrap();
+ assert_eq!(expected, out);
+ }
+
+ // Pass: `--allow-net=127.0.0.1:4553`
+ {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--check")
+ .arg("--allow-net=127.0.0.1:4553")
+ .arg("run/resolve_dns.ts")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ let err = String::from_utf8_lossy(&output.stderr);
+ let out = String::from_utf8_lossy(&output.stdout);
+ assert!(output.status.success());
+ assert!(err.starts_with("Check file"));
+
+ let expected = std::fs::read_to_string(
+ util::testdata_path().join("run/resolve_dns.ts.out"),
+ )
+ .unwrap();
+ assert_eq!(expected, out);
+ }
+
+ // Permission error: `--allow-net=deno.land`
+ {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--check")
+ .arg("--allow-net=deno.land")
+ .arg("run/resolve_dns.ts")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ let err = String::from_utf8_lossy(&output.stderr);
+ let out = String::from_utf8_lossy(&output.stdout);
+ assert!(!output.status.success());
+ assert!(err.starts_with("Check file"));
+ assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
+ assert!(out.is_empty());
+ }
+
+ // Permission error: no permission specified
+ {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--check")
+ .arg("run/resolve_dns.ts")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ let err = String::from_utf8_lossy(&output.stderr);
+ let out = String::from_utf8_lossy(&output.stdout);
+ assert!(!output.status.success());
+ assert!(err.starts_with("Check file"));
+ assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
+ assert!(out.is_empty());
+ }
+
+ handle.abort();
+}
+
+#[tokio::test]
+async fn http2_request_url() {
+ // TLS streams require the presence of an ambient local task set to gracefully
+ // close dropped connections in the background.
+ LocalSet::new()
+ .run_until(async {
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("--quiet")
+ .arg("--allow-net")
+ .arg("--allow-read")
+ .arg("./run/http2_request_url.ts")
+ .arg("4506")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let stdout = child.stdout.as_mut().unwrap();
+ let mut buffer = [0; 5];
+ let read = stdout.read(&mut buffer).unwrap();
+ assert_eq!(read, 5);
+ let msg = std::str::from_utf8(&buffer).unwrap();
+ assert_eq!(msg, "READY");
+
+ let cert = reqwest::Certificate::from_pem(include_bytes!(
+ "../testdata/tls/RootCA.crt"
+ ))
+ .unwrap();
+
+ let client = reqwest::Client::builder()
+ .add_root_certificate(cert)
+ .http2_prior_knowledge()
+ .build()
+ .unwrap();
+
+ let res = client.get("http://127.0.0.1:4506").send().await.unwrap();
+ assert_eq!(200, res.status());
+
+ let body = res.text().await.unwrap();
+ assert_eq!(body, "http://127.0.0.1:4506/");
+
+ child.kill().unwrap();
+ child.wait().unwrap();
+ })
+ .await;
+}
+
+#[cfg(not(windows))]
+#[test]
+fn set_raw_should_not_panic_on_no_tty() {
+ let output = util::deno_cmd()
+ .arg("eval")
+ .arg("Deno.stdin.setRaw(true)")
+ // stdin set to piped so it certainly does not refer to TTY
+ .stdin(std::process::Stdio::piped())
+ // stderr is piped so we can capture output.
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let stderr = std::str::from_utf8(&output.stderr).unwrap().trim();
+ assert!(stderr.contains("BadResource"));
+}
+
+#[test]
+fn timeout_clear() {
+ // https://github.com/denoland/deno/issues/7599
+
+ use std::time::Duration;
+ use std::time::Instant;
+
+ let source_code = r#"
+const handle = setTimeout(() => {
+ console.log("timeout finish");
+}, 10000);
+clearTimeout(handle);
+console.log("finish");
+"#;
+
+ let mut p = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("-")
+ .stdin(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let stdin = p.stdin.as_mut().unwrap();
+ stdin.write_all(source_code.as_bytes()).unwrap();
+ let start = Instant::now();
+ let status = p.wait().unwrap();
+ let end = Instant::now();
+ assert!(status.success());
+ // check that program did not run for 10 seconds
+ // for timeout to clear
+ assert!(end - start < Duration::new(10, 0));
+}
+
+#[test]
+fn broken_stdout() {
+ let (reader, writer) = os_pipe::pipe().unwrap();
+ // drop the reader to create a broken pipe
+ drop(reader);
+
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("eval")
+ .arg("console.log(3.14)")
+ .stdout(writer)
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+
+ assert!(!output.status.success());
+ let stderr = std::str::from_utf8(output.stderr.as_ref()).unwrap().trim();
+ assert!(stderr.contains("Uncaught BrokenPipe"));
+ assert!(!stderr.contains("panic"));
+}
+
+itest!(error_cause {
+ args: "run run/error_cause.ts",
+ output: "run/error_cause.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_cause_recursive_aggregate {
+ args: "run error_cause_recursive_aggregate.ts",
+ output: "error_cause_recursive_aggregate.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_cause_recursive_tail {
+ args: "run error_cause_recursive_tail.ts",
+ output: "error_cause_recursive_tail.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_cause_recursive {
+ args: "run run/error_cause_recursive.ts",
+ output: "run/error_cause_recursive.ts.out",
+ exit_code: 1,
+});
+
+#[test]
+fn websocket() {
+ let _g = util::http_server();
+
+ let script = util::testdata_path().join("run/websocket_test.ts");
+ let root_ca = util::testdata_path().join("tls/RootCA.pem");
+ let status = util::deno_cmd()
+ .arg("test")
+ .arg("--unstable")
+ .arg("--allow-net")
+ .arg("--cert")
+ .arg(root_ca)
+ .arg(script)
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+
+ assert!(status.success());
+}
+
+#[ignore]
+#[test]
+fn websocketstream() {
+ let _g = util::http_server();
+
+ let script = util::testdata_path().join("run/websocketstream_test.ts");
+ let root_ca = util::testdata_path().join("tls/RootCA.pem");
+ let status = util::deno_cmd()
+ .arg("test")
+ .arg("--unstable")
+ .arg("--allow-net")
+ .arg("--cert")
+ .arg(root_ca)
+ .arg(script)
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+
+ assert!(status.success());
+}
+
+#[test]
+fn websocketstream_ping() {
+ use deno_runtime::deno_websocket::tokio_tungstenite::tungstenite;
+ let _g = util::http_server();
+
+ let script = util::testdata_path().join("run/websocketstream_ping_test.ts");
+ let root_ca = util::testdata_path().join("tls/RootCA.pem");
+ let mut child = util::deno_cmd()
+ .arg("test")
+ .arg("--unstable")
+ .arg("--allow-net")
+ .arg("--cert")
+ .arg(root_ca)
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let server = std::net::TcpListener::bind("127.0.0.1:4513").unwrap();
+ let (stream, _) = server.accept().unwrap();
+ let mut socket = tungstenite::accept(stream).unwrap();
+ socket
+ .write_message(tungstenite::Message::Text(String::from("A")))
+ .unwrap();
+ socket
+ .write_message(tungstenite::Message::Ping(vec![]))
+ .unwrap();
+ socket
+ .write_message(tungstenite::Message::Text(String::from("B")))
+ .unwrap();
+ let message = socket.read_message().unwrap();
+ assert_eq!(message, tungstenite::Message::Pong(vec![]));
+ socket
+ .write_message(tungstenite::Message::Text(String::from("C")))
+ .unwrap();
+ socket.close(None).unwrap();
+
+ assert!(child.wait().unwrap().success());
+}
+
+#[test]
+fn websocket_server_multi_field_connection_header() {
+ let script = util::testdata_path()
+ .join("run/websocket_server_multi_field_connection_header_test.ts");
+ let root_ca = util::testdata_path().join("tls/RootCA.pem");
+ let mut child = util::deno_cmd()
+ .arg("run")
+ .arg("--unstable")
+ .arg("--allow-net")
+ .arg("--cert")
+ .arg(root_ca)
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stdout = child.stdout.as_mut().unwrap();
+ let mut buffer = [0; 5];
+ let read = stdout.read(&mut buffer).unwrap();
+ assert_eq!(read, 5);
+ let msg = std::str::from_utf8(&buffer).unwrap();
+ assert_eq!(msg, "READY");
+
+ let req = http::request::Builder::new()
+ .header(http::header::CONNECTION, "keep-alive, Upgrade")
+ .uri("ws://localhost:4319")
+ .body(())
+ .unwrap();
+ let (mut socket, _) =
+ deno_runtime::deno_websocket::tokio_tungstenite::tungstenite::connect(req)
+ .unwrap();
+ let message = socket.read_message().unwrap();
+ assert_eq!(message, deno_runtime::deno_websocket::tokio_tungstenite::tungstenite::Message::Close(None));
+ socket.close(None).unwrap();
+ assert!(child.wait().unwrap().success());
+}
+
+// TODO(bartlomieju): this should use `deno run`, not `deno test`; but the
+// test hangs then. https://github.com/denoland/deno/issues/14283
+#[test]
+#[ignore]
+fn websocket_server_idletimeout() {
+ let script =
+ util::testdata_path().join("run/websocket_server_idletimeout.ts");
+ let root_ca = util::testdata_path().join("tls/RootCA.pem");
+ let mut child = util::deno_cmd()
+ .arg("test")
+ .arg("--unstable")
+ .arg("--allow-net")
+ .arg("--cert")
+ .arg(root_ca)
+ .arg(script)
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let stdout = child.stdout.as_mut().unwrap();
+ let mut buffer = [0; 5];
+ let read = stdout.read(&mut buffer).unwrap();
+ assert_eq!(read, 5);
+ let msg = std::str::from_utf8(&buffer).unwrap();
+ assert_eq!(msg, "READY");
+
+ let req = http::request::Builder::new()
+ .uri("ws://localhost:4509")
+ .body(())
+ .unwrap();
+ let (_ws, _request) =
+ deno_runtime::deno_websocket::tokio_tungstenite::tungstenite::connect(req)
+ .unwrap();
+
+ assert!(child.wait().unwrap().success());
+}
+
+itest!(auto_discover_lockfile {
+ args: "run run/auto_discover_lockfile/main.ts",
+ output: "run/auto_discover_lockfile/main.out",
+ http_server: true,
+ exit_code: 10,
+});
+
+itest!(no_lock_flag {
+ args: "run --no-lock run/no_lock_flag/main.ts",
+ output: "run/no_lock_flag/main.out",
+ http_server: true,
+ exit_code: 0,
+});
+
+itest!(config_file_lock_false {
+ args: "run --config=run/config_file_lock_boolean/false.json run/config_file_lock_boolean/main.ts",
+ output: "run/config_file_lock_boolean/false.main.out",
+ http_server: true,
+ exit_code: 0,
+});
+
+itest!(config_file_lock_true {
+ args: "run --config=run/config_file_lock_boolean/true.json run/config_file_lock_boolean/main.ts",
+ output: "run/config_file_lock_boolean/true.main.out",
+ http_server: true,
+ exit_code: 10,
+});
+
+// Check https://github.com/denoland/deno_std/issues/2882
+itest!(flash_shutdown {
+ args: "run --unstable --allow-net run/flash_shutdown/main.ts",
+ exit_code: 0,
+});
+
+itest!(permission_args {
+ args: "run run/001_hello.js --allow-net",
+ output: "run/permission_args.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(permission_args_quiet {
+ args: "run --quiet run/001_hello.js --allow-net",
+ output: "run/001_hello.js.out",
+});
+
+// Regression test for https://github.com/denoland/deno/issues/16772
+#[test]
+fn file_fetcher_preserves_permissions() {
+ let _guard = util::http_server();
+ util::with_pty(&["repl"], |mut console| {
+ console.write_text(
+ "const a = import('http://127.0.0.1:4545/run/019_media_types.ts');",
+ );
+ console.write_text("y");
+ console.write_line("");
+ console.write_line("close();");
+ let output = console.read_all_output();
+ assert_contains!(output, "success");
+ assert_contains!(output, "true");
+ });
+}
diff --git a/cli/tests/integration/task_tests.rs b/cli/tests/integration/task_tests.rs
new file mode 100644
index 000000000..47c3b6b6b
--- /dev/null
+++ b/cli/tests/integration/task_tests.rs
@@ -0,0 +1,130 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// Most of the tests for this are in deno_task_shell.
+// These tests are intended to only test integration.
+
+itest!(task_no_args {
+ args: "task -q --config task/deno.json",
+ output: "task/task_no_args.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+});
+
+itest!(task_cwd {
+ args: "task -q --config task/deno.json --cwd .. echo_cwd",
+ output: "task/task_cwd.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 0,
+});
+
+itest!(task_init_cwd {
+ args: "task -q --config task/deno.json --cwd .. echo_init_cwd",
+ output: "task/task_init_cwd.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 0,
+});
+
+itest!(task_init_cwd_already_set {
+ args: "task -q --config task/deno.json echo_init_cwd",
+ output: "task/task_init_cwd_already_set.out",
+ envs: vec![
+ ("NO_COLOR".to_string(), "1".to_string()),
+ ("INIT_CWD".to_string(), "HELLO".to_string())
+ ],
+ exit_code: 0,
+});
+
+itest!(task_cwd_resolves_config_from_specified_dir {
+ args: "task -q --cwd task",
+ output: "task/task_no_args.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+});
+
+itest!(task_non_existent {
+ args: "task --config task/deno.json non_existent",
+ output: "task/task_non_existent.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+});
+
+#[test]
+fn task_emoji() {
+ // this bug only appears when using a pty/tty
+ let args = "task --config task/deno.json echo_emoji";
+ use test_util::PtyData::*;
+ test_util::test_pty2(args, vec![Output("Task echo_emoji echo 🔥\r\n🔥")]);
+}
+
+itest!(task_boolean_logic {
+ args: "task -q --config task/deno.json boolean_logic",
+ output: "task/task_boolean_logic.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(task_exit_code_5 {
+ args: "task --config task/deno.json exit_code_5",
+ output: "task/task_exit_code_5.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 5,
+});
+
+itest!(task_additional_args {
+ args: "task -q --config task/deno.json echo 2",
+ output: "task/task_additional_args.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(task_additional_args_no_shell_expansion {
+ args_vec: vec![
+ "task",
+ "-q",
+ "--config",
+ "task/deno.json",
+ "echo",
+ "$(echo 5)"
+ ],
+ output: "task/task_additional_args_no_shell_expansion.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(task_additional_args_nested_strings {
+ args_vec: vec![
+ "task",
+ "-q",
+ "--config",
+ "task/deno.json",
+ "echo",
+ "string \"quoted string\""
+ ],
+ output: "task/task_additional_args_nested_strings.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(task_additional_args_no_logic {
+ args_vec: vec![
+ "task",
+ "-q",
+ "--config",
+ "task/deno.json",
+ "echo",
+ "||",
+ "echo",
+ "5"
+ ],
+ output: "task/task_additional_args_no_logic.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(task_deno_exe_no_env {
+ args_vec: vec!["task", "-q", "--config", "task/deno.json", "deno_echo"],
+ output: "task/task_deno_exe_no_env.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ env_clear: true,
+});
+
+itest!(task_piped_stdin {
+ args_vec: vec!["task", "-q", "--config", "task/deno.json", "piped"],
+ output: "task/task_piped_stdin.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs
new file mode 100644
index 000000000..efe50ac16
--- /dev/null
+++ b/cli/tests/integration/test_tests.rs
@@ -0,0 +1,454 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::url::Url;
+use test_util as util;
+
+#[test]
+fn no_color() {
+ let (out, _) = util::run_and_collect_output(
+ false,
+ "test test/no_color.ts",
+ None,
+ Some(vec![("NO_COLOR".to_owned(), "true".to_owned())]),
+ false,
+ );
+ // ANSI escape codes should be stripped.
+ assert!(out.contains("success ... ok"));
+ assert!(out.contains("fail ... FAILED"));
+ assert!(out.contains("ignored ... ignored"));
+ assert!(out.contains("FAILED | 1 passed | 1 failed | 1 ignored"));
+}
+
+itest!(overloads {
+ args: "test test/overloads.ts",
+ exit_code: 0,
+ output: "test/overloads.out",
+});
+
+itest!(meta {
+ args: "test test/meta.ts",
+ exit_code: 0,
+ output: "test/meta.out",
+});
+
+itest!(pass {
+ args: "test test/pass.ts",
+ exit_code: 0,
+ output: "test/pass.out",
+});
+
+itest!(ignore {
+ args: "test test/ignore.ts",
+ exit_code: 0,
+ output: "test/ignore.out",
+});
+
+itest!(ignore_permissions {
+ args: "test --unstable test/ignore_permissions.ts",
+ exit_code: 0,
+ output: "test/ignore_permissions.out",
+});
+
+itest!(fail {
+ args: "test test/fail.ts",
+ exit_code: 1,
+ output: "test/fail.out",
+});
+
+itest!(collect {
+ args: "test --ignore=test/collect/ignore test/collect",
+ exit_code: 0,
+ output: "test/collect.out",
+});
+
+itest!(test_with_config {
+ args: "test --config test/collect/deno.jsonc test/collect",
+ exit_code: 0,
+ output: "test/collect.out",
+});
+
+itest!(test_with_config2 {
+ args: "test --config test/collect/deno2.jsonc test/collect",
+ exit_code: 0,
+ output: "test/collect2.out",
+});
+
+itest!(test_with_malformed_config {
+ args: "test --config test/collect/deno.malformed.jsonc",
+ exit_code: 1,
+ output: "test/collect_with_malformed_config.out",
+});
+
+itest!(parallel_flag {
+ args: "test test/short-pass.ts --parallel",
+ exit_code: 0,
+ output: "test/short-pass.out",
+});
+
+itest!(parallel_flag_with_env_variable {
+ args: "test test/short-pass.ts --parallel",
+ envs: vec![("DENO_JOBS".to_owned(), "2".to_owned())],
+ exit_code: 0,
+ output: "test/short-pass.out",
+});
+
+itest!(jobs_flag {
+ args: "test test/short-pass.ts --jobs",
+ exit_code: 0,
+ output: "test/short-pass-jobs-flag-warning.out",
+});
+
+itest!(jobs_flag_with_numeric_value {
+ args: "test test/short-pass.ts --jobs=2",
+ exit_code: 0,
+ output: "test/short-pass-jobs-flag-warning.out",
+});
+
+itest!(load_unload {
+ args: "test test/load_unload.ts",
+ exit_code: 0,
+ output: "test/load_unload.out",
+});
+
+itest!(interval {
+ args: "test test/interval.ts",
+ exit_code: 0,
+ output: "test/interval.out",
+});
+
+itest!(doc {
+ args: "test --doc --allow-all test/doc.ts",
+ exit_code: 1,
+ output: "test/doc.out",
+});
+
+itest!(doc_only {
+ args: "test --doc --allow-all test/doc_only",
+ exit_code: 0,
+ output: "test/doc_only.out",
+});
+
+itest!(markdown {
+ args: "test --doc --allow-all test/markdown.md",
+ exit_code: 1,
+ output: "test/markdown.out",
+});
+
+itest!(markdown_windows {
+ args: "test --doc --allow-all test/markdown_windows.md",
+ exit_code: 1,
+ output: "test/markdown_windows.out",
+});
+
+itest!(markdown_full_block_names {
+ args: "test --doc --allow-all test/markdown_full_block_names.md",
+ exit_code: 1,
+ output: "test/markdown_full_block_names.out",
+});
+
+itest!(markdown_ignore_html_comment {
+ args: "test --doc --allow-all test/markdown_with_comment.md",
+ exit_code: 1,
+ output: "test/markdown_with_comment.out",
+});
+
+itest!(text {
+ args: "test --doc --allow-all test/text.md",
+ exit_code: 0,
+ output: "test/text.out",
+});
+
+itest!(quiet {
+ args: "test --quiet test/quiet.ts",
+ exit_code: 0,
+ output: "test/quiet.out",
+});
+
+itest!(fail_fast {
+ args: "test --fail-fast test/fail_fast.ts",
+ exit_code: 1,
+ output: "test/fail_fast.out",
+});
+
+itest!(only {
+ args: "test test/only.ts",
+ exit_code: 1,
+ output: "test/only.out",
+});
+
+itest!(no_check {
+ args: "test --no-check test/no_check.ts",
+ exit_code: 1,
+ output: "test/no_check.out",
+});
+
+itest!(no_run {
+ args: "test --unstable --no-run test/no_run.ts",
+ output: "test/no_run.out",
+ exit_code: 1,
+});
+
+itest!(allow_all {
+ args: "test --unstable --allow-all test/allow_all.ts",
+ exit_code: 0,
+ output: "test/allow_all.out",
+});
+
+itest!(allow_none {
+ args: "test --unstable test/allow_none.ts",
+ exit_code: 1,
+ output: "test/allow_none.out",
+});
+
+itest!(ops_sanitizer_unstable {
+ args: "test --unstable --trace-ops test/ops_sanitizer_unstable.ts",
+ exit_code: 1,
+ output: "test/ops_sanitizer_unstable.out",
+});
+
+itest!(ops_sanitizer_timeout_failure {
+ args: "test test/ops_sanitizer_timeout_failure.ts",
+ output: "test/ops_sanitizer_timeout_failure.out",
+});
+
+itest!(ops_sanitizer_multiple_timeout_tests {
+ args: "test --trace-ops test/ops_sanitizer_multiple_timeout_tests.ts",
+ exit_code: 1,
+ output: "test/ops_sanitizer_multiple_timeout_tests.out",
+});
+
+itest!(ops_sanitizer_multiple_timeout_tests_no_trace {
+ args: "test test/ops_sanitizer_multiple_timeout_tests.ts",
+ exit_code: 1,
+ output: "test/ops_sanitizer_multiple_timeout_tests_no_trace.out",
+});
+
+itest!(trace_ops_catch_error {
+ args: "test -A --trace-ops test/trace_ops_caught_error/main.ts",
+ exit_code: 0,
+ output: "test/trace_ops_caught_error/main.out",
+});
+
+// TODO(@littledivy): re-enable this test, recent optimizations made output non deterministic.
+// https://github.com/denoland/deno/issues/14268
+//
+// itest!(ops_sanitizer_missing_details {
+// args: "test --allow-write --allow-read test/ops_sanitizer_missing_details.ts",
+// exit_code: 1,
+// output: "test/ops_sanitizer_missing_details.out",
+// });
+
+itest!(ops_sanitizer_nexttick {
+ args: "test test/ops_sanitizer_nexttick.ts",
+ output: "test/ops_sanitizer_nexttick.out",
+});
+
+itest!(resource_sanitizer {
+ args: "test --allow-read test/resource_sanitizer.ts",
+ exit_code: 1,
+ output: "test/resource_sanitizer.out",
+});
+
+itest!(exit_sanitizer {
+ args: "test test/exit_sanitizer.ts",
+ output: "test/exit_sanitizer.out",
+ exit_code: 1,
+});
+
+itest!(clear_timeout {
+ args: "test test/clear_timeout.ts",
+ exit_code: 0,
+ output: "test/clear_timeout.out",
+});
+
+itest!(finally_timeout {
+ args: "test test/finally_timeout.ts",
+ exit_code: 1,
+ output: "test/finally_timeout.out",
+});
+
+itest!(unresolved_promise {
+ args: "test test/unresolved_promise.ts",
+ exit_code: 1,
+ output: "test/unresolved_promise.out",
+});
+
+itest!(unhandled_rejection {
+ args: "test test/unhandled_rejection.ts",
+ exit_code: 1,
+ output: "test/unhandled_rejection.out",
+});
+
+itest!(filter {
+ args: "test --filter=foo test/filter",
+ exit_code: 0,
+ output: "test/filter.out",
+});
+
+itest!(shuffle {
+ args: "test --shuffle test/shuffle",
+ exit_code: 0,
+ output_str: Some("[WILDCARD]"),
+});
+
+itest!(shuffle_with_seed {
+ args: "test --shuffle=42 test/shuffle",
+ exit_code: 0,
+ output: "test/shuffle.out",
+});
+
+itest!(aggregate_error {
+ args: "test --quiet test/aggregate_error.ts",
+ exit_code: 1,
+ output: "test/aggregate_error.out",
+});
+
+itest!(steps_passing_steps {
+ args: "test test/steps/passing_steps.ts",
+ exit_code: 0,
+ output: "test/steps/passing_steps.out",
+});
+
+itest!(steps_failing_steps {
+ args: "test test/steps/failing_steps.ts",
+ exit_code: 1,
+ output: "test/steps/failing_steps.out",
+});
+
+itest!(steps_ignored_steps {
+ args: "test test/steps/ignored_steps.ts",
+ exit_code: 0,
+ output: "test/steps/ignored_steps.out",
+});
+
+itest!(steps_invalid_usage {
+ args: "test test/steps/invalid_usage.ts",
+ exit_code: 1,
+ output: "test/steps/invalid_usage.out",
+});
+
+itest!(steps_output_within {
+ args: "test test/steps/output_within.ts",
+ exit_code: 0,
+ output: "test/steps/output_within.out",
+});
+
+itest!(no_prompt_by_default {
+ args: "test --quiet test/no_prompt_by_default.ts",
+ exit_code: 1,
+ output: "test/no_prompt_by_default.out",
+});
+
+itest!(no_prompt_with_denied_perms {
+ args: "test --quiet --allow-read test/no_prompt_with_denied_perms.ts",
+ exit_code: 1,
+ output: "test/no_prompt_with_denied_perms.out",
+});
+
+itest!(test_with_custom_jsx {
+ args: "test --quiet --allow-read test/hello_world.ts --config=test/deno_custom_jsx.json",
+ exit_code: 0,
+ output: "test/hello_world.out",
+});
+
+#[test]
+fn captured_output() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("--allow-run")
+ .arg("--allow-read")
+ .arg("--unstable")
+ .arg("test/captured_output.ts")
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+
+ let output_start = "------- output -------";
+ let output_end = "----- output end -----";
+ assert!(output.status.success());
+ let output_text = String::from_utf8(output.stdout).unwrap();
+ let start = output_text.find(output_start).unwrap() + output_start.len();
+ let end = output_text.find(output_end).unwrap();
+ // replace zero width space that may appear in test output due
+ // to test runner output flusher
+ let output_text = output_text[start..end]
+ .replace('\u{200B}', "")
+ .trim()
+ .to_string();
+ let mut lines = output_text.lines().collect::<Vec<_>>();
+ // the output is racy on either stdout or stderr being flushed
+ // from the runtime into the rust code, so sort it... the main
+ // thing here to ensure is that we're capturing the output in
+ // this block on stdout
+ lines.sort_unstable();
+ assert_eq!(lines.join(" "), "0 1 2 3 4 5 6 7 8 9");
+}
+
+#[test]
+fn recursive_permissions_pledge() {
+ let output = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("test/recursive_permissions_pledge.js")
+ .stderr(std::process::Stdio::piped())
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert!(String::from_utf8(output.stderr).unwrap().contains(
+ "pledge test permissions called before restoring previous pledge"
+ ));
+}
+
+#[test]
+fn file_protocol() {
+ let file_url =
+ Url::from_file_path(util::testdata_path().join("test/file_protocol.ts"))
+ .unwrap()
+ .to_string();
+
+ (util::CheckOutputIntegrationTest {
+ args_vec: vec!["test", &file_url],
+ exit_code: 0,
+ output: "test/file_protocol.out",
+ ..Default::default()
+ })
+ .run();
+}
+
+itest!(uncaught_errors {
+ args: "test --quiet test/uncaught_errors_1.ts test/uncaught_errors_2.ts test/uncaught_errors_3.ts",
+ output: "test/uncaught_errors.out",
+ exit_code: 1,
+});
+
+itest!(check_local_by_default {
+ args: "test --quiet test/check_local_by_default.ts",
+ output: "test/check_local_by_default.out",
+ http_server: true,
+});
+
+itest!(check_local_by_default2 {
+ args: "test --quiet test/check_local_by_default2.ts",
+ output: "test/check_local_by_default2.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(non_error_thrown {
+ args: "test --quiet test/non_error_thrown.ts",
+ output: "test/non_error_thrown.out",
+ exit_code: 1,
+});
+
+itest!(parallel_output {
+ args: "test --parallel --reload test/parallel_output.ts",
+ output: "test/parallel_output.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/integration/upgrade_tests.rs b/cli/tests/integration/upgrade_tests.rs
new file mode 100644
index 000000000..2822bc8de
--- /dev/null
+++ b/cli/tests/integration/upgrade_tests.rs
@@ -0,0 +1,193 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::process::{Command, Stdio};
+use test_util as util;
+use test_util::TempDir;
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_in_tmpdir() {
+ let temp_dir = TempDir::new();
+ let exe_path = temp_dir.path().join("deno");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ let status = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--force")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let _mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ // TODO(ry) assert!(mtime1 < mtime2);
+}
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_with_space_in_path() {
+ let temp_dir = TempDir::new_with_prefix("directory with spaces");
+ let exe_path = temp_dir.path().join("deno");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let status = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--force")
+ .env("TMP", temp_dir.path())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_with_version_in_tmpdir() {
+ let temp_dir = TempDir::new();
+ let exe_path = temp_dir.path().join("deno");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ let status = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--force")
+ .arg("--version")
+ .arg("1.11.5")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let upgraded_deno_version = String::from_utf8(
+ Command::new(&exe_path).arg("-V").output().unwrap().stdout,
+ )
+ .unwrap();
+ assert!(upgraded_deno_version.contains("1.11.5"));
+ let _mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ // TODO(ry) assert!(mtime1 < mtime2);
+}
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_with_canary_in_tmpdir() {
+ let temp_dir = TempDir::new();
+ let exe_path = temp_dir.path().join("deno");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ let status = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--canary")
+ .arg("--version")
+ .arg("e6685f0f01b8a11a5eaff020f5babcfde76b3038")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let upgraded_deno_version = String::from_utf8(
+ Command::new(&exe_path).arg("-V").output().unwrap().stdout,
+ )
+ .unwrap();
+ assert!(upgraded_deno_version.contains("e6685f0"));
+ let _mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ // TODO(ry) assert!(mtime1 < mtime2);
+}
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_with_out_in_tmpdir() {
+ let temp_dir = TempDir::new();
+ let exe_path = temp_dir.path().join("deno");
+ let new_exe_path = temp_dir.path().join("foo");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ let status = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--version")
+ .arg("1.11.5")
+ .arg("--output")
+ .arg(new_exe_path.to_str().unwrap())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ assert!(new_exe_path.exists());
+ let mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
+ assert_eq!(mtime1, mtime2); // Original exe_path was not changed.
+
+ let v = String::from_utf8(
+ Command::new(&new_exe_path)
+ .arg("-V")
+ .output()
+ .unwrap()
+ .stdout,
+ )
+ .unwrap();
+ assert!(v.contains("1.11.5"));
+}
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_invalid_stable_version() {
+ let temp_dir = TempDir::new();
+ let exe_path = temp_dir.path().join("deno");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let output = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--version")
+ .arg("foobar")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(
+ "error: Invalid semver passed\n",
+ util::strip_ansi_codes(&String::from_utf8(output.stderr).unwrap())
+ );
+}
+
+// Warning: this test requires internet access.
+// TODO(#7412): reenable. test is flaky
+#[test]
+#[ignore]
+fn upgrade_invalid_canary_version() {
+ let temp_dir = TempDir::new();
+ let exe_path = temp_dir.path().join("deno");
+ let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
+ assert!(exe_path.exists());
+ let output = Command::new(&exe_path)
+ .arg("upgrade")
+ .arg("--canary")
+ .arg("--version")
+ .arg("foobar")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ assert_eq!(
+ "error: Invalid commit hash passed\n",
+ util::strip_ansi_codes(&String::from_utf8(output.stderr).unwrap())
+ );
+}
diff --git a/cli/tests/integration/vendor_tests.rs b/cli/tests/integration/vendor_tests.rs
new file mode 100644
index 000000000..11fee5686
--- /dev/null
+++ b/cli/tests/integration/vendor_tests.rs
@@ -0,0 +1,574 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use pretty_assertions::assert_eq;
+use std::fmt::Write as _;
+use std::path::PathBuf;
+use std::process::Stdio;
+use test_util as util;
+use test_util::TempDir;
+use util::http_server;
+use util::new_deno_dir;
+
+#[test]
+fn output_dir_exists() {
+ let t = TempDir::new();
+ t.write("mod.ts", "");
+ t.create_dir_all("vendor");
+ t.write("vendor/mod.ts", "");
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("mod.ts")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ concat!(
+ "error: Output directory was not empty. Please specify an empty ",
+ "directory or use --force to ignore this error and potentially ",
+ "overwrite its contents.",
+ ),
+ );
+ assert!(!output.status.success());
+
+ // ensure it errors when using the `--output` arg too
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("--output")
+ .arg("vendor")
+ .arg("mod.ts")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ concat!(
+ "error: Output directory was not empty. Please specify an empty ",
+ "directory or use --force to ignore this error and potentially ",
+ "overwrite its contents.",
+ ),
+ );
+ assert!(!output.status.success());
+
+ // now use `--force`
+ let status = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("mod.ts")
+ .arg("--force")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn standard_test() {
+ let _server = http_server();
+ let t = TempDir::new();
+ let vendor_dir = t.path().join("vendor2");
+ t.write(
+ "my_app.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/query_reexport.ts?testing'; new Logger().log('outputted');",
+ );
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .arg("vendor")
+ .arg("my_app.ts")
+ .arg("--output")
+ .arg("vendor2")
+ .env("NO_COLOR", "1")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ format!(
+ concat!(
+ "Download http://localhost:4545/vendor/query_reexport.ts?testing\n",
+ "Download http://localhost:4545/vendor/logger.ts?test\n",
+ "{}",
+ ),
+ success_text("2 modules", "vendor2", true),
+ )
+ );
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
+ assert!(output.status.success());
+
+ assert!(vendor_dir.exists());
+ assert!(!t.path().join("vendor").exists());
+ let import_map: serde_json::Value =
+ serde_json::from_str(&t.read_to_string("vendor2/import_map.json")).unwrap();
+ assert_eq!(
+ import_map,
+ json!({
+ "imports": {
+ "http://localhost:4545/vendor/query_reexport.ts?testing": "./localhost_4545/vendor/query_reexport.ts",
+ "http://localhost:4545/": "./localhost_4545/",
+ },
+ "scopes": {
+ "./localhost_4545/": {
+ "./localhost_4545/vendor/logger.ts?test": "./localhost_4545/vendor/logger.ts"
+ }
+ }
+ }),
+ );
+
+ // try running the output with `--no-remote`
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--no-remote")
+ .arg("--check")
+ .arg("--quiet")
+ .arg("--import-map")
+ .arg("vendor2/import_map.json")
+ .arg("my_app.ts")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8_lossy(&output.stderr).trim(), "");
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "outputted");
+ assert!(output.status.success());
+}
+
+#[test]
+fn import_map_output_dir() {
+ let _server = http_server();
+ let t = TempDir::new();
+ t.write("mod.ts", "");
+ t.create_dir_all("vendor");
+ t.write(
+ "vendor/import_map.json",
+ // will be ignored
+ "{ \"imports\": { \"https://localhost:4545/\": \"./localhost/\" }}",
+ );
+ t.write(
+ "deno.json",
+ "{ \"import_map\": \"./vendor/import_map.json\" }",
+ );
+ t.write(
+ "my_app.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/logger.ts'; new Logger().log('outputted');",
+ );
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("--force")
+ .arg("--import-map")
+ .arg("vendor/import_map.json")
+ .arg("my_app.ts")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ format!(
+ concat!(
+ "Ignoring import map. Specifying an import map file ({}) in the deno ",
+ "vendor output directory is not supported. If you wish to use an ",
+ "import map while vendoring, please specify one located outside this ",
+ "directory.\n",
+ "Download http://localhost:4545/vendor/logger.ts\n",
+ "{}",
+ ),
+ PathBuf::from("vendor").join("import_map.json").display(),
+ success_text_updated_deno_json("1 module", "vendor/"),
+ )
+ );
+ assert!(output.status.success());
+}
+
+#[test]
+fn remote_module_test() {
+ let _server = http_server();
+ let t = TempDir::new();
+ let vendor_dir = t.path().join("vendor");
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("http://localhost:4545/vendor/query_reexport.ts")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ format!(
+ concat!(
+ "Download http://localhost:4545/vendor/query_reexport.ts\n",
+ "Download http://localhost:4545/vendor/logger.ts?test\n",
+ "{}",
+ ),
+ success_text("2 modules", "vendor/", true),
+ )
+ );
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
+ assert!(output.status.success());
+ assert!(vendor_dir.exists());
+ assert!(vendor_dir
+ .join("localhost_4545/vendor/query_reexport.ts")
+ .exists());
+ assert!(vendor_dir.join("localhost_4545/vendor/logger.ts").exists());
+ let import_map: serde_json::Value =
+ serde_json::from_str(&t.read_to_string("vendor/import_map.json")).unwrap();
+ assert_eq!(
+ import_map,
+ json!({
+ "imports": {
+ "http://localhost:4545/": "./localhost_4545/",
+ },
+ "scopes": {
+ "./localhost_4545/": {
+ "./localhost_4545/vendor/logger.ts?test": "./localhost_4545/vendor/logger.ts",
+ }
+ }
+ }),
+ );
+}
+
+#[test]
+fn existing_import_map_no_remote() {
+ let _server = http_server();
+ let t = TempDir::new();
+ t.write(
+ "mod.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/logger.ts';",
+ );
+ let import_map_filename = "imports2.json";
+ let import_map_text =
+ r#"{ "imports": { "http://localhost:4545/vendor/": "./logger/" } }"#;
+ t.write(import_map_filename, import_map_text);
+ t.create_dir_all("logger");
+ t.write("logger/logger.ts", "export class Logger {}");
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("mod.ts")
+ .arg("--import-map")
+ .arg(import_map_filename)
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ success_text("0 modules", "vendor/", false)
+ );
+ assert!(output.status.success());
+ // it should not have found any remote dependencies because
+ // the provided import map mapped it to a local directory
+ assert_eq!(t.read_to_string(import_map_filename), import_map_text);
+}
+
+#[test]
+fn existing_import_map_mixed_with_remote() {
+ let _server = http_server();
+ let deno_dir = new_deno_dir();
+ let t = TempDir::new();
+ t.write(
+ "mod.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/logger.ts';",
+ );
+
+ let status = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(t.path())
+ .arg("vendor")
+ .arg("mod.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+
+ assert_eq!(
+ t.read_to_string("vendor/import_map.json"),
+ r#"{
+ "imports": {
+ "http://localhost:4545/": "./localhost_4545/"
+ }
+}
+"#,
+ );
+
+ // make the import map specific to support vendoring mod.ts in the next step
+ t.write(
+ "vendor/import_map.json",
+ r#"{
+ "imports": {
+ "http://localhost:4545/vendor/logger.ts": "./localhost_4545/vendor/logger.ts"
+ }
+}
+"#,
+ );
+
+ t.write(
+ "mod.ts",
+ concat!(
+ "import {Logger} from 'http://localhost:4545/vendor/logger.ts';\n",
+ "import {Logger as OtherLogger} from 'http://localhost:4545/vendor/mod.ts';\n",
+ ),
+ );
+
+ // now vendor with the existing import map in a separate vendor directory
+ let deno = util::deno_cmd_with_deno_dir(&deno_dir)
+ .env("NO_COLOR", "1")
+ .current_dir(t.path())
+ .arg("vendor")
+ .arg("mod.ts")
+ .arg("--import-map")
+ .arg("vendor/import_map.json")
+ .arg("--output")
+ .arg("vendor2")
+ .stderr(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ format!(
+ concat!("Download http://localhost:4545/vendor/mod.ts\n", "{}",),
+ success_text("1 module", "vendor2", true),
+ )
+ );
+ assert!(output.status.success());
+
+ // tricky scenario here where the output directory now contains a mapping
+ // back to the previous vendor location
+ assert_eq!(
+ t.read_to_string("vendor2/import_map.json"),
+ r#"{
+ "imports": {
+ "http://localhost:4545/vendor/logger.ts": "../vendor/localhost_4545/vendor/logger.ts",
+ "http://localhost:4545/": "./localhost_4545/"
+ },
+ "scopes": {
+ "./localhost_4545/": {
+ "./localhost_4545/vendor/logger.ts": "../vendor/localhost_4545/vendor/logger.ts"
+ }
+ }
+}
+"#,
+ );
+
+ // ensure it runs
+ let status = util::deno_cmd()
+ .current_dir(t.path())
+ .arg("run")
+ .arg("--check")
+ .arg("--no-remote")
+ .arg("--import-map")
+ .arg("vendor2/import_map.json")
+ .arg("mod.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+}
+
+#[test]
+fn dynamic_import() {
+ let _server = http_server();
+ let t = TempDir::new();
+ t.write(
+ "mod.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/dynamic.ts'; new Logger().log('outputted');",
+ );
+
+ let status = util::deno_cmd()
+ .current_dir(t.path())
+ .arg("vendor")
+ .arg("mod.ts")
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ assert!(status.success());
+ let import_map: serde_json::Value =
+ serde_json::from_str(&t.read_to_string("vendor/import_map.json")).unwrap();
+ assert_eq!(
+ import_map,
+ json!({
+ "imports": {
+ "http://localhost:4545/": "./localhost_4545/",
+ }
+ }),
+ );
+
+ // try running the output with `--no-remote`
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--allow-read=.")
+ .arg("--no-remote")
+ .arg("--check")
+ .arg("--quiet")
+ .arg("--import-map")
+ .arg("vendor/import_map.json")
+ .arg("mod.ts")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8_lossy(&output.stderr).trim(), "");
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "outputted");
+ assert!(output.status.success());
+}
+
+#[test]
+fn dynamic_non_analyzable_import() {
+ let _server = http_server();
+ let t = TempDir::new();
+ t.write(
+ "mod.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/dynamic_non_analyzable.ts'; new Logger().log('outputted');",
+ );
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("vendor")
+ .arg("--reload")
+ .arg("mod.ts")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ // todo(https://github.com/denoland/deno_graph/issues/138): it should warn about
+ // how it couldn't analyze the dynamic import
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ format!(
+ "Download http://localhost:4545/vendor/dynamic_non_analyzable.ts\n{}",
+ success_text("1 module", "vendor/", true),
+ )
+ );
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
+ assert!(output.status.success());
+}
+
+#[test]
+fn update_existing_config_test() {
+ let _server = http_server();
+ let t = TempDir::new();
+ t.write(
+ "my_app.ts",
+ "import {Logger} from 'http://localhost:4545/vendor/logger.ts'; new Logger().log('outputted');",
+ );
+ t.write("deno.json", "{\n}");
+
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .arg("vendor")
+ .arg("my_app.ts")
+ .arg("--output")
+ .arg("vendor2")
+ .env("NO_COLOR", "1")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(
+ String::from_utf8_lossy(&output.stderr).trim(),
+ format!(
+ "Download http://localhost:4545/vendor/logger.ts\n{}",
+ success_text_updated_deno_json("1 module", "vendor2",)
+ )
+ );
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
+ assert!(output.status.success());
+
+ // try running the output with `--no-remote` and not specifying a `--vendor`
+ let deno = util::deno_cmd()
+ .current_dir(t.path())
+ .env("NO_COLOR", "1")
+ .arg("run")
+ .arg("--no-remote")
+ .arg("--check")
+ .arg("--quiet")
+ .arg("my_app.ts")
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let output = deno.wait_with_output().unwrap();
+ assert_eq!(String::from_utf8_lossy(&output.stderr).trim(), "");
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "outputted");
+ assert!(output.status.success());
+}
+
+fn success_text(module_count: &str, dir: &str, has_import_map: bool) -> String {
+ let mut text = format!("Vendored {} into {} directory.", module_count, dir);
+ if has_import_map {
+ let f = format!(
+ concat!(
+ "\n\nTo use vendored modules, specify the `--import-map {}import_map.json` flag when ",
+ r#"invoking Deno subcommands or add an `"importMap": "<path_to_vendored_import_map>"` "#,
+ "entry to a deno.json file.",
+ ),
+ if dir != "vendor/" {
+ format!("{}{}", dir.trim_end_matches('/'), if cfg!(windows) { '\\' } else {'/'})
+ } else {
+ dir.to_string()
+ }
+ );
+ write!(text, "{}", f).unwrap();
+ }
+ text
+}
+
+fn success_text_updated_deno_json(module_count: &str, dir: &str) -> String {
+ format!(
+ concat!(
+ "Vendored {} into {} directory.\n\n",
+ "Updated your local Deno configuration file with a reference to the ",
+ "new vendored import map at {}import_map.json. Invoking Deno subcommands will ",
+ "now automatically resolve using the vendored modules. You may override ",
+ "this by providing the `--import-map <other-import-map>` flag or by ",
+ "manually editing your Deno configuration file.",
+ ),
+ module_count,
+ dir,
+ if dir != "vendor/" {
+ format!(
+ "{}{}",
+ dir.trim_end_matches('/'),
+ if cfg!(windows) { '\\' } else { '/' }
+ )
+ } else {
+ dir.to_string()
+ }
+ )
+}
diff --git a/cli/tests/integration/watcher_tests.rs b/cli/tests/integration/watcher_tests.rs
new file mode 100644
index 000000000..375bf6804
--- /dev/null
+++ b/cli/tests/integration/watcher_tests.rs
@@ -0,0 +1,1217 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use flaky_test::flaky_test;
+use std::fs::write;
+use std::io::BufRead;
+use test_util as util;
+use test_util::assert_contains;
+use test_util::TempDir;
+
+use util::assert_not_contains;
+
+const CLEAR_SCREEN: &str = r#"[2J"#;
+
+// Helper function to skip watcher output that contains "Restarting"
+// phrase.
+fn skip_restarting_line(
+ stderr_lines: &mut impl Iterator<Item = String>,
+) -> String {
+ loop {
+ let msg = stderr_lines.next().unwrap();
+ if !msg.contains("Restarting") {
+ return msg;
+ }
+ }
+}
+
+fn read_all_lints(stderr_lines: &mut impl Iterator<Item = String>) -> String {
+ let mut str = String::new();
+ for t in stderr_lines {
+ let t = util::strip_ansi_codes(&t);
+ if t.starts_with("Watcher File change detected") {
+ continue;
+ }
+ if t.starts_with("Watcher") {
+ break;
+ }
+ if t.starts_with('(') {
+ str.push_str(&t);
+ str.push('\n');
+ }
+ }
+ str
+}
+
+fn wait_for(
+ condition: impl Fn(&str) -> bool,
+ lines: &mut impl Iterator<Item = String>,
+) {
+ loop {
+ let msg = lines.next().unwrap();
+ if condition(&msg) {
+ break;
+ }
+ }
+}
+
+fn wait_contains(s: &str, lines: &mut impl Iterator<Item = String>) {
+ wait_for(|msg| msg.contains(s), lines)
+}
+
+fn read_line(s: &str, lines: &mut impl Iterator<Item = String>) -> String {
+ lines.find(|m| m.contains(s)).unwrap()
+}
+
+fn check_alive_then_kill(mut child: std::process::Child) {
+ assert!(child.try_wait().unwrap().is_none());
+ child.kill().unwrap();
+}
+
+fn child_lines(
+ child: &mut std::process::Child,
+) -> (impl Iterator<Item = String>, impl Iterator<Item = String>) {
+ let stdout_lines = std::io::BufReader::new(child.stdout.take().unwrap())
+ .lines()
+ .map(|r| {
+ let line = r.unwrap();
+ eprintln!("STDOUT: {}", line);
+ line
+ });
+ let stderr_lines = std::io::BufReader::new(child.stderr.take().unwrap())
+ .lines()
+ .map(|r| {
+ let line = r.unwrap();
+ eprintln!("STDERR: {}", line);
+ line
+ });
+ (stdout_lines, stderr_lines)
+}
+
+#[test]
+fn lint_watch_test() {
+ let t = TempDir::new();
+ let badly_linted_original =
+ util::testdata_path().join("lint/watch/badly_linted.js");
+ let badly_linted_output =
+ util::testdata_path().join("lint/watch/badly_linted.js.out");
+ let badly_linted_fixed1 =
+ util::testdata_path().join("lint/watch/badly_linted_fixed1.js");
+ let badly_linted_fixed1_output =
+ util::testdata_path().join("lint/watch/badly_linted_fixed1.js.out");
+ let badly_linted_fixed2 =
+ util::testdata_path().join("lint/watch/badly_linted_fixed2.js");
+ let badly_linted_fixed2_output =
+ util::testdata_path().join("lint/watch/badly_linted_fixed2.js.out");
+ let badly_linted = t.path().join("badly_linted.js");
+
+ std::fs::copy(badly_linted_original, &badly_linted).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("lint")
+ .arg(&badly_linted)
+ .arg("--watch")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Lint started");
+ let mut output = read_all_lints(&mut stderr_lines);
+ let expected = std::fs::read_to_string(badly_linted_output).unwrap();
+ assert_eq!(output, expected);
+
+ // Change content of the file again to be badly-linted
+ std::fs::copy(badly_linted_fixed1, &badly_linted).unwrap();
+ std::thread::sleep(std::time::Duration::from_secs(1));
+
+ output = read_all_lints(&mut stderr_lines);
+ let expected = std::fs::read_to_string(badly_linted_fixed1_output).unwrap();
+ assert_eq!(output, expected);
+
+ // Change content of the file again to be badly-linted
+ std::fs::copy(badly_linted_fixed2, &badly_linted).unwrap();
+
+ output = read_all_lints(&mut stderr_lines);
+ let expected = std::fs::read_to_string(badly_linted_fixed2_output).unwrap();
+ assert_eq!(output, expected);
+
+ // the watcher process is still alive
+ assert!(child.try_wait().unwrap().is_none());
+
+ child.kill().unwrap();
+ drop(t);
+}
+
+#[test]
+fn lint_watch_without_args_test() {
+ let t = TempDir::new();
+ let badly_linted_original =
+ util::testdata_path().join("lint/watch/badly_linted.js");
+ let badly_linted_output =
+ util::testdata_path().join("lint/watch/badly_linted.js.out");
+ let badly_linted_fixed1 =
+ util::testdata_path().join("lint/watch/badly_linted_fixed1.js");
+ let badly_linted_fixed1_output =
+ util::testdata_path().join("lint/watch/badly_linted_fixed1.js.out");
+ let badly_linted_fixed2 =
+ util::testdata_path().join("lint/watch/badly_linted_fixed2.js");
+ let badly_linted_fixed2_output =
+ util::testdata_path().join("lint/watch/badly_linted_fixed2.js.out");
+ let badly_linted = t.path().join("badly_linted.js");
+
+ std::fs::copy(badly_linted_original, &badly_linted).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(t.path())
+ .arg("lint")
+ .arg("--watch")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Lint started");
+ let mut output = read_all_lints(&mut stderr_lines);
+ let expected = std::fs::read_to_string(badly_linted_output).unwrap();
+ assert_eq!(output, expected);
+
+ // Change content of the file again to be badly-linted
+ std::fs::copy(badly_linted_fixed1, &badly_linted).unwrap();
+
+ output = read_all_lints(&mut stderr_lines);
+ let expected = std::fs::read_to_string(badly_linted_fixed1_output).unwrap();
+ assert_eq!(output, expected);
+
+ // Change content of the file again to be badly-linted
+ std::fs::copy(badly_linted_fixed2, &badly_linted).unwrap();
+ std::thread::sleep(std::time::Duration::from_secs(1));
+
+ output = read_all_lints(&mut stderr_lines);
+ let expected = std::fs::read_to_string(badly_linted_fixed2_output).unwrap();
+ assert_eq!(output, expected);
+
+ // the watcher process is still alive
+ assert!(child.try_wait().unwrap().is_none());
+
+ child.kill().unwrap();
+ drop(t);
+}
+
+#[test]
+fn lint_all_files_on_each_change_test() {
+ let t = TempDir::new();
+ let badly_linted_fixed0 =
+ util::testdata_path().join("lint/watch/badly_linted.js");
+ let badly_linted_fixed1 =
+ util::testdata_path().join("lint/watch/badly_linted_fixed1.js");
+ let badly_linted_fixed2 =
+ util::testdata_path().join("lint/watch/badly_linted_fixed2.js");
+
+ let badly_linted_1 = t.path().join("badly_linted_1.js");
+ let badly_linted_2 = t.path().join("badly_linted_2.js");
+ std::fs::copy(badly_linted_fixed0, badly_linted_1).unwrap();
+ std::fs::copy(badly_linted_fixed1, &badly_linted_2).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("lint")
+ .arg(t.path())
+ .arg("--watch")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 2 files");
+
+ std::fs::copy(badly_linted_fixed2, badly_linted_2).unwrap();
+
+ assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 2 files");
+
+ assert!(child.try_wait().unwrap().is_none());
+
+ child.kill().unwrap();
+ drop(t);
+}
+
+#[test]
+fn fmt_watch_test() {
+ let fmt_testdata_path = util::testdata_path().join("fmt");
+ let t = TempDir::new();
+ let fixed = fmt_testdata_path.join("badly_formatted_fixed.js");
+ let badly_formatted_original = fmt_testdata_path.join("badly_formatted.mjs");
+ let badly_formatted = t.path().join("badly_formatted.js");
+ std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(&fmt_testdata_path)
+ .arg("fmt")
+ .arg(&badly_formatted)
+ .arg("--watch")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Fmt started");
+ assert_contains!(
+ skip_restarting_line(&mut stderr_lines),
+ "badly_formatted.js"
+ );
+ assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
+
+ let expected = std::fs::read_to_string(fixed.clone()).unwrap();
+ let actual = std::fs::read_to_string(badly_formatted.clone()).unwrap();
+ assert_eq!(actual, expected);
+
+ // Change content of the file again to be badly formatted
+ std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
+
+ assert_contains!(
+ skip_restarting_line(&mut stderr_lines),
+ "badly_formatted.js"
+ );
+ assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
+
+ // Check if file has been automatically formatted by watcher
+ let expected = std::fs::read_to_string(fixed).unwrap();
+ let actual = std::fs::read_to_string(badly_formatted).unwrap();
+ assert_eq!(actual, expected);
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn fmt_watch_without_args_test() {
+ let fmt_testdata_path = util::testdata_path().join("fmt");
+ let t = TempDir::new();
+ let fixed = fmt_testdata_path.join("badly_formatted_fixed.js");
+ let badly_formatted_original = fmt_testdata_path.join("badly_formatted.mjs");
+ let badly_formatted = t.path().join("badly_formatted.js");
+ std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(t.path())
+ .arg("fmt")
+ .arg("--watch")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Fmt started");
+ assert_contains!(
+ skip_restarting_line(&mut stderr_lines),
+ "badly_formatted.js"
+ );
+ assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
+
+ let expected = std::fs::read_to_string(fixed.clone()).unwrap();
+ let actual = std::fs::read_to_string(badly_formatted.clone()).unwrap();
+ assert_eq!(actual, expected);
+
+ // Change content of the file again to be badly formatted
+ std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
+ assert_contains!(
+ skip_restarting_line(&mut stderr_lines),
+ "badly_formatted.js"
+ );
+ assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
+
+ // Check if file has been automatically formatted by watcher
+ let expected = std::fs::read_to_string(fixed).unwrap();
+ let actual = std::fs::read_to_string(badly_formatted).unwrap();
+ assert_eq!(actual, expected);
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn fmt_check_all_files_on_each_change_test() {
+ let t = TempDir::new();
+ let fmt_testdata_path = util::testdata_path().join("fmt");
+ let badly_formatted_original = fmt_testdata_path.join("badly_formatted.mjs");
+ let badly_formatted_1 = t.path().join("badly_formatted_1.js");
+ let badly_formatted_2 = t.path().join("badly_formatted_2.js");
+ std::fs::copy(&badly_formatted_original, &badly_formatted_1).unwrap();
+ std::fs::copy(&badly_formatted_original, badly_formatted_2).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(&fmt_testdata_path)
+ .arg("fmt")
+ .arg(t.path())
+ .arg("--watch")
+ .arg("--check")
+ .arg("--unstable")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ assert_contains!(
+ read_line("error", &mut stderr_lines),
+ "Found 2 not formatted files in 2 files"
+ );
+
+ // Change content of the file again to be badly formatted
+ std::fs::copy(&badly_formatted_original, &badly_formatted_1).unwrap();
+
+ assert_contains!(
+ read_line("error", &mut stderr_lines),
+ "Found 2 not formatted files in 2 files"
+ );
+
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn bundle_js_watch() {
+ use std::path::PathBuf;
+ // Test strategy extends this of test bundle_js by adding watcher
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.ts");
+ write(&file_to_watch, "console.log('Hello world');").unwrap();
+ assert!(file_to_watch.is_file());
+ let t = TempDir::new();
+ let bundle = t.path().join("mod6.bundle.js");
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(&file_to_watch)
+ .arg(&bundle)
+ .arg("--watch")
+ .arg("--unstable")
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
+
+ assert_contains!(stderr_lines.next().unwrap(), "Check");
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Bundle started");
+ assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
+ assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
+ let file = PathBuf::from(&bundle);
+ assert!(file.is_file());
+ wait_contains("Bundle finished", &mut stderr_lines);
+
+ write(&file_to_watch, "console.log('Hello world2');").unwrap();
+
+ assert_contains!(stderr_lines.next().unwrap(), "Check");
+ let next_line = stderr_lines.next().unwrap();
+ // Should not clear screen, as we are in non-TTY environment
+ assert_not_contains!(&next_line, CLEAR_SCREEN);
+ assert_contains!(&next_line, "File change detected!");
+ assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
+ assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
+ let file = PathBuf::from(&bundle);
+ assert!(file.is_file());
+ wait_contains("Bundle finished", &mut stderr_lines);
+
+ // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
+ write(&file_to_watch, "syntax error ^^").unwrap();
+
+ assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
+ assert_contains!(stderr_lines.next().unwrap(), "error: ");
+ wait_contains("Bundle failed", &mut stderr_lines);
+ check_alive_then_kill(deno);
+}
+
+/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt
+#[test]
+fn bundle_watch_not_exit() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.ts");
+ write(&file_to_watch, "syntax error ^^").unwrap();
+ let target_file = t.path().join("target.js");
+
+ let mut deno = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("bundle")
+ .arg(&file_to_watch)
+ .arg(&target_file)
+ .arg("--watch")
+ .arg("--unstable")
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
+
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Bundle started");
+ assert_contains!(stderr_lines.next().unwrap(), "error:");
+ assert_eq!(stderr_lines.next().unwrap(), "");
+ assert_eq!(stderr_lines.next().unwrap(), " syntax error ^^");
+ assert_eq!(stderr_lines.next().unwrap(), " ~~~~~");
+ assert_contains!(stderr_lines.next().unwrap(), "Bundle failed");
+ // the target file hasn't been created yet
+ assert!(!target_file.is_file());
+
+ // Make sure the watcher actually restarts and works fine with the proper syntax
+ write(&file_to_watch, "console.log(42);").unwrap();
+
+ assert_contains!(stderr_lines.next().unwrap(), "Check");
+ let next_line = stderr_lines.next().unwrap();
+ // Should not clear screen, as we are in non-TTY environment
+ assert_not_contains!(&next_line, CLEAR_SCREEN);
+ assert_contains!(&next_line, "File change detected!");
+ assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
+ assert_contains!(stderr_lines.next().unwrap(), "target.js");
+
+ wait_contains("Bundle finished", &mut stderr_lines);
+
+ // bundled file is created
+ assert!(target_file.is_file());
+ check_alive_then_kill(deno);
+}
+
+#[test]
+fn run_watch_no_dynamic() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(&file_to_watch, "console.log('Hello world');").unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg("--unstable")
+ .arg("-L")
+ .arg("debug")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ wait_contains("Hello world", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
+
+ // Change content of the file
+ write(&file_to_watch, "console.log('Hello world2');").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("Hello world2", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
+
+ // Add dependency
+ let another_file = t.path().join("another_file.js");
+ write(&another_file, "export const foo = 0;").unwrap();
+ write(
+ &file_to_watch,
+ "import { foo } from './another_file.js'; console.log(foo);",
+ )
+ .unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("0", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("another_file.js"),
+ &mut stderr_lines,
+ );
+
+ // Confirm that restarting occurs when a new file is updated
+ write(&another_file, "export const foo = 42;").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("42", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
+
+ // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
+ write(&file_to_watch, "syntax error ^^").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("error:", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
+
+ // Then restore the file
+ write(
+ &file_to_watch,
+ "import { foo } from './another_file.js'; console.log(foo);",
+ )
+ .unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("42", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("another_file.js"),
+ &mut stderr_lines,
+ );
+
+ // Update the content of the imported file with invalid syntax
+ write(&another_file, "syntax error ^^").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("error:", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("another_file.js"),
+ &mut stderr_lines,
+ );
+
+ // Modify the imported file and make sure that restarting occurs
+ write(&another_file, "export const foo = 'modified!';").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("modified!", &mut stdout_lines);
+ wait_contains("Watching paths", &mut stderr_lines);
+ check_alive_then_kill(child);
+}
+
+// TODO(bartlomieju): this test became flaky on macOS runner; it is unclear
+// if that's because of a bug in code or the runner itself. We should reenable
+// it once we upgrade to XL runners for macOS.
+#[cfg(not(target_os = "macos"))]
+#[test]
+fn run_watch_external_watch_files() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(&file_to_watch, "console.log('Hello world');").unwrap();
+
+ let external_file_to_watch = t.path().join("external_file_to_watch.txt");
+ write(&external_file_to_watch, "Hello world").unwrap();
+
+ let mut watch_arg = "--watch=".to_owned();
+ let external_file_to_watch_str = external_file_to_watch
+ .clone()
+ .into_os_string()
+ .into_string()
+ .unwrap();
+ watch_arg.push_str(&external_file_to_watch_str);
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg(watch_arg)
+ .arg("-L")
+ .arg("debug")
+ .arg("--unstable")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+ wait_contains("Process started", &mut stderr_lines);
+ wait_contains("Hello world", &mut stdout_lines);
+ wait_for(
+ |m| {
+ m.contains("Watching paths") && m.contains("external_file_to_watch.txt")
+ },
+ &mut stderr_lines,
+ );
+
+ // Change content of the external file
+ write(&external_file_to_watch, "Hello world2").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("Process finished", &mut stderr_lines);
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn run_watch_load_unload_events() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(
+ &file_to_watch,
+ r#"
+ setInterval(() => {}, 0);
+ window.addEventListener("load", () => {
+ console.log("load");
+ });
+
+ window.addEventListener("unload", () => {
+ console.log("unload");
+ });
+ "#,
+ )
+ .unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg("--unstable")
+ .arg("-L")
+ .arg("debug")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ // Wait for the first load event to fire
+ wait_contains("load", &mut stdout_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
+
+ // Change content of the file, this time without an interval to keep it alive.
+ write(
+ &file_to_watch,
+ r#"
+ window.addEventListener("load", () => {
+ console.log("load");
+ });
+
+ window.addEventListener("unload", () => {
+ console.log("unload");
+ });
+ "#,
+ )
+ .unwrap();
+
+ // Wait for the restart
+ wait_contains("Restarting", &mut stderr_lines);
+
+ // Confirm that the unload event was dispatched from the first run
+ wait_contains("unload", &mut stdout_lines);
+
+ // Followed by the load event of the second run
+ wait_contains("load", &mut stdout_lines);
+
+ // Which is then unloaded as there is nothing keeping it alive.
+ wait_contains("unload", &mut stdout_lines);
+ check_alive_then_kill(child);
+}
+
+/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt
+#[test]
+fn run_watch_not_exit() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(&file_to_watch, "syntax error ^^").unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg("--unstable")
+ .arg("-L")
+ .arg("debug")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ wait_contains("Process started", &mut stderr_lines);
+ wait_contains("error:", &mut stderr_lines);
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
+ &mut stderr_lines,
+ );
+
+ // Make sure the watcher actually restarts and works fine with the proper syntax
+ write(&file_to_watch, "console.log(42);").unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains("42", &mut stdout_lines);
+ wait_contains("Process finished", &mut stderr_lines);
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn run_watch_with_import_map_and_relative_paths() {
+ fn create_relative_tmp_file(
+ directory: &TempDir,
+ filename: &'static str,
+ filecontent: &'static str,
+ ) -> std::path::PathBuf {
+ let absolute_path = directory.path().join(filename);
+ write(&absolute_path, filecontent).unwrap();
+ let relative_path = absolute_path
+ .strip_prefix(util::testdata_path())
+ .unwrap()
+ .to_owned();
+ assert!(relative_path.is_relative());
+ relative_path
+ }
+ let temp_directory = TempDir::new_in(&util::testdata_path());
+ let file_to_watch = create_relative_tmp_file(
+ &temp_directory,
+ "file_to_watch.js",
+ "console.log('Hello world');",
+ );
+ let import_map_path = create_relative_tmp_file(
+ &temp_directory,
+ "import_map.json",
+ "{\"imports\": {}}",
+ );
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("--watch")
+ .arg("--import-map")
+ .arg(&import_map_path)
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+ let next_line = stderr_lines.next().unwrap();
+ assert_contains!(&next_line, "Process started");
+ assert_contains!(stderr_lines.next().unwrap(), "Process finished");
+ assert_contains!(stdout_lines.next().unwrap(), "Hello world");
+
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn run_watch_error_messages() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(
+ &file_to_watch,
+ "throw SyntaxError(`outer`, {cause: TypeError(`inner`)})",
+ )
+ .unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_, mut stderr_lines) = child_lines(&mut child);
+
+ wait_contains("Process started", &mut stderr_lines);
+ wait_contains("error: Uncaught SyntaxError: outer", &mut stderr_lines);
+ wait_contains("Caused by: TypeError: inner", &mut stderr_lines);
+ wait_contains("Process finished", &mut stderr_lines);
+
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn test_watch() {
+ let t = TempDir::new();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("--watch")
+ .arg("--unstable")
+ .arg("--no-check")
+ .arg(t.path())
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ assert_eq!(stdout_lines.next().unwrap(), "");
+ assert_contains!(stdout_lines.next().unwrap(), "0 passed | 0 failed");
+ wait_contains("Test finished", &mut stderr_lines);
+
+ let foo_file = t.path().join("foo.js");
+ let bar_file = t.path().join("bar.js");
+ let foo_test = t.path().join("foo_test.js");
+ let bar_test = t.path().join("bar_test.js");
+ write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
+ write(&bar_file, "export default function bar() { 2 + 2 }").unwrap();
+ write(
+ &foo_test,
+ "import foo from './foo.js'; Deno.test('foo', foo);",
+ )
+ .unwrap();
+ write(
+ bar_test,
+ "import bar from './bar.js'; Deno.test('bar', bar);",
+ )
+ .unwrap();
+
+ assert_eq!(stdout_lines.next().unwrap(), "");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "foo", "bar");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "foo", "bar");
+ stdout_lines.next();
+ stdout_lines.next();
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Change content of the file
+ write(
+ &foo_test,
+ "import foo from './foo.js'; Deno.test('foobar', foo);",
+ )
+ .unwrap();
+
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "foobar");
+ stdout_lines.next();
+ stdout_lines.next();
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Add test
+ let another_test = t.path().join("new_test.js");
+ write(&another_test, "Deno.test('another one', () => 3 + 3)").unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "another one");
+ stdout_lines.next();
+ stdout_lines.next();
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Confirm that restarting occurs when a new file is updated
+ write(&another_test, "Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)")
+ .unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stdout_lines.next().unwrap(), "running 2 tests");
+ assert_contains!(stdout_lines.next().unwrap(), "another one");
+ assert_contains!(stdout_lines.next().unwrap(), "another another one");
+ stdout_lines.next();
+ stdout_lines.next();
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
+ write(&another_test, "syntax error ^^").unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stderr_lines.next().unwrap(), "error:");
+ assert_eq!(stderr_lines.next().unwrap(), "");
+ assert_eq!(stderr_lines.next().unwrap(), " syntax error ^^");
+ assert_eq!(stderr_lines.next().unwrap(), " ~~~~~");
+ assert_contains!(stderr_lines.next().unwrap(), "Test failed");
+
+ // Then restore the file
+ write(&another_test, "Deno.test('another one', () => 3 + 3)").unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "another one");
+ stdout_lines.next();
+ stdout_lines.next();
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Confirm that the watcher keeps on working even if the file is updated and the test fails
+ // This also confirms that it restarts when dependencies change
+ write(
+ &foo_file,
+ "export default function foo() { throw new Error('Whoops!'); }",
+ )
+ .unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "FAILED");
+ wait_for(|m| m.contains("FAILED"), &mut stdout_lines);
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Then restore the file
+ write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
+ assert_contains!(stderr_lines.next().unwrap(), "Restarting");
+ assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
+ assert_contains!(stdout_lines.next().unwrap(), "foo");
+ stdout_lines.next();
+ stdout_lines.next();
+ stdout_lines.next();
+ wait_contains("Test finished", &mut stderr_lines);
+
+ // Test that circular dependencies work fine
+ write(
+ &foo_file,
+ "import './bar.js'; export default function foo() { 1 + 1 }",
+ )
+ .unwrap();
+ write(
+ &bar_file,
+ "import './foo.js'; export default function bar() { 2 + 2 }",
+ )
+ .unwrap();
+ check_alive_then_kill(child);
+}
+
+#[flaky_test]
+fn test_watch_doc() {
+ let t = TempDir::new();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("test")
+ .arg("--watch")
+ .arg("--doc")
+ .arg("--unstable")
+ .arg(t.path())
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ assert_eq!(stdout_lines.next().unwrap(), "");
+ assert_contains!(stdout_lines.next().unwrap(), "0 passed | 0 failed");
+ wait_contains("Test finished", &mut stderr_lines);
+
+ let foo_file = t.path().join("foo.ts");
+ write(
+ &foo_file,
+ r#"
+ export default function foo() {}
+ "#,
+ )
+ .unwrap();
+
+ write(
+ &foo_file,
+ r#"
+ /**
+ * ```ts
+ * import foo from "./foo.ts";
+ * ```
+ */
+ export default function foo() {}
+ "#,
+ )
+ .unwrap();
+
+ // We only need to scan for a Check file://.../foo.ts$3-6 line that
+ // corresponds to the documentation block being type-checked.
+ assert_contains!(skip_restarting_line(&mut stderr_lines), "foo.ts$3-6");
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn test_watch_module_graph_error_referrer() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(&file_to_watch, "import './nonexistent.js';").unwrap();
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg("--unstable")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_, mut stderr_lines) = child_lines(&mut child);
+ let line1 = stderr_lines.next().unwrap();
+ assert_contains!(&line1, "Process started");
+ let line2 = stderr_lines.next().unwrap();
+ assert_contains!(&line2, "error: Module not found");
+ assert_contains!(&line2, "nonexistent.js");
+ let line3 = stderr_lines.next().unwrap();
+ assert_contains!(&line3, " at ");
+ assert_contains!(&line3, "file_to_watch.js");
+ wait_contains("Process finished", &mut stderr_lines);
+ check_alive_then_kill(child);
+}
+
+// Regression test for https://github.com/denoland/deno/issues/15428.
+#[test]
+fn test_watch_unload_handler_error_on_drop() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(
+ &file_to_watch,
+ r#"
+ addEventListener("unload", () => {
+ throw new Error("foo");
+ });
+ setTimeout(() => {
+ throw new Error("bar");
+ });
+ "#,
+ )
+ .unwrap();
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (_, mut stderr_lines) = child_lines(&mut child);
+ wait_contains("Process started", &mut stderr_lines);
+ wait_contains("Uncaught Error: bar", &mut stderr_lines);
+ wait_contains("Process finished", &mut stderr_lines);
+ check_alive_then_kill(child);
+}
+
+// Regression test for https://github.com/denoland/deno/issues/15465.
+#[test]
+fn run_watch_reload_once() {
+ let _g = util::http_server();
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ let file_content = r#"
+ import { time } from "http://localhost:4545/dynamic_module.ts";
+ console.log(time);
+ "#;
+ write(&file_to_watch, file_content).unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg("--reload")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ wait_contains("finished", &mut stderr_lines);
+ let first_output = stdout_lines.next().unwrap();
+
+ write(&file_to_watch, file_content).unwrap();
+ // The remote dynamic module should not have been reloaded again.
+
+ wait_contains("finished", &mut stderr_lines);
+ let second_output = stdout_lines.next().unwrap();
+ assert_eq!(second_output, first_output);
+
+ check_alive_then_kill(child);
+}
+
+#[test]
+fn run_watch_dynamic_imports() {
+ let t = TempDir::new();
+ let file_to_watch = t.path().join("file_to_watch.js");
+ write(
+ &file_to_watch,
+ r#"
+ console.log("Hopefully dynamic import will be watched...");
+ await import("./imported.js");
+ "#,
+ )
+ .unwrap();
+ let file_to_watch2 = t.path().join("imported.js");
+ write(
+ file_to_watch2,
+ r#"
+ import "./imported2.js";
+ console.log("I'm dynamically imported and I cause restarts!");
+ "#,
+ )
+ .unwrap();
+ let file_to_watch3 = t.path().join("imported2.js");
+ write(
+ &file_to_watch3,
+ r#"
+ console.log("I'm statically imported from the dynamic import");
+ "#,
+ )
+ .unwrap();
+
+ let mut child = util::deno_cmd()
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--watch")
+ .arg("--unstable")
+ .arg("--allow-read")
+ .arg("-L")
+ .arg("debug")
+ .arg(&file_to_watch)
+ .env("NO_COLOR", "1")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
+
+ assert_contains!(stderr_lines.next().unwrap(), "Process started");
+
+ wait_contains(
+ "Hopefully dynamic import will be watched...",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm statically imported from the dynamic import",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm dynamically imported and I cause restarts!",
+ &mut stdout_lines,
+ );
+
+ wait_for(
+ |m| m.contains("Watching paths") && m.contains("imported2.js"),
+ &mut stderr_lines,
+ );
+ wait_contains("finished", &mut stderr_lines);
+
+ write(
+ &file_to_watch3,
+ r#"
+ console.log("I'm statically imported from the dynamic import and I've changed");
+ "#,
+ )
+ .unwrap();
+
+ wait_contains("Restarting", &mut stderr_lines);
+ wait_contains(
+ "Hopefully dynamic import will be watched...",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm statically imported from the dynamic import and I've changed",
+ &mut stdout_lines,
+ );
+ wait_contains(
+ "I'm dynamically imported and I cause restarts!",
+ &mut stdout_lines,
+ );
+
+ check_alive_then_kill(child);
+}
diff --git a/cli/tests/integration/worker_tests.rs b/cli/tests/integration/worker_tests.rs
new file mode 100644
index 000000000..916e8c887
--- /dev/null
+++ b/cli/tests/integration/worker_tests.rs
@@ -0,0 +1,110 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+itest!(workers {
+ args: "test --reload --location http://127.0.0.1:4545/ -A --unstable workers/test.ts",
+ output: "workers/test.ts.out",
+ http_server: true,
+ });
+
+itest!(worker_error {
+ args: "run -A workers/worker_error.ts",
+ output: "workers/worker_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(worker_nested_error {
+ args: "run -A workers/worker_nested_error.ts",
+ output: "workers/worker_nested_error.ts.out",
+ exit_code: 1,
+});
+
+itest!(worker_async_error {
+ args: "run -A --quiet --reload workers/worker_async_error.ts",
+ output: "workers/worker_async_error.ts.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(worker_message_handler_error {
+ args: "run -A --quiet --reload workers/worker_message_handler_error.ts",
+ output: "workers/worker_message_handler_error.ts.out",
+ http_server: true,
+ exit_code: 1,
+});
+
+itest!(nonexistent_worker {
+ args: "run --allow-read workers/nonexistent_worker.ts",
+ output: "workers/nonexistent_worker.out",
+ exit_code: 1,
+});
+
+itest!(_084_worker_custom_inspect {
+ args: "run --allow-read workers/custom_inspect/main.ts",
+ output: "workers/custom_inspect/main.out",
+});
+
+itest!(error_worker_permissions_local {
+ args: "run --reload workers/error_worker_permissions_local.ts",
+ output: "workers/error_worker_permissions_local.ts.out",
+ exit_code: 1,
+});
+
+itest!(error_worker_permissions_remote {
+ args: "run --reload workers/error_worker_permissions_remote.ts",
+ http_server: true,
+ output: "workers/error_worker_permissions_remote.ts.out",
+ exit_code: 1,
+});
+
+itest!(worker_permissions_remote_remote {
+ args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_remote_remote.ts",
+ output: "workers/permissions_remote_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(worker_permissions_dynamic_remote {
+ args: "run --quiet --reload --allow-net --unstable workers/permissions_dynamic_remote.ts",
+ output: "workers/permissions_dynamic_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(worker_permissions_data_remote {
+ args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_data_remote.ts",
+ output: "workers/permissions_data_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(worker_permissions_blob_remote {
+ args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_blob_remote.ts",
+ output: "workers/permissions_blob_remote.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(worker_permissions_data_local {
+ args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_data_local.ts",
+ output: "workers/permissions_data_local.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(worker_permissions_blob_local {
+ args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_blob_local.ts",
+ output: "workers/permissions_blob_local.ts.out",
+ http_server: true,
+ exit_code: 1,
+ });
+
+itest!(worker_terminate_tla_crash {
+ args: "run --quiet --reload workers/terminate_tla_crash.js",
+ output: "workers/terminate_tla_crash.js.out",
+});
+
+itest!(worker_error_event {
+ args: "run --quiet -A workers/error_event.ts",
+ output: "workers/error_event.ts.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs
new file mode 100644
index 000000000..55afbe5ce
--- /dev/null
+++ b/cli/tests/integration_tests.rs
@@ -0,0 +1,6 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// The tests exist in a sub folder instead of as separate files in
+// this directory so that cargo doesn't compile each file as a new crate.
+
+mod integration;
diff --git a/cli/tests/js_unit_tests.rs b/cli/tests/js_unit_tests.rs
deleted file mode 100644
index 52c24679b..000000000
--- a/cli/tests/js_unit_tests.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-
-mod js {
- use super::*;
-
- #[test]
- fn js_unit_tests_lint() {
- let status = util::deno_cmd()
- .arg("lint")
- .arg("--unstable")
- .arg(util::tests_path().join("unit"))
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn js_unit_tests() {
- let _g = util::http_server();
-
- // Note that the unit tests are not safe for concurrency and must be run with a concurrency limit
- // of one because there are some chdir tests in there.
- // TODO(caspervonb) split these tests into two groups: parallel and serial.
- let mut deno = util::deno_cmd()
- .current_dir(util::root_path())
- .arg("test")
- .arg("--unstable")
- .arg("--location=http://js-unit-tests/foo/bar")
- .arg("--no-prompt")
- .arg("-A")
- .arg(util::tests_path().join("unit"))
- .spawn()
- .expect("failed to spawn script");
-
- let status = deno.wait().expect("failed to wait for the child process");
- assert_eq!(Some(0), status.code());
- assert!(status.success());
- }
-}
diff --git a/cli/tests/lint_tests.rs b/cli/tests/lint_tests.rs
deleted file mode 100644
index 98fe066c2..000000000
--- a/cli/tests/lint_tests.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-use test_util as util;
-
-mod lint {
- use super::*;
-
- #[test]
- fn ignore_unexplicit_files() {
- let output = util::deno_cmd()
- .current_dir(util::root_path())
- .env("NO_COLOR", "1")
- .arg("lint")
- .arg("--unstable")
- .arg("--ignore=./")
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(
- String::from_utf8_lossy(&output.stderr),
- "error: No target files found.\n"
- );
- }
-
- itest!(all {
- args: "lint lint/without_config/file1.js lint/without_config/file2.ts lint/without_config/ignored_file.ts",
- output: "lint/expected.out",
- exit_code: 1,
-});
-
- itest!(quiet {
- args: "lint --quiet lint/without_config/file1.js",
- output: "lint/expected_quiet.out",
- exit_code: 1,
- });
-
- itest!(json {
- args:
- "lint --json lint/without_config/file1.js lint/without_config/file2.ts lint/without_config/ignored_file.ts lint/without_config/malformed.js",
- output: "lint/expected_json.out",
- exit_code: 1,
-});
-
- itest!(compact {
- args:
- "lint --compact lint/without_config/file1.js lint/without_config/ignored_file.tss",
- output: "lint/expected_compact.out",
- exit_code: 1,
-});
-
- itest!(ignore {
- args:
- "lint --ignore=lint/without_config/file1.js,lint/without_config/malformed.js,lint/without_config/lint_with_config/ lint/without_config/",
- output: "lint/expected_ignore.out",
- exit_code: 1,
-});
-
- itest!(glob {
- args: "lint --ignore=lint/without_config/malformed.js,lint/with_config/ lint/without_config/",
- output: "lint/expected_glob.out",
- exit_code: 1,
-});
-
- itest!(stdin {
- args: "lint -",
- input: Some("let _a: any;"),
- output: "lint/expected_from_stdin.out",
- exit_code: 1,
- });
-
- itest!(stdin_json {
- args: "lint --json -",
- input: Some("let _a: any;"),
- output: "lint/expected_from_stdin_json.out",
- exit_code: 1,
- });
-
- itest!(rules {
- args: "lint --rules",
- output: "lint/expected_rules.out",
- exit_code: 0,
- });
-
- // Make sure that the rules are printed if quiet option is enabled.
- itest!(rules_quiet {
- args: "lint --rules -q",
- output: "lint/expected_rules.out",
- exit_code: 0,
- });
-
- itest!(lint_with_config {
- args: "lint --config lint/Deno.jsonc lint/with_config/",
- output: "lint/with_config.out",
- exit_code: 1,
- });
-
- itest!(lint_with_report_config {
- args: "lint --config lint/Deno.compact.format.jsonc lint/with_config/",
- output: "lint/with_report_config_compact.out",
- exit_code: 1,
- });
-
- // Check if CLI flags take precedence
- itest!(lint_with_report_config_override {
- args:
- "lint --config lint/Deno.compact.format.jsonc lint/with_config/ --json",
- output: "lint/with_report_config_override.out",
- exit_code: 1,
- });
-
- itest!(lint_with_config_and_flags {
- args: "lint --config lint/Deno.jsonc --ignore=lint/with_config/a.ts",
- output: "lint/with_config_and_flags.out",
- exit_code: 1,
- });
-
- itest!(lint_with_config_without_tags {
- args: "lint --config lint/Deno.no_tags.jsonc lint/with_config/",
- output: "lint/with_config_without_tags.out",
- exit_code: 1,
- });
-
- itest!(lint_with_malformed_config {
- args: "lint --config lint/Deno.malformed.jsonc",
- output: "lint/with_malformed_config.out",
- exit_code: 1,
- });
-
- itest!(lint_with_malformed_config2 {
- args: "lint --config lint/Deno.malformed2.jsonc",
- output: "lint/with_malformed_config2.out",
- exit_code: 1,
- });
-}
diff --git a/cli/tests/lsp_tests.rs b/cli/tests/lsp_tests.rs
deleted file mode 100644
index cb8e2d24c..000000000
--- a/cli/tests/lsp_tests.rs
+++ /dev/null
@@ -1,6445 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod lsp {
- use deno_ast::ModuleSpecifier;
- use deno_core::serde::de::DeserializeOwned;
- use deno_core::serde::Deserialize;
- use deno_core::serde::Serialize;
- use deno_core::serde_json;
- use deno_core::serde_json::json;
- use deno_core::serde_json::Value;
- use deno_core::url::Url;
- use pretty_assertions::assert_eq;
- use std::collections::HashSet;
- use std::fs;
- use std::process::Stdio;
- use test_util::deno_cmd_with_deno_dir;
- use test_util::deno_exe_path;
- use test_util::env_vars_for_npm_tests;
- use test_util::http_server;
- use test_util::lsp::LspClient;
- use test_util::testdata_path;
- use test_util::TempDir;
- use tower_lsp::lsp_types as lsp;
-
- fn load_fixture(path: &str) -> Value {
- load_fixture_as(path)
- }
-
- fn load_fixture_as<T>(path: &str) -> T
- where
- T: DeserializeOwned,
- {
- let fixture_str = load_fixture_str(path);
- serde_json::from_str::<T>(&fixture_str).unwrap()
- }
-
- fn load_fixture_str(path: &str) -> String {
- let fixtures_path = testdata_path().join("lsp");
- let path = fixtures_path.join(path);
- fs::read_to_string(path).unwrap()
- }
-
- fn init(init_path: &str) -> LspClient {
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", load_fixture(init_path))
- .unwrap();
- client.write_notification("initialized", json!({})).unwrap();
- client
- }
-
- fn did_open<V>(
- client: &mut LspClient,
- params: V,
- ) -> Vec<lsp::PublishDiagnosticsParams>
- where
- V: Serialize,
- {
- client
- .write_notification("textDocument/didOpen", params)
- .unwrap();
-
- handle_configuration_request(
- client,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- );
- read_diagnostics(client).0
- }
-
- fn handle_configuration_request(client: &mut LspClient, result: Value) {
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client.write_response(id, result).unwrap();
- }
-
- fn read_diagnostics(client: &mut LspClient) -> CollectedDiagnostics {
- // diagnostics come in batches of three unless they're cancelled
- let mut diagnostics = vec![];
- for _ in 0..3 {
- let (method, response) = client
- .read_notification::<lsp::PublishDiagnosticsParams>()
- .unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- diagnostics.push(response.unwrap());
- }
- CollectedDiagnostics(diagnostics)
- }
-
- fn shutdown(client: &mut LspClient) {
- client
- .write_request::<_, _, Value>("shutdown", json!(null))
- .unwrap();
- client.write_notification("exit", json!(null)).unwrap();
- }
-
- pub fn ensure_directory_specifier(
- mut specifier: ModuleSpecifier,
- ) -> ModuleSpecifier {
- let path = specifier.path();
- if !path.ends_with('/') {
- let new_path = format!("{}/", path);
- specifier.set_path(&new_path);
- }
- specifier
- }
-
- struct TestSession {
- client: LspClient,
- open_file_count: usize,
- }
-
- impl TestSession {
- pub fn from_file(init_path: &str) -> Self {
- Self::from_client(init(init_path))
- }
-
- pub fn from_client(client: LspClient) -> Self {
- Self {
- client,
- open_file_count: 0,
- }
- }
-
- pub fn did_open<V>(&mut self, params: V) -> CollectedDiagnostics
- where
- V: Serialize,
- {
- self
- .client
- .write_notification("textDocument/didOpen", params)
- .unwrap();
-
- let (id, method, _) = self.client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- self
- .client
- .write_response(
- id,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- )
- .unwrap();
-
- self.open_file_count += 1;
- self.read_diagnostics()
- }
-
- pub fn read_diagnostics(&mut self) -> CollectedDiagnostics {
- let mut all_diagnostics = Vec::new();
- for _ in 0..self.open_file_count {
- all_diagnostics.extend(read_diagnostics(&mut self.client).0);
- }
- CollectedDiagnostics(all_diagnostics)
- }
-
- pub fn shutdown_and_exit(&mut self) {
- shutdown(&mut self.client);
- }
- }
-
- #[derive(Debug, Clone)]
- struct CollectedDiagnostics(Vec<lsp::PublishDiagnosticsParams>);
-
- impl CollectedDiagnostics {
- /// Gets the diagnostics that the editor will see after all the publishes.
- pub fn viewed(&self) -> Vec<lsp::Diagnostic> {
- self
- .viewed_messages()
- .into_iter()
- .flat_map(|m| m.diagnostics)
- .collect()
- }
-
- /// Gets the messages that the editor will see after all the publishes.
- pub fn viewed_messages(&self) -> Vec<lsp::PublishDiagnosticsParams> {
- // go over the publishes in reverse order in order to get
- // the final messages that will be shown in the editor
- let mut messages = Vec::new();
- let mut had_specifier = HashSet::new();
- for message in self.0.iter().rev() {
- if had_specifier.insert(message.uri.clone()) {
- messages.insert(0, message.clone());
- }
- }
- messages
- }
-
- pub fn with_source(&self, source: &str) -> lsp::PublishDiagnosticsParams {
- self
- .viewed_messages()
- .iter()
- .find(|p| {
- p.diagnostics
- .iter()
- .any(|d| d.source == Some(source.to_string()))
- })
- .map(ToOwned::to_owned)
- .unwrap()
- }
-
- pub fn with_file_and_source(
- &self,
- specifier: &str,
- source: &str,
- ) -> lsp::PublishDiagnosticsParams {
- let specifier = ModuleSpecifier::parse(specifier).unwrap();
- self
- .viewed_messages()
- .iter()
- .find(|p| {
- p.uri == specifier
- && p
- .diagnostics
- .iter()
- .any(|d| d.source == Some(source.to_string()))
- })
- .map(ToOwned::to_owned)
- .unwrap()
- }
- }
-
- #[test]
- fn lsp_startup_shutdown() {
- let mut client = init("initialize_params.json");
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_init_tsconfig() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let tsconfig =
- serde_json::to_vec_pretty(&load_fixture("lib.tsconfig.json")).unwrap();
- fs::write(temp_dir.path().join("lib.tsconfig.json"), tsconfig).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./lib.tsconfig.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "location.pathname;\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_tsconfig_types() {
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let temp_dir = TempDir::new();
- let tsconfig =
- serde_json::to_vec_pretty(&load_fixture("types.tsconfig.json")).unwrap();
- fs::write(temp_dir.path().join("types.tsconfig.json"), tsconfig).unwrap();
- let a_dts = load_fixture_str("a.d.ts");
- fs::write(temp_dir.path().join("a.d.ts"), a_dts).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./types.tsconfig.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(a);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_tsconfig_bad_config_path() {
- let mut client = init("initialize_params_bad_config_option.json");
- let (method, maybe_params) = client.read_notification().unwrap();
- assert_eq!(method, "window/showMessage");
- assert_eq!(maybe_params, Some(lsp::ShowMessageParams {
- typ: lsp::MessageType::WARNING,
- message: "The path to the configuration file (\"bad_tsconfig.json\") is not resolvable.".to_string()
- }));
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.args);\n"
- }
- }),
- );
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
- }
-
- #[test]
- fn lsp_triple_slash_types() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let a_dts = load_fixture_str("a.d.ts");
- fs::write(temp_dir.path().join("a.d.ts"), a_dts).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "/// <reference types=\"./a.d.ts\" />\n\nconsole.log(a);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let import_map =
- serde_json::to_vec_pretty(&load_fixture("import-map.json")).unwrap();
- fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
- fs::create_dir(temp_dir.path().join("lib")).unwrap();
- fs::write(
- temp_dir.path().join("lib").join("b.ts"),
- r#"export const b = "b";"#,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("importMap".to_string(), json!("import-map.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
- let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": uri,
- "languageId": "typescript",
- "version": 1,
- "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 2,
- "character": 12
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value":"(alias) const b: \"b\"\nimport b"
- },
- ""
- ],
- "range": {
- "start": {
- "line": 2,
- "character": 12
- },
- "end": {
- "line": 2,
- "character": 13
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map_data_url() {
- let mut client = init("initialize_params_import_map.json");
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import example from \"example\";\n"
- }
- }),
- );
-
- let mut diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- // This indicates that the import map from initialize_params_import_map.json
- // is applied correctly.
- assert!(diagnostics.any(|diagnostic| diagnostic.code
- == Some(lsp::NumberOrString::String("no-cache".to_string()))
- && diagnostic
- .message
- .contains("https://deno.land/x/example/mod.ts")));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map_config_file() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
-
- let deno_import_map_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.import_map.jsonc"))
- .unwrap();
- fs::write(
- temp_dir.path().join("deno.import_map.jsonc"),
- deno_import_map_jsonc,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.import_map.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
- let import_map =
- serde_json::to_vec_pretty(&load_fixture("import-map.json")).unwrap();
- fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
- fs::create_dir(temp_dir.path().join("lib")).unwrap();
- fs::write(
- temp_dir.path().join("lib").join("b.ts"),
- r#"export const b = "b";"#,
- )
- .unwrap();
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
- let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": uri,
- "languageId": "typescript",
- "version": 1,
- "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 2,
- "character": 12
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value":"(alias) const b: \"b\"\nimport b"
- },
- ""
- ],
- "range": {
- "start": {
- "line": 2,
- "character": 12
- },
- "end": {
- "line": 2,
- "character": 13
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_deno_task() {
- let temp_dir = TempDir::new();
- let workspace_root = temp_dir.path().canonicalize().unwrap();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- fs::write(
- workspace_root.join("deno.jsonc"),
- r#"{
- "tasks": {
- "build": "deno test",
- "some:test": "deno bundle mod.ts"
- }
- }"#,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(workspace_root).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>("deno/task", json!(null))
- .unwrap();
-
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "name": "build",
- "detail": "deno test"
- },
- {
- "name": "some:test",
- "detail": "deno bundle mod.ts"
- }
- ]))
- );
- }
-
- #[test]
- fn lsp_import_assertions() {
- let mut client = init("initialize_params_import_map.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/test.json",
- "languageId": "json",
- "version": 1,
- "text": "{\"a\":1}"
- }
- }),
- )
- .unwrap();
- handle_configuration_request(
- &mut client,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- );
-
- let diagnostics = CollectedDiagnostics(did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/a.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import a from \"./test.json\";\n\nconsole.log(a);\n"
- }
- }),
- ));
-
- assert_eq!(
- json!(
- diagnostics
- .with_file_and_source("file:///a/a.ts", "deno")
- .diagnostics
- ),
- json!([
- {
- "range": {
- "start": {
- "line": 0,
- "character": 14
- },
- "end": {
- "line": 0,
- "character": 27
- }
- },
- "severity": 1,
- "code": "no-assert-type",
- "source": "deno",
- "message": "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement."
- }
- ])
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params_import_assertion.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_import_assertion.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map_import_completions() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let import_map =
- serde_json::to_vec_pretty(&load_fixture("import-map-completions.json"))
- .unwrap();
- fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
- fs::create_dir(temp_dir.path().join("lib")).unwrap();
- fs::write(
- temp_dir.path().join("lib").join("b.ts"),
- r#"export const b = "b";"#,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("importMap".to_string(), json!("import-map.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
- let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": uri,
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"/~/b.ts\";\nimport * as b from \"\""
- }
- }),
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 1,
- "character": 20
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "\""
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "isIncomplete": false,
- "items": [
- {
- "label": ".",
- "kind": 19,
- "detail": "(local)",
- "sortText": "1",
- "insertText": ".",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "..",
- "kind": 19,
- "detail": "(local)",
- "sortText": "1",
- "insertText": "..",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "std",
- "kind": 19,
- "detail": "(import map)",
- "sortText": "std",
- "insertText": "std",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "fs",
- "kind": 17,
- "detail": "(import map)",
- "sortText": "fs",
- "insertText": "fs",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "/~",
- "kind": 19,
- "detail": "(import map)",
- "sortText": "/~",
- "insertText": "/~",
- "commitCharacters": ["\"", "'"],
- }
- ]
- }))
- );
-
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": uri,
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 1,
- "character": 20
- },
- "end": {
- "line": 1,
- "character": 20
- }
- },
- "text": "/~/"
- }
- ]
- }),
- )
- .unwrap();
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 1,
- "character": 23
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "/"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "b.ts",
- "kind": 9,
- "detail": "(import map)",
- "sortText": "1",
- "filterText": "/~/b.ts",
- "textEdit": {
- "range": {
- "start": {
- "line": 1,
- "character": 20
- },
- "end": {
- "line": 1,
- "character": 23
- }
- },
- "newText": "/~/b.ts"
- },
- "commitCharacters": ["\"", "'"],
- }
- ]
- }))
- );
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.args);\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const Deno.args: string[]"
- },
- "Returns the script arguments to the program.\n\nGive the following command line invocation of Deno:\n\n```sh\ndeno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd\n```\n\nThen `Deno.args` will contain:\n\n```\n[ \"/etc/passwd\" ]\n```\n\nIf you are looking for a structured way to parse arguments, there is the\n[`std/flags`](https://deno.land/std/flags) module as part of the Deno\nstandard library.",
- "\n\n*@category* - Runtime Environment",
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 17
- },
- "end": {
- "line": 0,
- "character": 21
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_asset() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
- let (_, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/definition",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 14
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- let (_, maybe_error) = client
- .write_request::<_, _, Value>(
- "deno/virtualTextDocument",
- json!({
- "textDocument": {
- "uri": "deno:/asset/lib.deno.shared_globals.d.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "deno:/asset/lib.es2015.symbol.wellknown.d.ts"
- },
- "position": {
- "line": 109,
- "character": 13
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "interface Date",
- },
- "Enables basic storage and retrieval of dates and times."
- ],
- "range": {
- "start": {
- "line": 109,
- "character": 10,
- },
- "end": {
- "line": 109,
- "character": 14,
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_disabled() {
- let mut client = init("initialize_params_disabled.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- )
- .unwrap();
-
- handle_configuration_request(&mut client, json!([{ "enable": false }]));
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_inlay_hints() {
- let mut client = init("initialize_params_hints.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"function a(b: string) {
- return b;
- }
-
- a("foo");
-
- enum C {
- A,
- }
-
- parseInt("123", 8);
-
- const d = Date.now();
-
- class E {
- f = Date.now();
- }
-
- ["a"].map((v) => v + v);
- "#
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/inlayHint",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 19,
- "character": 0,
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- json!(maybe_res),
- json!([
- {
- "position": {
- "line": 0,
- "character": 21
- },
- "label": ": string",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 4,
- "character": 10
- },
- "label": "b:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 7,
- "character": 11
- },
- "label": "= 0",
- "paddingLeft": true
- },
- {
- "position": {
- "line": 10,
- "character": 17
- },
- "label": "string:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 10,
- "character": 24
- },
- "label": "radix:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 12,
- "character": 15
- },
- "label": ": number",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 15,
- "character": 11
- },
- "label": ": number",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 18,
- "character": 18
- },
- "label": "callbackfn:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 18,
- "character": 20
- },
- "label": ": string",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 18,
- "character": 21
- },
- "label": ": string",
- "kind": 1,
- "paddingLeft": true
- }
- ])
- );
- }
-
- #[test]
- fn lsp_inlay_hints_not_enabled() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"function a(b: string) {
- return b;
- }
-
- a("foo");
-
- enum C {
- A,
- }
-
- parseInt("123", 8);
-
- const d = Date.now();
-
- class E {
- f = Date.now();
- }
-
- ["a"].map((v) => v + v);
- "#
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/inlayHint",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 19,
- "character": 0,
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(json!(maybe_res), json!(null));
- }
-
- #[test]
- fn lsp_workspace_enable_paths() {
- let mut params: lsp::InitializeParams = serde_json::from_value(
- load_fixture("initialize_params_workspace_enable_paths.json"),
- )
- .unwrap();
- // we aren't actually writing anything to the tempdir in this test, but we
- // just need a legitimate file path on the host system so that logic that
- // tries to convert to and from the fs paths works on all env
- let temp_dir = TempDir::new();
-
- let root_specifier =
- ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
-
- params.root_uri = Some(root_specifier.clone());
- params.workspace_folders = Some(vec![lsp::WorkspaceFolder {
- uri: root_specifier.clone(),
- name: "project".to_string(),
- }]);
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- handle_configuration_request(
- &mut client,
- json!([{
- "enable": false,
- "enablePaths": [
- "./worker"
- ],
- }]),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./other/file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./other/file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "(method) DateConstructor.now(): number",
- },
- "Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC)."
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 17,
- },
- "end": {
- "line": 0,
- "character": 20,
- }
- }
- }))
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "(method) DateConstructor.now(): number",
- },
- "Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC)."
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 17,
- },
- "end": {
- "line": 0,
- "character": 20,
- }
- }
- }))
- );
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_unstable_disabled() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.dlopen);\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "any"
- }
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 17
- },
- "end": {
- "line": 0,
- "character": 23
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_unstable_enabled() {
- let mut client = init("initialize_params_unstable.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.ppid);\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents":[
- {
- "language":"typescript",
- "value":"const Deno.ppid: number"
- },
- "The process ID of parent process of this instance of the Deno CLI.\n\n```ts\nconsole.log(Deno.ppid);\n```",
- "\n\n*@category* - Runtime Environment",
- ],
- "range":{
- "start":{
- "line":0,
- "character":17
- },
- "end":{
- "line":0,
- "character":21
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_change_mbc() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "const a = `编写软件很难`;\nconst b = `👍🦕😃`;\nconsole.log(a, b);\n"
- }
- }),
- );
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 1,
- "character": 11
- },
- "end": {
- "line": 1,
- // the LSP uses utf16 encoded characters indexes, so
- // after the deno emoiji is character index 15
- "character": 15
- }
- },
- "text": ""
- }
- ]
- }),
- )
- .unwrap();
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 15
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const b: \"😃\"",
- },
- "",
- ],
- "range": {
- "start": {
- "line": 2,
- "character": 15,
- },
- "end": {
- "line": 2,
- "character": 16,
- },
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_closed_document() {
- let temp_dir_guard = TempDir::new();
- let temp_dir = temp_dir_guard.path();
- let a_path = temp_dir.join("a.ts");
- fs::write(a_path, r#"export const a = "a";"#).unwrap();
- let b_path = temp_dir.join("b.ts");
- fs::write(&b_path, r#"export * from "./a.ts";"#).unwrap();
- let b_specifier = Url::from_file_path(b_path).unwrap();
- let c_path = temp_dir.join("c.ts");
- fs::write(&c_path, "import { a } from \"./b.ts\";\nconsole.log(a);\n")
- .unwrap();
- let c_specifier = Url::from_file_path(c_path).unwrap();
-
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": b_specifier,
- "languageId": "typescript",
- "version": 1,
- "text": r#"export * from "./a.ts";"#
- }
- }),
- )
- .unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(id, json!([{ "enable": true }]))
- .unwrap();
-
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": c_specifier,
- "languageId": "typescript",
- "version": 1,
- "text": "import { a } from \"./b.ts\";\nconsole.log(a);\n",
- }
- }),
- )
- .unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(id, json!([{ "enable": true }]))
- .unwrap();
-
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": c_specifier,
- },
- "position": {
- "line": 0,
- "character": 10
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "(alias) const a: \"a\"\nimport a"
- },
- ""
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 9
- },
- "end": {
- "line": 0,
- "character": 10
- }
- }
- }))
- );
- client
- .write_notification(
- "textDocument/didClose",
- json!({
- "textDocument": {
- "uri": b_specifier,
- }
- }),
- )
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": c_specifier,
- },
- "position": {
- "line": 0,
- "character": 10
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "(alias) const a: \"a\"\nimport a"
- },
- ""
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 9
- },
- "end": {
- "line": 0,
- "character": 10
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_dependency() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- }
- }),
- );
- did_open(
- &mut client,
- load_fixture("did_open_params_import_hover.json"),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 0,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end":{
- "line": 0,
- "character": 62
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 3,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/subdir/type_reference.js\n\n**Types**: http&#8203;://127.0.0.1:4545/subdir/type_reference.d.ts\n"
- },
- "range": {
- "start": {
- "line": 3,
- "character": 19
- },
- "end":{
- "line": 3,
- "character": 67
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 4,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/subdir/mod1.ts\n"
- },
- "range": {
- "start": {
- "line": 4,
- "character": 19
- },
- "end":{
- "line": 4,
- "character": 57
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 5,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: _(a data url)_\n"
- },
- "range": {
- "start": {
- "line": 5,
- "character": 19
- },
- "end":{
- "line": 5,
- "character": 132
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 6,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: file&#8203;:///a/file_01.ts\n"
- },
- "range": {
- "start": {
- "line": 6,
- "character": 19
- },
- "end":{
- "line": 6,
- "character": 33
- }
- }
- }))
- );
- }
-
- // This tests for a regression covered by denoland/deno#12753 where the lsp was
- // unable to resolve dependencies when there was an invalid syntax in the module
- #[test]
- fn lsp_hover_deps_preserved_when_invalid_parse() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file1.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export type Foo = { bar(): string };\n"
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import { Foo } from './file1.ts'; declare const f: Foo; f\n"
- }
- }),
- );
- let (maybe_res, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts"
- },
- "position": {
- "line": 0,
- "character": 56
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const f: Foo",
- },
- ""
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 56,
- },
- "end": {
- "line": 0,
- "character": 57,
- }
- }
- }))
- );
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 0,
- "character": 57
- },
- "end": {
- "line": 0,
- "character": 58
- }
- },
- "text": "."
- }
- ]
- }),
- )
- .unwrap();
- let (maybe_res, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts"
- },
- "position": {
- "line": 0,
- "character": 56
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const f: Foo",
- },
- ""
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 56,
- },
- "end": {
- "line": 0,
- "character": 57,
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_typescript_types() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"http://127.0.0.1:4545/xTypeScriptTypes.js\";\n\nconsole.log(a.foo);\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [
- {
- "uri": "http://127.0.0.1:4545/xTypeScriptTypes.js",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 24
- }
- }),
- )
- .unwrap();
- assert!(maybe_res.is_some());
- assert!(maybe_err.is_none());
- assert_eq!(
- json!(maybe_res.unwrap()),
- json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end": {
- "line": 0,
- "character": 62
- }
- }
- })
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_jsdoc_symbol_link() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/b.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export function hello() {}\n"
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import { hello } from \"./b.ts\";\n\nhello();\n\nconst b = \"b\";\n\n/** JSDoc {@link hello} and {@linkcode b} */\nfunction a() {}\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 7,
- "character": 10
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "function a(): void"
- },
- "JSDoc [hello](file:///a/file.ts#L1,10) and [`b`](file:///a/file.ts#L5,7)"
- ],
- "range": {
- "start": {
- "line": 7,
- "character": 9
- },
- "end": {
- "line": 7,
- "character": 10
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_goto_type_definition() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "interface A {\n a: string;\n}\n\nexport class B implements A {\n a = \"a\";\n log() {\n console.log(this.a);\n }\n}\n\nconst b = new B();\nb;\n",
- }
- }),
- );
- let (maybe_res, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/typeDefinition",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 12,
- "character": 1
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "targetUri": "file:///a/file.ts",
- "targetRange": {
- "start": {
- "line": 4,
- "character": 0
- },
- "end": {
- "line": 9,
- "character": 1
- }
- },
- "targetSelectionRange": {
- "start": {
- "line": 4,
- "character": 13
- },
- "end": {
- "line": 4,
- "character": 14
- }
- }
- }
- ]))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_call_hierarchy() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "function foo() {\n return false;\n}\n\nclass Bar {\n baz() {\n return foo();\n }\n}\n\nfunction main() {\n const bar = new Bar();\n bar.baz();\n}\n\nmain();"
- }
- }),
- );
- let (maybe_res, maybe_error) = client
- .write_request(
- "textDocument/prepareCallHierarchy",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 5,
- "character": 3
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("prepare_call_hierarchy_response.json"))
- );
- let (maybe_res, maybe_error) = client
- .write_request(
- "callHierarchy/incomingCalls",
- load_fixture("incoming_calls_params.json"),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("incoming_calls_response.json"))
- );
- let (maybe_res, maybe_error) = client
- .write_request(
- "callHierarchy/outgoingCalls",
- load_fixture("outgoing_calls_params.json"),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("outgoing_calls_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_large_doc_changes() {
- let mut client = init("initialize_params.json");
- did_open(&mut client, load_fixture("did_open_params_large.json"));
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 444,
- "character": 11
- },
- "end": {
- "line": 444,
- "character": 14
- }
- },
- "text": "+++"
- }
- ]
- }),
- )
- .unwrap();
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 445,
- "character": 4
- },
- "end": {
- "line": 445,
- "character": 4
- }
- },
- "text": "// "
- }
- ]
- }),
- )
- .unwrap();
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 477,
- "character": 4
- },
- "end": {
- "line": 477,
- "character": 9
- }
- },
- "text": "error"
- }
- ]
- }),
- )
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 421,
- "character": 30
- }
- }),
- )
- .unwrap();
- assert!(maybe_res.is_some());
- assert!(maybe_err.is_none());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 444,
- "character": 6
- }
- }),
- )
- .unwrap();
- assert!(maybe_res.is_some());
- assert!(maybe_err.is_none());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 461,
- "character": 34
- }
- }),
- )
- .unwrap();
- assert!(maybe_res.is_some());
- assert!(maybe_err.is_none());
- shutdown(&mut client);
-
- assert!(client.duration().as_millis() <= 15000);
- }
-
- #[test]
- fn lsp_document_symbol() {
- let mut client = init("initialize_params.json");
- did_open(&mut client, load_fixture("did_open_params_doc_symbol.json"));
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/documentSymbol",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("document_symbol_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_folding_range() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "// #region 1\n/*\n * Some comment\n */\nclass Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}\n// #endregion"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/foldingRange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "startLine": 0,
- "endLine": 12,
- "kind": "region"
- },
- {
- "startLine": 1,
- "endLine": 3,
- "kind": "comment"
- },
- {
- "startLine": 4,
- "endLine": 10
- },
- {
- "startLine": 5,
- "endLine": 9
- },
- {
- "startLine": 6,
- "endLine": 7
- }
- ]))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_rename() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- // this should not rename in comments and strings
- "text": "let variable = 'a'; // variable\nconsole.log(variable);\n\"variable\";\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/rename",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 4
- },
- "newName": "variable_modified"
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(load_fixture("rename_response.json")));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_selection_range() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "class Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/selectionRange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "positions": [
- {
- "line": 2,
- "character": 8
- }
- ]
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("selection_range_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_semantic_tokens() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- load_fixture("did_open_params_semantic_tokens.json"),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/semanticTokens/full",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "data": [
- 0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15, 3, 10, 5, 0, 4, 1,
- 6, 1, 0, 12, 7, 2, 16, 1, 8, 1, 7, 41, 0, 4, 1, 6, 0, 0, 2, 5, 11, 16,
- 1, 9, 1, 7, 40, 3, 10, 4, 2, 1, 1, 11, 1, 9, 9, 1, 2, 3, 11, 1, 3, 6, 3,
- 0, 1, 0, 15, 4, 2, 0, 1, 30, 1, 6, 9, 1, 2, 3, 11,1, 1, 9, 9, 9, 3, 0,
- 16, 3, 0, 0, 1, 17, 12, 11, 3, 0, 24, 3, 0, 0, 0, 4, 9, 9, 2
- ]
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/semanticTokens/range",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 6,
- "character": 0
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "data": [
- 0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15, 3, 10, 5, 0, 4, 1,
- 6, 1, 0, 12, 7, 2, 16, 1, 8, 1, 7, 41, 0, 4, 1, 6, 0, 0, 2, 5, 11, 16,
- 1, 9, 1, 7, 40
- ]
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "class A {\n a = \"a\";\n\n b() {\n console.log(this.a);\n }\n\n c() {\n this.a = \"c\";\n }\n}\n\nconst a = new A();\na.b();\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(load_fixture("code_lens_response.json")));
- let (maybe_res, maybe_err) = client
- .write_request(
- "codeLens/resolve",
- json!({
- "range": {
- "start": {
- "line": 0,
- "character": 6
- },
- "end": {
- "line": 0,
- "character": 7
- }
- },
- "data": {
- "specifier": "file:///a/file.ts",
- "source": "references"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_lens_resolve_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens_impl() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "interface A {\n b(): void;\n}\n\nclass B implements A {\n b() {\n console.log(\"b\");\n }\n}\n\ninterface C {\n c: string;\n}\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_lens_response_impl.json"))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "codeLens/resolve",
- json!({
- "range": {
- "start": {
- "line": 0,
- "character": 10
- },
- "end": {
- "line": 0,
- "character": 11
- }
- },
- "data": {
- "specifier": "file:///a/file.ts",
- "source": "implementations"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_lens_resolve_response_impl.json"))
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "codeLens/resolve",
- json!({
- "range": {
- "start": {
- "line": 10,
- "character": 10
- },
- "end": {
- "line": 10,
- "character": 11
- }
- },
- "data": {
- "specifier": "file:///a/file.ts",
- "source": "implementations"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "range": {
- "start": {
- "line": 10,
- "character": 10
- },
- "end": {
- "line": 10,
- "character": 11
- }
- },
- "command": {
- "title": "0 implementations",
- "command": ""
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens_test() {
- let mut client = init("initialize_params_code_lens_test.json");
- did_open(
- &mut client,
- load_fixture("did_open_params_test_code_lens.json"),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_lens_response_test.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens_test_disabled() {
- let mut client = init("initialize_params_code_lens_test_disabled.json");
- client
- .write_notification(
- "textDocument/didOpen",
- load_fixture("did_open_params_test_code_lens.json"),
- )
- .unwrap();
-
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(
- id,
- json!([{
- "enable": true,
- "codeLens": {
- "test": false
- }
- }]),
- )
- .unwrap();
-
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!([])));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens_non_doc_nav_tree() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/references",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 3
- },
- "context": {
- "includeDeclaration": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/virtualTextDocument",
- json!({
- "textDocument": {
- "uri": "deno:/asset/lib.deno.shared_globals.d.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Vec<lsp::CodeLens>>(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "deno:/asset/lib.deno.shared_globals.d.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let res = maybe_res.unwrap();
- assert!(res.len() > 50);
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, lsp::CodeLens>(
- "codeLens/resolve",
- json!({
- "range": {
- "start": {
- "line": 416,
- "character": 12
- },
- "end": {
- "line": 416,
- "character": 19
- }
- },
- "data": {
- "specifier": "asset:///lib.deno.shared_globals.d.ts",
- "source": "references"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_nav_tree_updates() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "interface A {\n b(): void;\n}\n\nclass B implements A {\n b() {\n console.log(\"b\");\n }\n}\n\ninterface C {\n c: string;\n}\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_lens_response_impl.json"))
- );
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 10,
- "character": 0
- },
- "end": {
- "line": 13,
- "character": 0
- }
- },
- "text": ""
- }
- ]
- }),
- )
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeLens",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_lens_response_changed.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_signature_help() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "/**\n * Adds two numbers.\n * @param a This is a first number.\n * @param b This is a second number.\n */\nfunction add(a: number, b: number) {\n return a + b;\n}\n\nadd("
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/signatureHelp",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "character": 4,
- "line": 9
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "(",
- "isRetrigger": false
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "signatures": [
- {
- "label": "add(a: number, b: number): number",
- "documentation": {
- "kind": "markdown",
- "value": "Adds two numbers."
- },
- "parameters": [
- {
- "label": "a: number",
- "documentation": {
- "kind": "markdown",
- "value": "This is a first number."
- }
- },
- {
- "label": "b: number",
- "documentation": {
- "kind": "markdown",
- "value": "This is a second number."
- }
- }
- ]
- }
- ],
- "activeSignature": 0,
- "activeParameter": 0
- }))
- );
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 9,
- "character": 4
- },
- "end": {
- "line": 9,
- "character": 4
- }
- },
- "text": "123, "
- }
- ]
- }),
- )
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/signatureHelp",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "character": 8,
- "line": 9
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "signatures": [
- {
- "label": "add(a: number, b: number): number",
- "documentation": {
- "kind": "markdown",
- "value": "Adds two numbers."
- },
- "parameters": [
- {
- "label": "a: number",
- "documentation": {
- "kind": "markdown",
- "value": "This is a first number."
- }
- },
- {
- "label": "b: number",
- "documentation": {
- "kind": "markdown",
- "value": "This is a second number."
- }
- }
- ]
- }
- ],
- "activeSignature": 0,
- "activeParameter": 1
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export function a(): void {\n await Promise.resolve(\"a\");\n}\n\nexport function b(): void {\n await Promise.resolve(\"b\");\n}\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(load_fixture("code_action_response.json")));
- let (maybe_res, maybe_err) = client
- .write_request(
- "codeAction/resolve",
- load_fixture("code_action_resolve_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_resolve_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions_deno_cache() {
- let mut session = TestSession::from_file("initialize_params.json");
- let diagnostics = session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"https://deno.land/x/a/mod.ts\";\n\nconsole.log(a);\n"
- }
- }));
- assert_eq!(
- diagnostics.with_source("deno"),
- load_fixture_as("diagnostics_deno_deps.json")
- );
-
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params_cache.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_cache.json"))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_code_actions_deno_cache_npm() {
- let mut session = TestSession::from_file("initialize_params.json");
- let diagnostics = session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import chalk from \"npm:chalk\";\n\nconsole.log(chalk.green);\n"
- }
- }));
- assert_eq!(
- diagnostics.with_source("deno"),
- load_fixture_as("code_actions/cache_npm/diagnostics.json")
- );
-
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_actions/cache_npm/cache_action.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_actions/cache_npm/cache_response.json"))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_code_actions_imports() {
- let mut session = TestSession::from_file("initialize_params.json");
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file00.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"export interface MallardDuckConfigOptions extends DuckConfigOptions {
- kind: "mallard";
-}
-
-export class MallardDuckConfig extends DuckConfig {
- constructor(options: MallardDuckConfigOptions) {
- super(options);
- }
-}
-"#
- }
- }));
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"import { DuckConfigOptions } from "./file02.ts";
-
-export class DuckConfig {
- readonly kind;
- constructor(options: DuckConfigOptions) {
- this.kind = options.kind;
- }
-}
-"#
- }
- }));
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file02.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"export interface DuckConfigOptions {
- kind: string;
- quacks: boolean;
-}
-"#
- }
- }));
-
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params_imports.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_imports.json"))
- );
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "codeAction/resolve",
- load_fixture("code_action_resolve_params_imports.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_resolve_response_imports.json"))
- );
-
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_code_actions_refactor() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "var x: { a?: number; b?: string } = {};\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params_refactor.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_refactor.json"))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "codeAction/resolve",
- load_fixture("code_action_resolve_params_refactor.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_resolve_response_refactor.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions_refactor_no_disabled_support() {
- let mut client = init("initialize_params_ca_no_disabled.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "interface A {\n a: string;\n}\n\ninterface B {\n b: string;\n}\n\nclass AB implements A, B {\n a = \"a\";\n b = \"b\";\n}\n\nnew AB().a;\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 14,
- "character": 0
- }
- },
- "context": {
- "diagnostics": [],
- "only": [
- "refactor"
- ]
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_no_disabled.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions_deadlock() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- load_fixture("did_open_params_large.json"),
- )
- .unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(id, json!([{ "enable": true }]))
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/semanticTokens/full",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- read_diagnostics(&mut client);
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 444,
- "character": 11
- },
- "end": {
- "line": 444,
- "character": 14
- }
- },
- "text": "+++"
- }
- ]
- }),
- )
- .unwrap();
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 445,
- "character": 4
- },
- "end": {
- "line": 445,
- "character": 4
- }
- },
- "text": "// "
- }
- ]
- }),
- )
- .unwrap();
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 477,
- "character": 4
- },
- "end": {
- "line": 477,
- "character": 9
- }
- },
- "text": "error"
- }
- ]
- }),
- )
- .unwrap();
- // diagnostics only trigger after changes have elapsed in a separate thread,
- // so we need to delay the next messages a little bit to attempt to create a
- // potential for a deadlock with the codeAction
- std::thread::sleep(std::time::Duration::from_millis(50));
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 609,
- "character": 33,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/codeAction",
- load_fixture("code_action_params_deadlock.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
-
- read_diagnostics(&mut client);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_completions() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "Deno."
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 5
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "."
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert!(list.items.len() > 90);
- } else {
- panic!("unexpected response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- load_fixture("completion_resolve_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("completion_resolve_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_completions_private_fields() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"class Foo { #myProperty = "value"; constructor() { this.# } }"#
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 57
- },
- "context": {
- "triggerKind": 1
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert_eq!(list.items.len(), 1);
- let item = &list.items[0];
- assert_eq!(item.label, "#myProperty");
- assert!(!list.is_incomplete);
- } else {
- panic!("unexpected response");
- }
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_completions_optional() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "interface A {\n b?: string;\n}\n\nconst o: A = {};\n\nfunction c(s: string) {}\n\nc(o.)"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- load_fixture("completion_request_params_optional.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "b?",
- "kind": 5,
- "sortText": "11",
- "filterText": "b",
- "insertText": "b",
- "commitCharacters": [".", ",", ";", "("],
- "data": {
- "tsc": {
- "specifier": "file:///a/file.ts",
- "position": 79,
- "name": "b",
- "useCodeSnippet": false
- }
- }
- }
- ]
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- load_fixture("completion_resolve_params_optional.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "label": "b?",
- "kind": 5,
- "detail": "(property) A.b?: string | undefined",
- "documentation": {
- "kind": "markdown",
- "value": ""
- },
- "sortText": "1",
- "filterText": "b",
- "insertText": "b"
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_completions_auto_import() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/b.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const foo = \"foo\";\n",
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export {};\n\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 0,
- },
- "context": {
- "triggerKind": 1,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- if !list.items.iter().any(|item| item.label == "foo") {
- panic!("completions items missing 'foo' symbol");
- }
- } else {
- panic!("unexpected completion response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- json!({
- "label": "foo",
- "kind": 6,
- "sortText": "￿16",
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/file.ts",
- "position": 12,
- "name": "foo",
- "source": "./b",
- "data": {
- "exportName": "foo",
- "moduleSpecifier": "./b",
- "fileName": "file:///a/b.ts"
- },
- "useCodeSnippet": false
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "label": "foo",
- "kind": 6,
- "detail": "const foo: \"foo\"",
- "documentation": {
- "kind": "markdown",
- "value": ""
- },
- "sortText": "￿16",
- "additionalTextEdits": [
- {
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 0,
- "character": 0
- }
- },
- "newText": "import { foo } from \"./b.ts\";\n\n"
- }
- ]
- }))
- );
- }
-
- #[test]
- fn lsp_completions_snippet() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx",
- "languageId": "typescriptreact",
- "version": 1,
- "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return <A t\n}",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx"
- },
- "position": {
- "line": 5,
- "character": 13,
- },
- "context": {
- "triggerKind": 1,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(
- json!(list),
- json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "type",
- "kind": 5,
- "sortText": "11",
- "filterText": "type=\"$1\"",
- "insertText": "type=\"$1\"",
- "insertTextFormat": 2,
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/a.tsx",
- "position": 87,
- "name": "type",
- "useCodeSnippet": false
- }
- }
- }
- ]
- })
- );
- } else {
- panic!("unexpected completion response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- json!({
- "label": "type",
- "kind": 5,
- "sortText": "11",
- "filterText": "type=\"$1\"",
- "insertText": "type=\"$1\"",
- "insertTextFormat": 2,
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/a.tsx",
- "position": 87,
- "name": "type",
- "useCodeSnippet": false
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "label": "type",
- "kind": 5,
- "detail": "(property) type: string",
- "documentation": {
- "kind": "markdown",
- "value": ""
- },
- "sortText": "11",
- "filterText": "type=\"$1\"",
- "insertText": "type=\"$1\"",
- "insertTextFormat": 2
- }))
- );
- }
-
- #[test]
- fn lsp_completions_no_snippet() {
- let mut client = init("initialize_params_no_snippet.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx",
- "languageId": "typescriptreact",
- "version": 1,
- "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return <A t\n}",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx"
- },
- "position": {
- "line": 5,
- "character": 13,
- },
- "context": {
- "triggerKind": 1,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(
- json!(list),
- json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "type",
- "kind": 5,
- "sortText": "11",
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/a.tsx",
- "position": 87,
- "name": "type",
- "useCodeSnippet": false
- }
- }
- }
- ]
- })
- );
- } else {
- panic!("unexpected completion response");
- }
- }
-
- #[test]
- fn lsp_completions_npm() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import cjsDefault from 'npm:@denotest/cjs-default-export';import chalk from 'npm:chalk';\n\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [
- {
- "uri": "npm:@denotest/cjs-default-export",
- },
- {
- "uri": "npm:chalk",
- }
- ]
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
-
- // check importing a cjs default import
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 0
- }
- },
- "text": "cjsDefault."
- }
- ]
- }),
- )
- .unwrap();
- read_diagnostics(&mut client);
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 11
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "."
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(list.items.len(), 3);
- assert!(list.items.iter().any(|i| i.label == "default"));
- assert!(list.items.iter().any(|i| i.label == "MyClass"));
- } else {
- panic!("unexpected response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- load_fixture("completions/npm/resolve_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("completions/npm/resolve_response.json"))
- );
-
- // now check chalk, which is esm
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 3
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 11
- }
- },
- "text": "chalk."
- }
- ]
- }),
- )
- .unwrap();
- read_diagnostics(&mut client);
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 6
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "."
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert!(list.items.iter().any(|i| i.label == "green"));
- assert!(list.items.iter().any(|i| i.label == "red"));
- } else {
- panic!("unexpected response");
- }
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_npm_specifier_unopened_file() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
-
- // create other.ts, which re-exports an npm specifier
- client.deno_dir().write(
- "other.ts",
- "export { default as chalk } from 'npm:chalk@5';",
- );
-
- // cache the other.ts file to the DENO_DIR
- let deno = deno_cmd_with_deno_dir(client.deno_dir())
- .current_dir(client.deno_dir().path())
- .arg("cache")
- .arg("--quiet")
- .arg("other.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
- assert_eq!(output.status.code(), Some(0));
-
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert!(stdout.is_empty());
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stderr.is_empty());
-
- // open main.ts, which imports other.ts (unopened)
- let main_url =
- ModuleSpecifier::from_file_path(client.deno_dir().path().join("main.ts"))
- .unwrap();
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": main_url,
- "languageId": "typescript",
- "version": 1,
- "text": "import { chalk } from './other.ts';\n\n",
- }
- }),
- );
-
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": main_url,
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 0
- }
- },
- "text": "chalk."
- }
- ]
- }),
- )
- .unwrap();
- read_diagnostics(&mut client);
-
- // now ensure completions work
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": main_url
- },
- "position": {
- "line": 2,
- "character": 6
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "."
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(list.items.len(), 63);
- assert!(list.items.iter().any(|i| i.label == "ansi256"));
- } else {
- panic!("unexpected response");
- }
- }
-
- #[test]
- fn lsp_completions_registry() {
- let _g = http_server();
- let mut client = init("initialize_params_registry.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"http://localhost:4545/x/a@\""
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 46
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "@"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(list.items.len(), 3);
- } else {
- panic!("unexpected response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- load_fixture("completion_resolve_params_registry.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("completion_resolve_response_registry.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_completions_registry_empty() {
- let _g = http_server();
- let mut client = init("initialize_params_registry.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"\""
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 20
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "\""
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("completion_request_response_empty.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_auto_discover_registry() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"http://localhost:4545/x/a@\""
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 46
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "@"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (method, maybe_res) = client.read_notification().unwrap();
- assert_eq!(method, "deno/registryState");
- assert_eq!(
- maybe_res,
- Some(json!({
- "origin": "http://localhost:4545",
- "suggestions": true,
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_cache_location() {
- let _g = http_server();
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params_registry.json"))
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("cache".to_string(), json!(".cache"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
- client.write_notification("initialized", json!({})).unwrap();
- let mut session = TestSession::from_client(client);
-
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- }
- }));
- let diagnostics =
- session.did_open(load_fixture("did_open_params_import_hover.json"));
- assert_eq!(diagnostics.viewed().len(), 7);
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 0,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end":{
- "line": 0,
- "character": 62
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 7,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
- },
- "range": {
- "start": {
- "line": 7,
- "character": 19
- },
- "end": {
- "line": 7,
- "character": 53
- }
- }
- }))
- );
- let cache_path = temp_dir.path().join(".cache");
- assert!(cache_path.is_dir());
- assert!(cache_path.join("gen").is_dir());
- session.shutdown_and_exit();
- }
-
- /// Sets the TLS root certificate on startup, which allows the LSP to connect to
- /// the custom signed test server and be able to retrieve the registry config
- /// and cache files.
- #[test]
- fn lsp_tls_cert() {
- let _g = http_server();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params_tls_cert.json"))
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(testdata_path()).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
- client.write_notification("initialized", json!({})).unwrap();
- let mut session = TestSession::from_client(client);
-
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- }
- }));
- let diagnostics =
- session.did_open(load_fixture("did_open_params_tls_cert.json"));
- let diagnostics = diagnostics.viewed();
- assert_eq!(diagnostics.len(), 7);
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 0,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: https&#8203;://localhost:5545/xTypeScriptTypes.js\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end":{
- "line": 0,
- "character": 63
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 7,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
- },
- "range": {
- "start": {
- "line": 7,
- "character": 19
- },
- "end": {
- "line": 7,
- "character": 53
- }
- }
- }))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_diagnostics_warn_redirect() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"http://127.0.0.1:4545/x_deno_warning.js\";\n\nconsole.log(a)\n",
- },
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [
- {
- "uri": "http://127.0.0.1:4545/x_deno_warning.js",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let diagnostics = read_diagnostics(&mut client);
- assert_eq!(
- diagnostics.with_source("deno"),
- lsp::PublishDiagnosticsParams {
- uri: Url::parse("file:///a/file.ts").unwrap(),
- diagnostics: vec![
- lsp::Diagnostic {
- range: lsp::Range {
- start: lsp::Position {
- line: 0,
- character: 19
- },
- end: lsp::Position {
- line: 0,
- character: 60
- }
- },
- severity: Some(lsp::DiagnosticSeverity::WARNING),
- code: Some(lsp::NumberOrString::String("deno-warn".to_string())),
- source: Some("deno".to_string()),
- message: "foobar".to_string(),
- ..Default::default()
- },
- lsp::Diagnostic {
- range: lsp::Range {
- start: lsp::Position {
- line: 0,
- character: 19
- },
- end: lsp::Position {
- line: 0,
- character: 60
- }
- },
- severity: Some(lsp::DiagnosticSeverity::INFORMATION),
- code: Some(lsp::NumberOrString::String("redirect".to_string())),
- source: Some("deno".to_string()),
- message: "The import of \"http://127.0.0.1:4545/x_deno_warning.js\" was redirected to \"http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js\".".to_string(),
- data: Some(json!({"specifier": "http://127.0.0.1:4545/x_deno_warning.js", "redirect": "http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js"})),
- ..Default::default()
- }
- ],
- version: Some(1),
- }
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_redirect_quick_fix() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"http://127.0.0.1:4545/x_deno_warning.js\";\n\nconsole.log(a)\n",
- },
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [
- {
- "uri": "http://127.0.0.1:4545/x_deno_warning.js",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let diagnostics = read_diagnostics(&mut client)
- .with_source("deno")
- .diagnostics;
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- json!(json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end": {
- "line": 0,
- "character": 60
- }
- },
- "context": {
- "diagnostics": diagnostics,
- "only": [
- "quickfix"
- ]
- }
- })),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_redirect_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_diagnostics_deprecated() {
- let mut client = init("initialize_params.json");
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "/** @deprecated */\nexport const a = \"a\";\n\na;\n",
- },
- }),
- );
- assert_eq!(
- json!(diagnostics),
- json!([
- {
- "uri": "file:///a/file.ts",
- "diagnostics": [],
- "version": 1
- },
- {
- "uri": "file:///a/file.ts",
- "diagnostics": [],
- "version": 1
- },
- {
- "uri": "file:///a/file.ts",
- "diagnostics": [
- {
- "range": {
- "start": {
- "line": 3,
- "character": 0
- },
- "end": {
- "line": 3,
- "character": 1
- }
- },
- "severity": 4,
- "code": 6385,
- "source": "deno-ts",
- "message": "'a' is deprecated.",
- "relatedInformation": [],
- "tags": [
- 2
- ]
- }
- ],
- "version": 1
- }
- ])
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_diagnostics_deno_types() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- load_fixture("did_open_params_deno_types.json"),
- )
- .unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(id, json!([{ "enable": true }]))
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/documentSymbol",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- }
- }),
- )
- .unwrap();
- assert!(maybe_res.is_some());
- assert!(maybe_err.is_none());
- let diagnostics = read_diagnostics(&mut client);
- assert_eq!(diagnostics.viewed().len(), 5);
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_diagnostics_refresh_dependents() {
- let mut session = TestSession::from_file("initialize_params.json");
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_00.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- },
- }));
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export * from \"./file_00.ts\";\n",
- },
- }));
- let diagnostics = session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_02.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import { a, b } from \"./file_01.ts\";\n\nconsole.log(a, b);\n"
- }
- }));
- assert_eq!(
- json!(diagnostics.with_file_and_source("file:///a/file_02.ts", "deno-ts")),
- json!({
- "uri": "file:///a/file_02.ts",
- "diagnostics": [
- {
- "range": {
- "start": {
- "line": 0,
- "character": 12
- },
- "end": {
- "line": 0,
- "character": 13
- }
- },
- "severity": 1,
- "code": 2305,
- "source": "deno-ts",
- "message": "Module '\"./file_01.ts\"' has no exported member 'b'."
- }
- ],
- "version": 1
- })
- );
-
- // fix the code causing the diagnostic
- session
- .client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file_00.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 1,
- "character": 0
- },
- "end": {
- "line": 1,
- "character": 0
- }
- },
- "text": "export const b = \"b\";\n"
- }
- ]
- }),
- )
- .unwrap();
- let diagnostics = session.read_diagnostics();
- assert_eq!(diagnostics.viewed().len(), 0); // no diagnostics now
-
- session.shutdown_and_exit();
- assert_eq!(session.client.queue_len(), 0);
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct PerformanceAverage {
- pub name: String,
- pub count: u32,
- pub average_duration: u32,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct PerformanceAverages {
- averages: Vec<PerformanceAverage>,
- }
-
- #[test]
- fn lsp_performance() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.args);\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, PerformanceAverages>(
- "deno/performance",
- json!(null),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(res) = maybe_res {
- assert_eq!(res.averages.len(), 13);
- } else {
- panic!("unexpected result");
- }
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_no_changes() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console;\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- client.assert_no_notification("window/showMessage");
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_error() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console test test\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_mbc() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "const bar = '👍🇺🇸😃'\nconsole.log('hello deno')\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!(load_fixture("formatting_mbc_response.json")))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_exclude_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_fmt_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.fmt.exclude.jsonc"))
- .unwrap();
- fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.fmt.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let file_uri =
- ModuleSpecifier::from_file_path(temp_dir.path().join("ignored.ts"))
- .unwrap()
- .to_string();
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": file_uri,
- "languageId": "typescript",
- "version": 1,
- "text": "function myFunc(){}"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": file_uri
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_exclude_default_config() {
- let temp_dir = TempDir::new();
- let workspace_root = temp_dir.path().canonicalize().unwrap();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.fmt.exclude.jsonc"))
- .unwrap();
- fs::write(workspace_root.join("deno.jsonc"), deno_jsonc).unwrap();
-
- params.root_uri =
- Some(Url::from_file_path(workspace_root.clone()).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let file_uri =
- ModuleSpecifier::from_file_path(workspace_root.join("ignored.ts"))
- .unwrap()
- .to_string();
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": file_uri,
- "languageId": "typescript",
- "version": 1,
- "text": "function myFunc(){}"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": file_uri
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_json() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- // Also test out using a non-json file extension here.
- // What should matter is the language identifier.
- "uri": "file:///a/file.lock",
- "languageId": "json",
- "version": 1,
- "text": "{\"key\":\"value\"}"
- }
- }),
- )
- .unwrap();
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": "file:///a/file.lock"
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
-
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "range": {
- "start": {
- "line": 0,
- "character": 1
- },
- "end": {
- "line": 0,
- "character": 1
- }
- },
- "newText": " "
- },
- {
- "range": {
- "start": { "line": 0, "character": 7 },
- "end": { "line": 0, "character": 7 }
- },
- "newText": " "
- },
- {
- "range": {
- "start": { "line": 0, "character": 14 },
- "end": { "line": 0, "character": 15 }
- },
- "newText": " }\n"
- }
- ]))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_json_no_diagnostics() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/file.json",
- "languageId": "json",
- "version": 1,
- "text": "{\"key\":\"value\"}"
- }
- }),
- )
- .unwrap();
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/semanticTokens/full",
- json!({
- "textDocument": {
- "uri": "file:///a/file.json"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.json"
- },
- "position": {
- "line": 0,
- "character": 3
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_markdown() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md",
- "languageId": "markdown",
- "version": 1,
- "text": "# Hello World"
- }
- }),
- )
- .unwrap();
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md"
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
-
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "range": {
- "start": { "line": 0, "character": 1 },
- "end": { "line": 0, "character": 3 }
- },
- "newText": ""
- },
- {
- "range": {
- "start": { "line": 0, "character": 15 },
- "end": { "line": 0, "character": 15 }
- },
- "newText": "\n"
- }
- ]))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_fmt_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.fmt.jsonc")).unwrap();
- fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.fmt.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export async function someVeryLongFunctionName() {\nconst response = fetch(\"http://localhost:4545/some/non/existent/path.json\");\nconsole.log(response.text());\nconsole.log(\"finished!\")\n}"
- }
- }),
- )
- .unwrap();
-
- // The options below should be ignored in favor of configuration from config file.
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
-
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([{
- "range": {
- "start": {
- "line": 1,
- "character": 0
- },
- "end": {
- "line": 1,
- "character": 0
- }
- },
- "newText": "\t"
- },
- {
- "range": {
- "start": {
- "line": 1,
- "character": 23
- },
- "end": {
- "line": 1,
- "character": 24
- }
- },
- "newText": "\n\t\t'"
- },
- {
- "range": {
- "start": {
- "line": 1,
- "character": 73
- },
- "end": {
- "line": 1,
- "character": 74
- }
- },
- "newText": "',\n\t"
- },
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 0
- }
- },
- "newText": "\t"
- },
- {
- "range": {
- "start": {
- "line": 3,
- "character": 0
- },
- "end": {
- "line": 3,
- "character": 0
- }
- },
- "newText": "\t"
- },
- {
- "range": {
- "start": {
- "line": 3,
- "character": 12
- },
- "end": {
- "line": 3,
- "character": 13
- }
- },
- "newText": "'"
- },
- {
- "range": {
- "start": {
- "line": 3,
- "character": 22
- },
- "end": {
- "line": 3,
- "character": 24
- }
- },
- "newText": "');"
- },
- {
- "range": {
- "start": {
- "line": 4,
- "character": 1
- },
- "end": {
- "line": 4,
- "character": 1
- }
- },
- "newText": "\n"
- }]
- ))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_markdown_no_diagnostics() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md",
- "languageId": "markdown",
- "version": 1,
- "text": "# Hello World"
- }
- }),
- )
- .unwrap();
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/semanticTokens/full",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md"
- },
- "position": {
- "line": 0,
- "character": 3
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_configuration_did_change() {
- let _g = http_server();
- let mut client = init("initialize_params_did_config_change.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"http://localhost:4545/x/a@\""
- }
- }),
- );
- client
- .write_notification(
- "workspace/didChangeConfiguration",
- json!({
- "settings": {}
- }),
- )
- .unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(
- id,
- json!([{
- "enable": true,
- "codeLens": {
- "implementations": true,
- "references": true
- },
- "importMap": null,
- "lint": true,
- "suggest": {
- "autoImports": true,
- "completeFunctionCalls": false,
- "names": true,
- "paths": true,
- "imports": {
- "hosts": {
- "http://localhost:4545/": true
- }
- }
- },
- "unstable": false
- }]),
- )
- .unwrap();
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 46
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "@"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(list.items.len(), 3);
- } else {
- panic!("unexpected response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- load_fixture("completion_resolve_params_registry.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("completion_resolve_response_registry.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_workspace_symbol() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export class A {\n fieldA: string;\n fieldB: string;\n}\n",
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export class B {\n fieldC: string;\n fieldD: string;\n}\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "workspace/symbol",
- json!({
- "query": "field"
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "name": "fieldA",
- "kind": 8,
- "location": {
- "uri": "file:///a/file.ts",
- "range": {
- "start": {
- "line": 1,
- "character": 2
- },
- "end": {
- "line": 1,
- "character": 17
- }
- }
- },
- "containerName": "A"
- },
- {
- "name": "fieldB",
- "kind": 8,
- "location": {
- "uri": "file:///a/file.ts",
- "range": {
- "start": {
- "line": 2,
- "character": 2
- },
- "end": {
- "line": 2,
- "character": 17
- }
- }
- },
- "containerName": "A"
- },
- {
- "name": "fieldC",
- "kind": 8,
- "location": {
- "uri": "file:///a/file_01.ts",
- "range": {
- "start": {
- "line": 1,
- "character": 2
- },
- "end": {
- "line": 1,
- "character": 17
- }
- }
- },
- "containerName": "B"
- },
- {
- "name": "fieldD",
- "kind": 8,
- "location": {
- "uri": "file:///a/file_01.ts",
- "range": {
- "start": {
- "line": 2,
- "character": 2
- },
- "end": {
- "line": 2,
- "character": 17
- }
- }
- },
- "containerName": "B"
- }
- ]))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions_ignore_lint() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "let message = 'Hello, Deno!';\nconsole.log(message);\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_ignore_lint_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_ignore_lint_response.json"))
- );
- shutdown(&mut client);
- }
-
- /// This test exercises updating an existing deno-lint-ignore-file comment.
- #[test]
- fn lsp_code_actions_update_ignore_lint() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text":
-"#!/usr/bin/env -S deno run
-// deno-lint-ignore-file camelcase
-let snake_case = 'Hello, Deno!';
-console.log(snake_case);
-",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_update_ignore_lint_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_update_ignore_lint_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_lint_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_lint_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.lint.jsonc")).unwrap();
- fs::write(temp_dir.path().join("deno.lint.jsonc"), deno_lint_jsonc)
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.lint.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
- let mut session = TestSession::from_client(client);
-
- let diagnostics = session.did_open(load_fixture("did_open_lint.json"));
- let diagnostics = diagnostics.viewed();
- assert_eq!(diagnostics.len(), 1);
- assert_eq!(
- diagnostics[0].code,
- Some(lsp::NumberOrString::String("ban-untagged-todo".to_string()))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_lint_exclude_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_lint_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.lint.exclude.jsonc"))
- .unwrap();
- fs::write(temp_dir.path().join("deno.lint.jsonc"), deno_lint_jsonc)
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.lint.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": ModuleSpecifier::from_file_path(temp_dir.path().join("ignored.ts")).unwrap().to_string(),
- "languageId": "typescript",
- "version": 1,
- "text": "// TODO: fixme\nexport async function non_camel_case() {\nconsole.log(\"finished!\")\n}"
- }
- }),
- );
- let diagnostics = diagnostics
- .into_iter()
- .flat_map(|x| x.diagnostics)
- .collect::<Vec<_>>();
- assert_eq!(diagnostics, Vec::new());
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_jsx_import_source_pragma() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.tsx",
- "languageId": "typescriptreact",
- "version": 1,
- "text":
-"/** @jsxImportSource http://localhost:4545/jsx */
-
-function A() {
- return \"hello\";
-}
-
-export function B() {
- return <A></A>;
-}
-",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.tsx",
- },
- "uris": [
- {
- "uri": "http://127.0.0.1:4545/jsx/jsx-runtime",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.tsx"
- },
- "position": {
- "line": 0,
- "character": 25
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/jsx/jsx-runtime\n",
- },
- "range": {
- "start": {
- "line": 0,
- "character": 21
- },
- "end": {
- "line": 0,
- "character": 46
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[derive(Debug, Clone, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- struct TestData {
- id: String,
- label: String,
- steps: Option<Vec<TestData>>,
- range: Option<lsp::Range>,
- }
-
- #[derive(Debug, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- enum TestModuleNotificationKind {
- Insert,
- Replace,
- }
-
- #[derive(Debug, Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct TestModuleNotificationParams {
- text_document: lsp::TextDocumentIdentifier,
- kind: TestModuleNotificationKind,
- label: String,
- tests: Vec<TestData>,
- }
-
- #[derive(Debug, Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct EnqueuedTestModule {
- text_document: lsp::TextDocumentIdentifier,
- ids: Vec<String>,
- }
-
- #[derive(Debug, Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct TestRunResponseParams {
- enqueued: Vec<EnqueuedTestModule>,
- }
-
- #[test]
- fn lsp_testing_api() {
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let temp_dir = TempDir::new();
-
- let root_specifier =
- ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
-
- let module_path = temp_dir.path().join("./test.ts");
- let specifier = ModuleSpecifier::from_file_path(&module_path).unwrap();
- let contents = r#"
-Deno.test({
- name: "test a",
- fn() {
- console.log("test a");
- }
-});
-"#;
- fs::write(&module_path, contents).unwrap();
- fs::write(temp_dir.path().join("./deno.jsonc"), r#"{}"#).unwrap();
-
- params.root_uri = Some(root_specifier);
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": specifier,
- "languageId": "typescript",
- "version": 1,
- "text": contents,
- }
- }),
- )
- .unwrap();
-
- handle_configuration_request(
- &mut client,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- );
-
- for _ in 0..4 {
- let result = client.read_notification::<Value>();
- assert!(result.is_ok());
- let (method, notification) = result.unwrap();
- if method.as_str() == "deno/testModule" {
- let params: TestModuleNotificationParams =
- serde_json::from_value(notification.unwrap()).unwrap();
- assert_eq!(params.text_document.uri, specifier);
- assert_eq!(params.kind, TestModuleNotificationKind::Replace);
- assert_eq!(params.label, "test.ts");
- assert_eq!(params.tests.len(), 1);
- let test = &params.tests[0];
- assert_eq!(test.label, "test a");
- assert!(test.steps.is_none());
- assert_eq!(
- test.range,
- Some(lsp::Range {
- start: lsp::Position {
- line: 1,
- character: 5,
- },
- end: lsp::Position {
- line: 1,
- character: 9,
- }
- })
- );
- }
- }
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, TestRunResponseParams>(
- "deno/testRun",
- json!({
- "id": 1,
- "kind": "run",
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let res = maybe_res.unwrap();
- assert_eq!(res.enqueued.len(), 1);
- assert_eq!(res.enqueued[0].text_document.uri, specifier);
- assert_eq!(res.enqueued[0].ids.len(), 1);
- let id = res.enqueued[0].ids[0].clone();
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- assert_eq!(
- notification,
- Some(json!({
- "id": 1,
- "message": {
- "type": "started",
- "test": {
- "textDocument": {
- "uri": specifier,
- },
- "id": id,
- },
- }
- }))
- );
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- let notification_value = notification
- .as_ref()
- .unwrap()
- .as_object()
- .unwrap()
- .get("message")
- .unwrap()
- .as_object()
- .unwrap()
- .get("value")
- .unwrap()
- .as_str()
- .unwrap();
- // deno test's output capturing flushes with a zero-width space in order to
- // synchronize the output pipes. Occassionally this zero width space
- // might end up in the output so strip it from the output comparison here.
- assert_eq!(notification_value.replace('\u{200B}', ""), "test a\r\n");
- assert_eq!(
- notification,
- Some(json!({
- "id": 1,
- "message": {
- "type": "output",
- "value": notification_value,
- "test": {
- "textDocument": {
- "uri": specifier,
- },
- "id": id,
- },
- }
- }))
- );
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- let notification = notification.unwrap();
- let obj = notification.as_object().unwrap();
- assert_eq!(obj.get("id"), Some(&json!(1)));
- let message = obj.get("message").unwrap().as_object().unwrap();
- match message.get("type").and_then(|v| v.as_str()) {
- Some("passed") => {
- assert_eq!(
- message.get("test"),
- Some(&json!({
- "textDocument": {
- "uri": specifier
- },
- "id": id,
- }))
- );
- assert!(message.contains_key("duration"));
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- assert_eq!(
- notification,
- Some(json!({
- "id": 1,
- "message": {
- "type": "end",
- }
- }))
- );
- }
- // sometimes on windows, the messages come out of order, but it actually is
- // working, so if we do get the end before the passed, we will simply let
- // the test pass
- Some("end") => (),
- _ => panic!("unexpected message {}", json!(notification)),
- }
-
- shutdown(&mut client);
- }
-}
diff --git a/cli/tests/npm_tests.rs b/cli/tests/npm_tests.rs
deleted file mode 100644
index c0ff56f2d..000000000
--- a/cli/tests/npm_tests.rs
+++ /dev/null
@@ -1,1549 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-mod npm {
- use super::*;
- use pretty_assertions::assert_eq;
- use std::process::Stdio;
- use test_util as util;
- use util::assert_contains;
- use util::env_vars_for_npm_tests;
- use util::env_vars_for_npm_tests_no_sync_download;
- use util::http_server;
-
- // NOTE: See how to make test npm packages at ./testdata/npm/README.md
-
- itest!(esm_module {
- args: "run --allow-read --allow-env npm/esm/main.js",
- output: "npm/esm/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(esm_module_eval {
- args_vec: vec![
- "eval",
- "import chalk from 'npm:chalk@5'; console.log(chalk.green('chalk esm loads'));",
- ],
- output: "npm/esm/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
-});
-
- itest!(esm_module_deno_test {
- args: "test --allow-read --allow-env --unstable npm/esm/test.js",
- output: "npm/esm/test.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(esm_import_cjs_default {
- args: "run --allow-read --allow-env --unstable --quiet --check=all npm/esm_import_cjs_default/main.ts",
- output: "npm/esm_import_cjs_default/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
-});
-
- itest!(cjs_with_deps {
- args: "run --allow-read --allow-env npm/cjs_with_deps/main.js",
- output: "npm/cjs_with_deps/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(cjs_sub_path {
- args: "run --allow-read npm/cjs_sub_path/main.js",
- output: "npm/cjs_sub_path/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(cjs_local_global_decls {
- args: "run --allow-read npm/cjs_local_global_decls/main.ts",
- output: "npm/cjs_local_global_decls/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(cjs_reexport_collision {
- args: "run -A --quiet npm/cjs_reexport_collision/main.ts",
- output: "npm/cjs_reexport_collision/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(cjs_this_in_exports {
- args: "run --allow-read --quiet npm/cjs_this_in_exports/main.js",
- output: "npm/cjs_this_in_exports/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(translate_cjs_to_esm {
- args: "run -A --quiet npm/translate_cjs_to_esm/main.js",
- output: "npm/translate_cjs_to_esm/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(compare_globals {
- args: "run --allow-read --unstable --check=all npm/compare_globals/main.ts",
- output: "npm/compare_globals/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(conditional_exports {
- args: "run --allow-read npm/conditional_exports/main.js",
- output: "npm/conditional_exports/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(conditional_exports_node_modules_dir {
- args:
- "run --allow-read --node-modules-dir $TESTDATA/npm/conditional_exports/main.js",
- output: "npm/conditional_exports/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- temp_cwd: true,
- });
-
- itest!(dual_cjs_esm {
- args: "run -A --quiet npm/dual_cjs_esm/main.ts",
- output: "npm/dual_cjs_esm/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(child_process_fork_test {
- args: "run -A --quiet npm/child_process_fork_test/main.ts",
- output: "npm/child_process_fork_test/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(cjs_module_export_assignment {
- args: "run -A --unstable --quiet --check=all npm/cjs_module_export_assignment/main.ts",
- output: "npm/cjs_module_export_assignment/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
-});
-
- itest!(cjs_module_export_assignment_number {
- args: "run -A --unstable --quiet --check=all npm/cjs_module_export_assignment_number/main.ts",
- output: "npm/cjs_module_export_assignment_number/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
-});
-
- itest!(mixed_case_package_name_global_dir {
- args: "run npm/mixed_case_package_name/global.ts",
- output: "npm/mixed_case_package_name/global.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(mixed_case_package_name_local_dir {
- args:
- "run --node-modules-dir -A $TESTDATA/npm/mixed_case_package_name/local.ts",
- output: "npm/mixed_case_package_name/local.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- temp_cwd: true,
- });
-
- // FIXME(bartlomieju): npm: specifiers are not handled in dynamic imports
- // at the moment
- // itest!(dynamic_import {
- // args: "run --allow-read --allow-env npm/dynamic_import/main.ts",
- // output: "npm/dynamic_import/main.out",
- // envs: env_vars_for_npm_tests(),
- // http_server: true,
- // });
-
- itest!(dynamic_import_reload_same_package {
- args: "run -A --reload npm/dynamic_import_reload_same_package/main.ts",
- output: "npm/dynamic_import_reload_same_package/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(env_var_re_export_dev {
- args: "run --allow-read --allow-env --quiet npm/env_var_re_export/main.js",
- output_str: Some("dev\n"),
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(env_var_re_export_prod {
- args: "run --allow-read --allow-env --quiet npm/env_var_re_export/main.js",
- output_str: Some("prod\n"),
- envs: {
- let mut vars = env_vars_for_npm_tests();
- vars.push(("NODE_ENV".to_string(), "production".to_string()));
- vars
- },
- http_server: true,
- });
-
- itest!(cached_only {
- args: "run --cached-only npm/cached_only/main.ts",
- output: "npm/cached_only/main.out",
- envs: env_vars_for_npm_tests(),
- exit_code: 1,
- });
-
- itest!(import_map {
- args: "run --allow-read --allow-env --import-map npm/import_map/import_map.json npm/import_map/main.js",
- output: "npm/import_map/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(lock_file {
- args: "run --allow-read --allow-env --lock npm/lock_file/lock.json npm/lock_file/main.js",
- output: "npm/lock_file/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 10,
- });
-
- itest!(sub_paths {
- args: "run -A --quiet npm/sub_paths/main.jsx",
- output: "npm/sub_paths/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(remote_npm_specifier {
- args: "run --quiet npm/remote_npm_specifier/main.ts",
- output: "npm/remote_npm_specifier/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(tarball_with_global_header {
- args: "run -A --quiet npm/tarball_with_global_header/main.js",
- output: "npm/tarball_with_global_header/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(nonexistent_file {
- args: "run -A --quiet npm/nonexistent_file/main.js",
- output: "npm/nonexistent_file/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(invalid_package_name {
- args: "run -A --quiet npm/invalid_package_name/main.js",
- output: "npm/invalid_package_name/main.out",
- envs: env_vars_for_npm_tests(),
- exit_code: 1,
- });
-
- itest!(require_json {
- args: "run -A --quiet npm/require_json/main.js",
- output: "npm/require_json/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(error_version_after_subpath {
- args: "run -A --quiet npm/error_version_after_subpath/main.js",
- output: "npm/error_version_after_subpath/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(deno_cache {
- args: "cache --reload npm:chalk npm:mkdirp",
- output: "npm/deno_cache.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(check_all {
- args: "check --all npm/check_errors/main.ts",
- output: "npm/check_errors/main_all.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(check_local {
- args: "check npm/check_errors/main.ts",
- output: "npm/check_errors/main_local.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(types_general {
- args: "check --quiet npm/types/main.ts",
- output: "npm/types/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(types_ambient_module {
- args: "check --quiet npm/types_ambient_module/main.ts",
- output: "npm/types_ambient_module/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(types_ambient_module_import_map {
- args: "check --quiet --import-map=npm/types_ambient_module/import_map.json npm/types_ambient_module/main_import_map.ts",
- output: "npm/types_ambient_module/main_import_map.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(no_types_cjs {
- args: "check --quiet npm/no_types_cjs/main.ts",
- output_str: Some(""),
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(no_types_in_conditional_exports {
- args: "run --check --unstable npm/no_types_in_conditional_exports/main.ts",
- output: "npm/no_types_in_conditional_exports/main.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(types_entry_value_not_exists {
- args: "check --all npm/types_entry_value_not_exists/main.ts",
- output: "npm/types_entry_value_not_exists/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(types_exports_import_types {
- args: "check --all npm/types_exports_import_types/main.ts",
- output: "npm/types_exports_import_types/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(types_no_types_entry {
- args: "check --all npm/types_no_types_entry/main.ts",
- output: "npm/types_no_types_entry/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(typescript_file_in_package {
- args: "run npm/typescript_file_in_package/main.ts",
- output: "npm/typescript_file_in_package/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(permissions_outside_package {
- args: "run --allow-read npm/permissions_outside_package/main.ts",
- output: "npm/permissions_outside_package/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- #[test]
- fn parallel_downloading() {
- let (out, _err) = util::run_and_collect_output_with_args(
- true,
- vec![
- "run",
- "--allow-read",
- "--allow-env",
- "npm/cjs_with_deps/main.js",
- ],
- None,
- // don't use the sync env var
- Some(env_vars_for_npm_tests_no_sync_download()),
- true,
- );
- assert!(out.contains("chalk cjs loads"));
- }
-
- #[test]
- fn cached_only_after_first_run() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("npm/cached_only_after_first_run/main1.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(stderr, "Download");
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--cached-only")
- .arg("npm/cached_only_after_first_run/main2.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(
- stderr,
- "An npm specifier not found in cache: \"ansi-styles\", --cached-only is specified."
- );
- assert!(stdout.is_empty());
- assert!(!output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--cached-only")
- .arg("npm/cached_only_after_first_run/main1.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
-
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert!(output.status.success());
- assert!(stderr.is_empty());
- assert_contains!(stdout, "createChalk: chalk");
- }
-
- #[test]
- fn reload_flag() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("npm/reload/main.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(stderr, "Download");
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--reload")
- .arg("npm/reload/main.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(stderr, "Download");
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--reload=npm:")
- .arg("npm/reload/main.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(stderr, "Download");
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--reload=npm:chalk")
- .arg("npm/reload/main.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(stderr, "Download");
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--reload=npm:foobar")
- .arg("npm/reload/main.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert!(stderr.is_empty());
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
- }
-
- #[test]
- fn no_npm_after_first_run() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--no-npm")
- .arg("npm/no_npm_after_first_run/main1.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(
- stderr,
- "Following npm specifiers were requested: \"chalk@5\"; but --no-npm is specified."
- );
- assert!(stdout.is_empty());
- assert!(!output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("npm/no_npm_after_first_run/main1.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(stderr, "Download");
- assert_contains!(stdout, "createChalk: chalk");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--no-npm")
- .arg("npm/no_npm_after_first_run/main1.ts")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- let stderr = String::from_utf8_lossy(&output.stderr);
- let stdout = String::from_utf8_lossy(&output.stdout);
- assert_contains!(
- stderr,
- "Following npm specifiers were requested: \"chalk@5\"; but --no-npm is specified."
- );
- assert!(stdout.is_empty());
- assert!(!output.status.success());
- }
-
- #[test]
- fn deno_run_cjs_module() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(deno_dir.path())
- .arg("run")
- .arg("--allow-read")
- .arg("--allow-env")
- .arg("--allow-write")
- .arg("npm:mkdirp@1.0.4")
- .arg("test_dir")
- .env("NO_COLOR", "1")
- .envs(env_vars_for_npm_tests())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
-
- assert!(deno_dir.path().join("test_dir").exists());
- }
-
- itest!(deno_run_cowsay {
- args: "run -A --quiet npm:cowsay@1.5.0 Hello",
- output: "npm/deno_run_cowsay.out",
- envs: env_vars_for_npm_tests_no_sync_download(),
- http_server: true,
- });
-
- itest!(deno_run_cowsay_explicit {
- args: "run -A --quiet npm:cowsay@1.5.0/cowsay Hello",
- output: "npm/deno_run_cowsay.out",
- envs: env_vars_for_npm_tests_no_sync_download(),
- http_server: true,
- });
-
- itest!(deno_run_cowthink {
- args: "run -A --quiet npm:cowsay@1.5.0/cowthink Hello",
- output: "npm/deno_run_cowthink.out",
- envs: env_vars_for_npm_tests_no_sync_download(),
- http_server: true,
- });
-
- itest!(deno_run_bin_esm {
- args: "run -A --quiet npm:@denotest/bin/cli-esm this is a test",
- output: "npm/deno_run_esm.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(deno_run_bin_no_ext {
- args: "run -A --quiet npm:@denotest/bin/cli-no-ext this is a test",
- output: "npm/deno_run_no_ext.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(deno_run_bin_cjs {
- args: "run -A --quiet npm:@denotest/bin/cli-cjs this is a test",
- output: "npm/deno_run_cjs.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(deno_run_non_existent {
- args: "run npm:mkdirp@0.5.125",
- output: "npm/deno_run_non_existent.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 1,
- });
-
- itest!(builtin_module_module {
- args: "run --allow-read --quiet npm/builtin_module_module/main.js",
- output: "npm/builtin_module_module/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(node_modules_dir_require_added_node_modules_folder {
- args:
- "run --node-modules-dir -A --quiet $TESTDATA/npm/require_added_nm_folder/main.js",
- output: "npm/require_added_nm_folder/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- exit_code: 0,
- temp_cwd: true,
-});
-
- itest!(node_modules_dir_with_deps {
- args: "run --allow-read --allow-env --node-modules-dir $TESTDATA/npm/cjs_with_deps/main.js",
- output: "npm/cjs_with_deps/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- temp_cwd: true,
-});
-
- itest!(node_modules_dir_yargs {
- args: "run --allow-read --allow-env --node-modules-dir $TESTDATA/npm/cjs_yargs/main.js",
- output: "npm/cjs_yargs/main.out",
- envs: env_vars_for_npm_tests(),
- http_server: true,
- temp_cwd: true,
-});
-
- #[test]
- fn node_modules_dir_cache() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(deno_dir.path())
- .arg("cache")
- .arg("--node-modules-dir")
- .arg("--quiet")
- .arg(util::testdata_path().join("npm/dual_cjs_esm/main.ts"))
- .envs(env_vars_for_npm_tests())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
-
- let node_modules = deno_dir.path().join("node_modules");
- assert!(node_modules
- .join(
- ".deno/@denotest+dual-cjs-esm@1.0.0/node_modules/@denotest/dual-cjs-esm"
- )
- .exists());
- assert!(node_modules.join("@denotest/dual-cjs-esm").exists());
-
- // now try deleting the folder with the package source in the npm cache dir
- let package_global_cache_dir = deno_dir
- .path()
- .join("npm")
- .join("localhost_4545")
- .join("npm")
- .join("registry")
- .join("@denotest")
- .join("dual-cjs-esm")
- .join("1.0.0");
- assert!(package_global_cache_dir.exists());
- std::fs::remove_dir_all(&package_global_cache_dir).unwrap();
-
- // run the output, and it shouldn't bother recreating the directory
- // because it already has everything cached locally in the node_modules folder
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(deno_dir.path())
- .arg("run")
- .arg("--node-modules-dir")
- .arg("--quiet")
- .arg("-A")
- .arg(util::testdata_path().join("npm/dual_cjs_esm/main.ts"))
- .envs(env_vars_for_npm_tests())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
-
- // this won't exist, but actually the parent directory
- // will because it still re-downloads the registry information
- assert!(!package_global_cache_dir.exists());
- }
-
- #[test]
- fn ensure_registry_files_local() {
- // ensures the registry files all point at local tarballs
- let registry_dir_path = util::testdata_path().join("npm").join("registry");
- for entry in std::fs::read_dir(&registry_dir_path).unwrap() {
- let entry = entry.unwrap();
- if entry.metadata().unwrap().is_dir() {
- let registry_json_path = registry_dir_path
- .join(entry.file_name())
- .join("registry.json");
- if registry_json_path.exists() {
- let file_text = std::fs::read_to_string(&registry_json_path).unwrap();
- if file_text.contains("https://registry.npmjs.org/") {
- panic!(
- "file {} contained a reference to the npm registry",
- registry_json_path.display(),
- );
- }
- }
- }
- }
- }
-
- itest!(compile_errors {
- args: "compile -A --quiet npm/cached_only/main.ts",
- output_str: Some("error: npm specifiers have not yet been implemented for this sub command (https://github.com/denoland/deno/issues/15960). Found: npm:chalk@5\n"),
- exit_code: 1,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(bundle_errors {
- args: "bundle --quiet npm/esm/main.js",
- output_str: Some("error: npm specifiers have not yet been implemented for this sub command (https://github.com/denoland/deno/issues/15960). Found: npm:chalk@5\n"),
- exit_code: 1,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(info_chalk_display {
- args: "info --quiet npm/cjs_with_deps/main.js",
- output: "npm/cjs_with_deps/main_info.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(info_chalk_display_node_modules_dir {
- args: "info --quiet --node-modules-dir $TESTDATA/npm/cjs_with_deps/main.js",
- output: "npm/cjs_with_deps/main_info.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- temp_cwd: true,
- });
-
- itest!(info_chalk_json {
- args: "info --quiet --json npm/cjs_with_deps/main.js",
- output: "npm/cjs_with_deps/main_info_json.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(info_chalk_json_node_modules_dir {
- args:
- "info --quiet --node-modules-dir --json $TESTDATA/npm/cjs_with_deps/main.js",
- output: "npm/cjs_with_deps/main_info_json.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- temp_cwd: true,
- });
-
- itest!(info_cli_chalk_display {
- args: "info --quiet npm:chalk@4",
- output: "npm/info/chalk.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(info_cli_chalk_json {
- args: "info --quiet --json npm:chalk@4",
- output: "npm/info/chalk_json.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- #[test]
- fn lock_file_missing_top_level_package() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
- let temp_dir = util::TempDir::new();
-
- // write empty config file
- temp_dir.write("deno.json", "{}");
-
- // Lock file that is automatically picked up has been intentionally broken,
- // by removing "cowsay" package from it. This test ensures that npm resolver
- // snapshot can be successfully hydrated in such situation
- let lock_file_content = r#"{
- "version": "2",
- "remote": {},
- "npm": {
- "specifiers": { "cowsay": "cowsay@1.5.0" },
- "packages": {
- "ansi-regex@3.0.1": {
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
- "dependencies": {}
- },
- "ansi-regex@5.0.1": {
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dependencies": {}
- },
- "ansi-styles@4.3.0": {
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dependencies": { "color-convert": "color-convert@2.0.1" }
- },
- "camelcase@5.3.1": {
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dependencies": {}
- },
- "cliui@6.0.0": {
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dependencies": {
- "string-width": "string-width@4.2.3",
- "strip-ansi": "strip-ansi@6.0.1",
- "wrap-ansi": "wrap-ansi@6.2.0"
- }
- },
- "color-convert@2.0.1": {
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dependencies": { "color-name": "color-name@1.1.4" }
- },
- "color-name@1.1.4": {
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dependencies": {}
- },
- "decamelize@1.2.0": {
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "dependencies": {}
- },
- "emoji-regex@8.0.0": {
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dependencies": {}
- },
- "find-up@4.1.0": {
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dependencies": {
- "locate-path": "locate-path@5.0.0",
- "path-exists": "path-exists@4.0.0"
- }
- },
- "get-caller-file@2.0.5": {
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dependencies": {}
- },
- "get-stdin@8.0.0": {
- "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
- "dependencies": {}
- },
- "is-fullwidth-code-point@2.0.0": {
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "dependencies": {}
- },
- "is-fullwidth-code-point@3.0.0": {
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dependencies": {}
- },
- "locate-path@5.0.0": {
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dependencies": { "p-locate": "p-locate@4.1.0" }
- },
- "p-limit@2.3.0": {
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dependencies": { "p-try": "p-try@2.2.0" }
- },
- "p-locate@4.1.0": {
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dependencies": { "p-limit": "p-limit@2.3.0" }
- },
- "p-try@2.2.0": {
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dependencies": {}
- },
- "path-exists@4.0.0": {
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dependencies": {}
- },
- "require-directory@2.1.1": {
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dependencies": {}
- },
- "require-main-filename@2.0.0": {
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dependencies": {}
- },
- "set-blocking@2.0.0": {
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dependencies": {}
- },
- "string-width@2.1.1": {
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "dependencies": {
- "is-fullwidth-code-point": "is-fullwidth-code-point@2.0.0",
- "strip-ansi": "strip-ansi@4.0.0"
- }
- },
- "string-width@4.2.3": {
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dependencies": {
- "emoji-regex": "emoji-regex@8.0.0",
- "is-fullwidth-code-point": "is-fullwidth-code-point@3.0.0",
- "strip-ansi": "strip-ansi@6.0.1"
- }
- },
- "strip-ansi@4.0.0": {
- "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
- "dependencies": { "ansi-regex": "ansi-regex@3.0.1" }
- },
- "strip-ansi@6.0.1": {
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": { "ansi-regex": "ansi-regex@5.0.1" }
- },
- "strip-final-newline@2.0.0": {
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dependencies": {}
- },
- "which-module@2.0.0": {
- "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
- "dependencies": {}
- },
- "wrap-ansi@6.2.0": {
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dependencies": {
- "ansi-styles": "ansi-styles@4.3.0",
- "string-width": "string-width@4.2.3",
- "strip-ansi": "strip-ansi@6.0.1"
- }
- },
- "y18n@4.0.3": {
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "dependencies": {}
- },
- "yargs-parser@18.1.3": {
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dependencies": {
- "camelcase": "camelcase@5.3.1",
- "decamelize": "decamelize@1.2.0"
- }
- },
- "yargs@15.4.1": {
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "dependencies": {
- "cliui": "cliui@6.0.0",
- "decamelize": "decamelize@1.2.0",
- "find-up": "find-up@4.1.0",
- "get-caller-file": "get-caller-file@2.0.5",
- "require-directory": "require-directory@2.1.1",
- "require-main-filename": "require-main-filename@2.0.0",
- "set-blocking": "set-blocking@2.0.0",
- "string-width": "string-width@4.2.3",
- "which-module": "which-module@2.0.0",
- "y18n": "y18n@4.0.3",
- "yargs-parser": "yargs-parser@18.1.3"
- }
- }
- }
- }
- }
- "#;
- temp_dir.write("deno.lock", lock_file_content);
- let main_contents = r#"
- import cowsay from "npm:cowsay";
- console.log(cowsay);
- "#;
- temp_dir.write("main.ts", main_contents);
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--quiet")
- .arg("--lock")
- .arg("deno.lock")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(!output.status.success());
-
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert_eq!(
- stderr,
- "error: failed reading lockfile 'deno.lock'\n\nCaused by:\n the lockfile is corrupt. You can recreate it with --lock-write\n"
- );
- }
-
- #[test]
- fn lock_file_lock_write() {
- // https://github.com/denoland/deno/issues/16666
- // Ensure that --lock-write still adds npm packages to the lockfile
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
- let temp_dir = util::TempDir::new();
-
- // write empty config file
- temp_dir.write("deno.json", "{}");
-
- // write a lock file with borked integrity
- let lock_file_content = r#"{
- "version": "2",
- "remote": {},
- "npm": {
- "specifiers": { "cowsay@1.5.0": "cowsay@1.5.0" },
- "packages": {
- "ansi-regex@3.0.1": {
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
- "dependencies": {}
- },
- "ansi-regex@5.0.1": {
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dependencies": {}
- },
- "ansi-styles@4.3.0": {
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dependencies": { "color-convert": "color-convert@2.0.1" }
- },
- "camelcase@5.3.1": {
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dependencies": {}
- },
- "cliui@6.0.0": {
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dependencies": {
- "string-width": "string-width@4.2.3",
- "strip-ansi": "strip-ansi@6.0.1",
- "wrap-ansi": "wrap-ansi@6.2.0"
- }
- },
- "color-convert@2.0.1": {
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dependencies": { "color-name": "color-name@1.1.4" }
- },
- "color-name@1.1.4": {
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dependencies": {}
- },
- "cowsay@1.5.0": {
- "integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
- "dependencies": {
- "get-stdin": "get-stdin@8.0.0",
- "string-width": "string-width@2.1.1",
- "strip-final-newline": "strip-final-newline@2.0.0",
- "yargs": "yargs@15.4.1"
- }
- },
- "decamelize@1.2.0": {
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "dependencies": {}
- },
- "emoji-regex@8.0.0": {
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dependencies": {}
- },
- "find-up@4.1.0": {
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dependencies": {
- "locate-path": "locate-path@5.0.0",
- "path-exists": "path-exists@4.0.0"
- }
- },
- "get-caller-file@2.0.5": {
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dependencies": {}
- },
- "get-stdin@8.0.0": {
- "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
- "dependencies": {}
- },
- "is-fullwidth-code-point@2.0.0": {
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "dependencies": {}
- },
- "is-fullwidth-code-point@3.0.0": {
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dependencies": {}
- },
- "locate-path@5.0.0": {
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dependencies": { "p-locate": "p-locate@4.1.0" }
- },
- "p-limit@2.3.0": {
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dependencies": { "p-try": "p-try@2.2.0" }
- },
- "p-locate@4.1.0": {
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dependencies": { "p-limit": "p-limit@2.3.0" }
- },
- "p-try@2.2.0": {
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dependencies": {}
- },
- "path-exists@4.0.0": {
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dependencies": {}
- },
- "require-directory@2.1.1": {
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dependencies": {}
- },
- "require-main-filename@2.0.0": {
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dependencies": {}
- },
- "set-blocking@2.0.0": {
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dependencies": {}
- },
- "string-width@2.1.1": {
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "dependencies": {
- "is-fullwidth-code-point": "is-fullwidth-code-point@2.0.0",
- "strip-ansi": "strip-ansi@4.0.0"
- }
- },
- "string-width@4.2.3": {
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dependencies": {
- "emoji-regex": "emoji-regex@8.0.0",
- "is-fullwidth-code-point": "is-fullwidth-code-point@3.0.0",
- "strip-ansi": "strip-ansi@6.0.1"
- }
- },
- "strip-ansi@4.0.0": {
- "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
- "dependencies": { "ansi-regex": "ansi-regex@3.0.1" }
- },
- "strip-ansi@6.0.1": {
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": { "ansi-regex": "ansi-regex@5.0.1" }
- },
- "strip-final-newline@2.0.0": {
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dependencies": {}
- },
- "which-module@2.0.0": {
- "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
- "dependencies": {}
- },
- "wrap-ansi@6.2.0": {
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dependencies": {
- "ansi-styles": "ansi-styles@4.3.0",
- "string-width": "string-width@4.2.3",
- "strip-ansi": "strip-ansi@6.0.1"
- }
- },
- "y18n@4.0.3": {
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "dependencies": {}
- },
- "yargs-parser@18.1.3": {
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dependencies": {
- "camelcase": "camelcase@5.3.1",
- "decamelize": "decamelize@1.2.0"
- }
- },
- "yargs@15.4.1": {
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "dependencies": {
- "cliui": "cliui@6.0.0",
- "decamelize": "decamelize@1.2.0",
- "find-up": "find-up@4.1.0",
- "get-caller-file": "get-caller-file@2.0.5",
- "require-directory": "require-directory@2.1.1",
- "require-main-filename": "require-main-filename@2.0.0",
- "set-blocking": "set-blocking@2.0.0",
- "string-width": "string-width@4.2.3",
- "which-module": "which-module@2.0.0",
- "y18n": "y18n@4.0.3",
- "yargs-parser": "yargs-parser@18.1.3"
- }
- }
- }
- }
-}
-"#;
- temp_dir.write("deno.lock", lock_file_content);
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("cache")
- .arg("--lock-write")
- .arg("--quiet")
- .arg("npm:cowsay@1.5.0")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
- assert_eq!(output.status.code(), Some(0));
-
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert!(stdout.is_empty());
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stderr.is_empty());
- assert_eq!(
- lock_file_content,
- std::fs::read_to_string(temp_dir.path().join("deno.lock")).unwrap()
- );
- }
-
- #[test]
- fn auto_discover_lock_file() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
- let temp_dir = util::TempDir::new();
-
- // write empty config file
- temp_dir.write("deno.json", "{}");
-
- // write a lock file with borked integrity
- let lock_file_content = r#"{
- "version": "2",
- "remote": {},
- "npm": {
- "specifiers": { "@denotest/bin": "@denotest/bin@1.0.0" },
- "packages": {
- "@denotest/bin@1.0.0": {
- "integrity": "sha512-foobar",
- "dependencies": {}
- }
- }
- }
- }"#;
- temp_dir.write("deno.lock", lock_file_content);
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--unstable")
- .arg("-A")
- .arg("npm:@denotest/bin/cli-esm")
- .arg("test")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(!output.status.success());
- assert_eq!(output.status.code(), Some(10));
-
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stderr.contains(
- "Integrity check failed for npm package: \"@denotest/bin@1.0.0\""
- ));
- }
-
- #[test]
- fn peer_deps_with_copied_folders_and_lockfile() {
- let _server = http_server();
-
- let deno_dir = util::new_deno_dir();
- let temp_dir = util::TempDir::new();
-
- // write empty config file
- temp_dir.write("deno.json", "{}");
- let test_folder_path = test_util::testdata_path()
- .join("npm")
- .join("peer_deps_with_copied_folders");
- let main_contents =
- std::fs::read_to_string(test_folder_path.join("main.ts")).unwrap();
- temp_dir.write("./main.ts", main_contents);
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
-
- let expected_output =
- std::fs::read_to_string(test_folder_path.join("main.out")).unwrap();
-
- assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output);
-
- assert!(temp_dir.path().join("deno.lock").exists());
- let grandchild_path = deno_dir
- .path()
- .join("npm")
- .join("localhost_4545")
- .join("npm")
- .join("registry")
- .join("@denotest")
- .join("peer-dep-test-grandchild");
- assert!(grandchild_path.join("1.0.0").exists());
- assert!(grandchild_path.join("1.0.0_1").exists()); // copy folder, which is hardlinked
-
- // run again
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8(output.stderr).unwrap(), "1\n2\n");
- assert!(output.status.success());
-
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--reload")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output);
- assert!(output.status.success());
-
- // now run with local node modules
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--node-modules-dir")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8(output.stderr).unwrap(), "1\n2\n");
- assert!(output.status.success());
-
- let deno_folder = temp_dir.path().join("node_modules").join(".deno");
- assert!(deno_folder
- .join("@denotest+peer-dep-test-grandchild@1.0.0")
- .exists());
- assert!(deno_folder
- .join("@denotest+peer-dep-test-grandchild@1.0.0_1")
- .exists()); // copy folder
-
- // now again run with local node modules
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--node-modules-dir")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
- assert_eq!(String::from_utf8(output.stderr).unwrap(), "1\n2\n");
-
- // now ensure it works with reloading
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--node-modules-dir")
- .arg("--reload")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
- assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output);
-
- // now ensure it works with reloading and no lockfile
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(temp_dir.path())
- .arg("run")
- .arg("--node-modules-dir")
- .arg("--no-lock")
- .arg("--reload")
- .arg("-A")
- .arg("main.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8(output.stderr).unwrap(), expected_output,);
- assert!(output.status.success());
- }
-
- itest!(info_peer_deps {
- args: "info --quiet npm/peer_deps_with_copied_folders/main.ts",
- output: "npm/peer_deps_with_copied_folders/main_info.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(info_peer_deps_json {
- args: "info --quiet --json npm/peer_deps_with_copied_folders/main.ts",
- output: "npm/peer_deps_with_copied_folders/main_info_json.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-
- itest!(create_require {
- args: "run --reload --allow-read npm/create_require/main.ts",
- output: "npm/create_require/main.out",
- exit_code: 0,
- envs: env_vars_for_npm_tests(),
- http_server: true,
- });
-}
diff --git a/cli/tests/repl_tests.rs b/cli/tests/repl_tests.rs
deleted file mode 100644
index 7a13f7553..000000000
--- a/cli/tests/repl_tests.rs
+++ /dev/null
@@ -1,966 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use test_util as util;
-use test_util::assert_contains;
-use test_util::assert_ends_with;
-use test_util::assert_not_contains;
-
-mod repl {
- use super::*;
-
- #[test]
- fn pty_multiline() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("(\n1 + 2\n)");
- console.write_line("{\nfoo: \"foo\"\n}");
- console.write_line("`\nfoo\n`");
- console.write_line("`\n\\`\n`");
- console.write_line("'{'");
- console.write_line("'('");
- console.write_line("'['");
- console.write_line("/{/");
- console.write_line("/\\(/");
- console.write_line("/\\[/");
- console.write_line("console.log(\"{test1} abc {test2} def {{test3}}\".match(/{([^{].+?)}/));");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, '3');
- assert_contains!(output, "{ foo: \"foo\" }");
- assert_contains!(output, "\"\\nfoo\\n\"");
- assert_contains!(output, "\"\\n`\\n\"");
- assert_contains!(output, "\"{\"");
- assert_contains!(output, "\"(\"");
- assert_contains!(output, "\"[\"");
- assert_contains!(output, "/{/");
- assert_contains!(output, "/\\(/");
- assert_contains!(output, "/\\[/");
- assert_contains!(output, "[ \"{test1}\", \"test1\" ]");
- });
- }
-
- #[test]
- fn pty_null() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("null");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "null");
- });
- }
-
- #[test]
- fn pty_unpaired_braces() {
- for right_brace in &[")", "]", "}"] {
- util::with_pty(&["repl"], |mut console| {
- console.write_line(right_brace);
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "Expression expected");
- });
- }
- }
-
- #[test]
- fn pty_bad_input() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("'\\u{1f3b5}'[0]");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "Unterminated string literal");
- });
- }
-
- #[test]
- fn pty_syntax_error_input() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("('\\u')");
- console.write_line("'");
- console.write_line("[{'a'}];");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(
- output,
- "Bad character escape sequence, expected 4 hex characters"
- );
- assert_contains!(output, "Unterminated string constant");
- assert_contains!(output, "Expected a semicolon");
- });
- }
-
- #[test]
- fn pty_complete_symbol() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("Symbol.it\t");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "Symbol(Symbol.iterator)");
- });
- }
-
- #[test]
- fn pty_complete_declarations() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("class MyClass {}");
- console.write_line("My\t");
- console.write_line("let myVar;");
- console.write_line("myV\t");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "> MyClass");
- assert_contains!(output, "> myVar");
- });
- }
-
- #[test]
- fn pty_complete_primitives() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("let func = function test(){}");
- console.write_line("func.appl\t");
- console.write_line("let str = ''");
- console.write_line("str.leng\t");
- console.write_line("false.valueO\t");
- console.write_line("5n.valueO\t");
- console.write_line("let num = 5");
- console.write_line("num.toStrin\t");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "> func.apply");
- assert_contains!(output, "> str.length");
- assert_contains!(output, "> 5n.valueOf");
- assert_contains!(output, "> false.valueOf");
- assert_contains!(output, "> num.toString");
- });
- }
-
- #[test]
- fn pty_complete_expression() {
- util::with_pty(&["repl"], |mut console| {
- console.write_text("Deno.\t\t");
- console.write_text("y");
- console.write_line("");
- console.write_line("close();");
- let output = console.read_all_output();
- assert_contains!(output, "Display all");
- assert_contains!(output, "core");
- assert_contains!(output, "args");
- assert_contains!(output, "exit");
- assert_contains!(output, "symlink");
- assert_contains!(output, "permissions");
- });
- }
-
- #[test]
- fn pty_complete_imports() {
- util::with_pty(&["repl", "-A"], |mut console| {
- // single quotes
- console.write_line("import './run/001_hel\t'");
- // double quotes
- console.write_line("import { output } from \"./run/045_out\t\"");
- console.write_line("output('testing output');");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "Hello World");
- assert_contains!(
- output,
- // on windows, could any (it's flaky)
- "\ntesting output",
- "testing output\u{1b}",
- "\r\n\u{1b}[?25htesting output",
- );
- });
-
- // ensure when the directory changes that the suggestions come from the cwd
- util::with_pty(&["repl", "-A"], |mut console| {
- console.write_line("Deno.chdir('./subdir');");
- console.write_line("import '../run/001_hel\t'");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "Hello World");
- });
- }
-
- #[test]
- fn pty_complete_imports_no_panic_empty_specifier() {
- // does not panic when tabbing when empty
- util::with_pty(&["repl"], |mut console| {
- console.write_line("import '\t';");
- console.write_line("close();");
- });
- }
-
- #[test]
- fn pty_ignore_symbols() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("Array.Symbol\t");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_contains!(output, "undefined");
- assert_not_contains!(
- output,
- "Uncaught TypeError: Array.Symbol is not a function"
- );
- });
- }
-
- #[test]
- fn pty_assign_global_this() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("globalThis = 42;");
- console.write_line("close();");
-
- let output = console.read_all_output();
- assert_not_contains!(output, "panicked");
- });
- }
-
- #[test]
- fn pty_emoji() {
- // windows was having issues displaying this
- util::with_pty(&["repl"], |mut console| {
- console.write_line(r#"console.log('\u{1F995}');"#);
- console.write_line("close();");
-
- let output = console.read_all_output();
- // only one for the output (since input is escaped)
- let emoji_count = output.chars().filter(|c| *c == '🦕').count();
- assert_eq!(emoji_count, 1);
- });
- }
-
- #[test]
- fn console_log() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["console.log('hello')", "'world'"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "hello\nundefined\n\"world\"\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn object_literal() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["{}", "{ foo: 'bar' }"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "{}\n{ foo: \"bar\" }\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn block_expression() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["{};", "{\"\"}"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "undefined\n\"\"\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn await_resolve() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["await Promise.resolve('done')"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "\"done\"\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn await_timeout() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["await new Promise((r) => setTimeout(r, 0, 'done'))"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "\"done\"\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn let_redeclaration() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["let foo = 0;", "foo", "let foo = 1;", "foo"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "undefined\n0\nundefined\n1\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn repl_cwd() {
- let (_out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["Deno.cwd()"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert!(err.is_empty());
- }
-
- #[test]
- fn typescript() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- "function add(a: number, b: number) { return a + b }",
- "const result: number = add(1, 2) as number;",
- "result",
- ]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "undefined\nundefined\n3\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn typescript_declarations() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- "namespace Test { export enum Values { A, B, C } }",
- "Test.Values.A",
- "Test.Values.C",
- "interface MyInterface { prop: string; }",
- "type MyTypeAlias = string;",
- ]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- let expected_end_text = "undefined\n0\n2\nundefined\nundefined\n";
- assert_ends_with!(out, expected_end_text);
- assert!(err.is_empty());
- }
-
- #[test]
- fn typescript_decorators() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- "function dec(target) { target.prototype.test = () => 2; }",
- "@dec class Test {}",
- "new Test().test()",
- ]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "undefined\n[Function: Test]\n2\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn eof() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["1 + 2"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "3\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn strict() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- "let a = {};",
- "Object.preventExtensions(a);",
- "a.c = 1;",
- ]),
- None,
- false,
- );
- assert_contains!(
- out,
- "Uncaught TypeError: Cannot add property c, object is not extensible"
- );
- assert!(err.is_empty());
- }
-
- #[test]
- fn close_command() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["close()", "'ignored'"]),
- None,
- false,
- );
-
- assert_not_contains!(out, "ignored");
- assert!(err.is_empty());
- }
-
- #[test]
- fn function() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["Deno.writeFileSync"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "[Function: writeFileSync]\n");
- assert!(err.is_empty());
- }
-
- #[test]
- #[ignore]
- fn multiline() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["(\n1 + 2\n)"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "3\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn import() {
- let (out, _) = util::run_and_collect_output_with_args(
- true,
- vec![],
- Some(vec!["import('./subdir/auto_print_hello.ts')"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_contains!(out, "hello!\n");
- }
-
- #[test]
- fn import_declarations() {
- let (out, _) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--allow-read"],
- Some(vec!["import './subdir/auto_print_hello.ts';"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_contains!(out, "hello!\n");
- }
-
- #[test]
- fn exports_stripped() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["export default 5;", "export class Test {}"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_contains!(out, "5\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn call_eval_unterminated() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["eval('{')"]),
- None,
- false,
- );
- assert_contains!(out, "Unexpected end of input");
- assert!(err.is_empty());
- }
-
- #[test]
- fn unpaired_braces() {
- for right_brace in &[")", "]", "}"] {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![right_brace]),
- None,
- false,
- );
- assert_contains!(out, "Expression expected");
- assert!(err.is_empty());
- }
- }
-
- #[test]
- fn reference_error() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["not_a_variable"]),
- None,
- false,
- );
- assert_contains!(out, "not_a_variable is not defined");
- assert!(err.is_empty());
- }
-
- #[test]
- fn syntax_error() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- "syntax error",
- "2", // ensure it keeps accepting input after
- ]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(
- out,
- "parse error: Expected ';', '}' or <eof> at 1:8\n2\n"
- );
- assert!(err.is_empty());
- }
-
- #[test]
- fn syntax_error_jsx() {
- // JSX is not supported in the REPL
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["const element = <div />;"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_contains!(out, "Expression expected");
- assert!(err.is_empty());
- }
-
- #[test]
- fn type_error() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["console()"]),
- None,
- false,
- );
- assert_contains!(out, "console is not a function");
- assert!(err.is_empty());
- }
-
- #[test]
- fn variable() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["var a = 123;", "a"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "undefined\n123\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn lexical_scoped_variable() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["let a = 123;", "a"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "undefined\n123\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn missing_deno_dir() {
- use std::fs::{read_dir, remove_dir_all};
- const DENO_DIR: &str = "nonexistent";
- let test_deno_dir = test_util::testdata_path().join(DENO_DIR);
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["1"]),
- Some(vec![
- ("DENO_DIR".to_owned(), DENO_DIR.to_owned()),
- ("NO_COLOR".to_owned(), "1".to_owned()),
- ]),
- false,
- );
- assert!(read_dir(&test_deno_dir).is_ok());
- remove_dir_all(&test_deno_dir).unwrap();
- assert_ends_with!(out, "1\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn save_last_eval() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["1", "_"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "1\n1\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn save_last_thrown() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["throw 1", "_error"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(out, "Uncaught 1\n1\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn assign_underscore() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["_ = 1", "2", "_"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert_ends_with!(
- out,
- "Last evaluation result is no longer saved to _.\n1\n2\n1\n"
- );
- assert!(err.is_empty());
- }
-
- #[test]
- fn assign_underscore_error() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["_error = 1", "throw 2", "_error"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- println!("{}", out);
- assert_ends_with!(
- out,
- "Last thrown error is no longer saved to _error.\n1\nUncaught 2\n1\n"
- );
- assert!(err.is_empty());
- }
-
- #[test]
- fn custom_inspect() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- r#"const o = {
- [Symbol.for("Deno.customInspect")]() {
- throw new Error('Oops custom inspect error');
- },
- };"#,
- "o",
- ]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
-
- assert_contains!(out, "Oops custom inspect error");
- assert!(err.is_empty());
- }
-
- #[test]
- fn eval_flag_valid_input() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--eval", "const t = 10;"],
- Some(vec!["t * 500;"]),
- None,
- false,
- );
- assert_contains!(out, "5000");
- assert!(err.is_empty());
- }
-
- #[test]
- fn eval_flag_parse_error() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--eval", "const %"],
- Some(vec!["250 * 10"]),
- None,
- false,
- );
- assert_contains!(
- test_util::strip_ansi_codes(&out),
- "Error in --eval flag: parse error: Unexpected token `%`."
- );
- assert_contains!(out, "2500"); // should not prevent input
- assert!(err.is_empty());
- }
-
- #[test]
- fn eval_flag_runtime_error() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--eval", "throw new Error('Testing')"],
- Some(vec!["250 * 10"]),
- None,
- false,
- );
- assert_contains!(out, "Error in --eval flag: Uncaught Error: Testing");
- assert_contains!(out, "2500"); // should not prevent input
- assert!(err.is_empty());
- }
-
- #[test]
- fn eval_file_flag_valid_input() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--eval-file=./run/001_hello.js"],
- None,
- None,
- false,
- );
- assert_contains!(out, "Hello World");
- assert!(err.is_empty());
- }
-
- #[test]
- fn eval_file_flag_call_defined_function() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--eval-file=./tsc/d.ts"],
- Some(vec!["v4()"]),
- None,
- false,
- );
- assert_contains!(out, "hello");
- assert!(err.is_empty());
- }
-
- #[test]
- fn eval_file_flag_http_input() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--eval-file=http://127.0.0.1:4545/tsc/d.ts"],
- Some(vec!["v4()"]),
- None,
- true,
- );
- assert_contains!(out, "hello");
- assert!(err.contains("Download"));
- }
-
- #[test]
- fn eval_file_flag_multiple_files() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--allow-read", "--eval-file=http://127.0.0.1:4545/repl/import_type.ts,./tsc/d.ts,http://127.0.0.1:4545/type_definitions/foo.js"],
- Some(vec!["b.method1=v4", "b.method1()+foo.toUpperCase()"]),
- None,
- true,
- );
- assert_contains!(out, "helloFOO");
- assert_contains!(err, "Download");
- }
-
- #[test]
- fn pty_clear_function() {
- util::with_pty(&["repl"], |mut console| {
- console.write_line("console.log('hello');");
- console.write_line("clear();");
- console.write_line("const clear = 1234 + 2000;");
- console.write_line("clear;");
- console.write_line("close();");
-
- let output = console.read_all_output();
- if cfg!(windows) {
- // Windows will overwrite what's in the console buffer before
- // we read from it. It contains this string repeated many times
- // to clear the screen.
- assert_contains!(output, "\r\n\u{1b}[K\r\n\u{1b}[K\r\n\u{1b}[K");
- } else {
- assert_contains!(output, "hello");
- assert_contains!(output, "[1;1H");
- }
- assert_contains!(output, "undefined");
- assert_contains!(output, "const clear = 1234 + 2000;");
- assert_contains!(output, "3234");
- });
- }
-
- #[test]
- fn pty_tab_handler() {
- // If the last character is **not** whitespace, we show the completions
- util::with_pty(&["repl"], |mut console| {
- console.write_line("a\t\t");
- console.write_line("close();");
- let output = console.read_all_output();
- assert_contains!(output, "addEventListener");
- assert_contains!(output, "alert");
- assert_contains!(output, "atob");
- });
- // If the last character is whitespace, we just insert a tab
- util::with_pty(&["repl"], |mut console| {
- console.write_line("a; \t\t"); // last character is whitespace
- console.write_line("close();");
- let output = console.read_all_output();
- assert_not_contains!(output, "addEventListener");
- assert_not_contains!(output, "alert");
- assert_not_contains!(output, "atob");
- });
- }
-
- #[test]
- fn repl_report_error() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec![
- r#"console.log(1); reportError(new Error("foo")); console.log(2);"#,
- ]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
-
- // TODO(nayeemrmn): The REPL should report event errors and rejections.
- assert_contains!(out, "1\n2\nundefined\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn pty_aggregate_error() {
- let (out, err) = util::run_and_collect_output(
- true,
- "repl",
- Some(vec!["await Promise.any([])"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
-
- assert_contains!(out, "AggregateError");
- assert!(err.is_empty());
- }
-
- #[test]
- fn repl_with_quiet_flag() {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--quiet"],
- Some(vec!["await Promise.resolve('done')"]),
- Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]),
- false,
- );
- assert!(!out.contains("Deno"));
- assert!(!out.contains("exit using ctrl+d, ctrl+c, or close()"));
- assert_ends_with!(out, "\"done\"\n");
- assert!(err.is_empty());
- }
-
- #[test]
- fn npm_packages() {
- let mut env_vars = util::env_vars_for_npm_tests();
- env_vars.push(("NO_COLOR".to_owned(), "1".to_owned()));
-
- {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--quiet", "--allow-read", "--allow-env"],
- Some(vec![
- r#"import chalk from "npm:chalk";"#,
- "chalk.red('hel' + 'lo')",
- ]),
- Some(env_vars.clone()),
- true,
- );
-
- assert_contains!(out, "hello");
- assert!(err.is_empty());
- }
-
- {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--quiet", "--allow-read", "--allow-env"],
- Some(vec![
- r#"const chalk = await import("npm:chalk");"#,
- "chalk.default.red('hel' + 'lo')",
- ]),
- Some(env_vars.clone()),
- true,
- );
-
- assert_contains!(out, "hello");
- assert!(err.is_empty());
- }
-
- {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--quiet", "--allow-read", "--allow-env"],
- Some(vec![r#"export {} from "npm:chalk";"#]),
- Some(env_vars.clone()),
- true,
- );
-
- assert_contains!(out, "Module {");
- assert_contains!(out, "Chalk: [Function: Chalk],");
- assert!(err.is_empty());
- }
-
- {
- let (out, err) = util::run_and_collect_output_with_args(
- true,
- vec!["repl", "--quiet", "--allow-read", "--allow-env"],
- Some(vec![r#"import foo from "npm:asdfawe52345asdf""#]),
- Some(env_vars),
- true,
- );
-
- assert_contains!(
- out,
- "error: npm package 'asdfawe52345asdf' does not exist"
- );
- assert!(err.is_empty());
- }
- }
-}
diff --git a/cli/tests/run_tests.rs b/cli/tests/run_tests.rs
deleted file mode 100644
index 8460d3f8f..000000000
--- a/cli/tests/run_tests.rs
+++ /dev/null
@@ -1,3745 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-use deno_core::url;
-use deno_runtime::deno_fetch::reqwest;
-use std::io::Read;
-use std::io::Write;
-use std::process::Command;
-use std::process::Stdio;
-use test_util as util;
-use test_util::TempDir;
-use tokio::task::LocalSet;
-use trust_dns_client::serialize::txt::Lexer;
-use trust_dns_client::serialize::txt::Parser;
-use util::assert_contains;
-
-mod run {
- use super::*;
-
- itest!(stdout_write_all {
- args: "run --quiet run/stdout_write_all.ts",
- output: "run/stdout_write_all.out",
- });
-
- itest!(stdin_read_all {
- args: "run --quiet run/stdin_read_all.ts",
- output: "run/stdin_read_all.out",
- input: Some("01234567890123456789012345678901234567890123456789"),
- });
-
- itest!(stdout_write_sync_async {
- args: "run --quiet run/stdout_write_sync_async.ts",
- output: "run/stdout_write_sync_async.out",
- });
-
- itest!(_001_hello {
- args: "run --reload run/001_hello.js",
- output: "run/001_hello.js.out",
- });
-
- itest!(_002_hello {
- args: "run --quiet --reload run/002_hello.ts",
- output: "run/002_hello.ts.out",
- });
-
- itest!(_003_relative_import {
- args: "run --quiet --reload run/003_relative_import.ts",
- output: "run/003_relative_import.ts.out",
- });
-
- itest!(_004_set_timeout {
- args: "run --quiet --reload run/004_set_timeout.ts",
- output: "run/004_set_timeout.ts.out",
- });
-
- itest!(_005_more_imports {
- args: "run --quiet --reload run/005_more_imports.ts",
- output: "run/005_more_imports.ts.out",
- });
-
- itest!(_006_url_imports {
- args: "run --quiet --reload run/006_url_imports.ts",
- output: "run/006_url_imports.ts.out",
- http_server: true,
- });
-
- itest!(_012_async {
- args: "run --quiet --reload run/012_async.ts",
- output: "run/012_async.ts.out",
- });
-
- itest!(_013_dynamic_import {
- args: "run --quiet --reload --allow-read run/013_dynamic_import.ts",
- output: "run/013_dynamic_import.ts.out",
- });
-
- itest!(_014_duplicate_import {
- args: "run --quiet --reload --allow-read run/014_duplicate_import.ts ",
- output: "run/014_duplicate_import.ts.out",
- });
-
- itest!(_015_duplicate_parallel_import {
- args:
- "run --quiet --reload --allow-read run/015_duplicate_parallel_import.js",
- output: "run/015_duplicate_parallel_import.js.out",
- });
-
- itest!(_016_double_await {
- args: "run --quiet --allow-read --reload run/016_double_await.ts",
- output: "run/016_double_await.ts.out",
- });
-
- itest!(_017_import_redirect {
- args: "run --quiet --reload run/017_import_redirect.ts",
- output: "run/017_import_redirect.ts.out",
- });
-
- itest!(_017_import_redirect_nocheck {
- args: "run --quiet --reload --no-check run/017_import_redirect.ts",
- output: "run/017_import_redirect.ts.out",
- });
-
- itest!(_017_import_redirect_info {
- args: "info --quiet --reload run/017_import_redirect.ts",
- output: "run/017_import_redirect_info.out",
- });
-
- itest!(_018_async_catch {
- args: "run --quiet --reload run/018_async_catch.ts",
- output: "run/018_async_catch.ts.out",
- });
-
- itest!(_019_media_types {
- args: "run --reload run/019_media_types.ts",
- output: "run/019_media_types.ts.out",
- http_server: true,
- });
-
- itest!(_020_json_modules {
- args: "run --reload run/020_json_modules.ts",
- output: "run/020_json_modules.ts.out",
- exit_code: 1,
- });
-
- itest!(_021_mjs_modules {
- args: "run --quiet --reload run/021_mjs_modules.ts",
- output: "run/021_mjs_modules.ts.out",
- });
-
- itest!(_023_no_ext {
- args: "run --reload --check run/023_no_ext",
- output: "run/023_no_ext.out",
- });
-
- // TODO(lucacasonato): remove --unstable when permissions goes stable
- itest!(_025_hrtime {
- args: "run --quiet --allow-hrtime --unstable --reload run/025_hrtime.ts",
- output: "run/025_hrtime.ts.out",
- });
-
- itest!(_025_reload_js_type_error {
- args: "run --quiet --reload run/025_reload_js_type_error.js",
- output: "run/025_reload_js_type_error.js.out",
- });
-
- itest!(_026_redirect_javascript {
- args: "run --quiet --reload run/026_redirect_javascript.js",
- output: "run/026_redirect_javascript.js.out",
- http_server: true,
- });
-
- itest!(_027_redirect_typescript {
- args: "run --quiet --reload run/027_redirect_typescript.ts",
- output: "run/027_redirect_typescript.ts.out",
- http_server: true,
- });
-
- itest!(_028_args {
- args:
- "run --quiet --reload run/028_args.ts --arg1 val1 --arg2=val2 -- arg3 arg4",
- output: "run/028_args.ts.out",
-});
-
- itest!(_033_import_map {
- args:
- "run --quiet --reload --import-map=import_maps/import_map.json import_maps/test.ts",
- output: "run/033_import_map.out",
-});
-
- itest!(_033_import_map_remote {
- args:
- "run --quiet --reload --import-map=http://127.0.0.1:4545/import_maps/import_map_remote.json --unstable import_maps/test_remote.ts",
- output: "run/033_import_map_remote.out",
- http_server: true,
-});
-
- itest!(onload {
- args: "run --quiet --reload run/onload/main.ts",
- output: "run/onload/main.out",
- });
-
- itest!(_035_cached_only_flag {
- args: "run --reload --check --cached-only http://127.0.0.1:4545/run/019_media_types.ts",
- output: "run/035_cached_only_flag.out",
- exit_code: 1,
- http_server: true,
-});
-
- itest!(_038_checkjs {
- // checking if JS file is run through TS compiler
- args:
- "run --reload --config run/checkjs.tsconfig.json --check run/038_checkjs.js",
- exit_code: 1,
- output: "run/038_checkjs.js.out",
-});
-
- itest!(_042_dyn_import_evalcontext {
- args: "run --quiet --allow-read --reload run/042_dyn_import_evalcontext.ts",
- output: "run/042_dyn_import_evalcontext.ts.out",
- });
-
- itest!(_044_bad_resource {
- args: "run --quiet --reload --allow-read run/044_bad_resource.ts",
- output: "run/044_bad_resource.ts.out",
- exit_code: 1,
- });
-
- // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
- itest!(_045_proxy {
- args: "run -L debug --unstable --allow-net --allow-env --allow-run --allow-read --reload --quiet run/045_proxy_test.ts",
- output: "run/045_proxy_test.ts.out",
- http_server: true,
-});
-
- itest!(_046_tsx {
- args: "run --quiet --reload run/046_jsx_test.tsx",
- output: "run/046_jsx_test.tsx.out",
- });
-
- itest!(_047_jsx {
- args: "run --quiet --reload run/047_jsx_test.jsx",
- output: "run/047_jsx_test.jsx.out",
- });
-
- itest!(_048_media_types_jsx {
- args: "run --reload run/048_media_types_jsx.ts",
- output: "run/048_media_types_jsx.ts.out",
- http_server: true,
- });
-
- itest!(_052_no_remote_flag {
- args:
- "run --reload --check --no-remote http://127.0.0.1:4545/run/019_media_types.ts",
- output: "run/052_no_remote_flag.out",
- exit_code: 1,
- http_server: true,
-});
-
- itest!(_056_make_temp_file_write_perm {
- args:
- "run --quiet --allow-read --allow-write=./subdir/ run/056_make_temp_file_write_perm.ts",
- output: "run/056_make_temp_file_write_perm.out",
-});
-
- itest!(_058_tasks_microtasks_close {
- args: "run --quiet run/058_tasks_microtasks_close.ts",
- output: "run/058_tasks_microtasks_close.ts.out",
- });
-
- itest!(_059_fs_relative_path_perm {
- args: "run run/059_fs_relative_path_perm.ts",
- output: "run/059_fs_relative_path_perm.ts.out",
- exit_code: 1,
- });
-
- itest!(_070_location {
- args: "run --location https://foo/bar?baz#bat run/070_location.ts",
- output: "run/070_location.ts.out",
- });
-
- itest!(_071_location_unset {
- args: "run run/071_location_unset.ts",
- output: "run/071_location_unset.ts.out",
- });
-
- itest!(_072_location_relative_fetch {
- args: "run --location http://127.0.0.1:4545/ --allow-net run/072_location_relative_fetch.ts",
- output: "run/072_location_relative_fetch.ts.out",
- http_server: true,
-});
-
- // tests the beforeunload event
- itest!(beforeunload_event {
- args: "run run/before_unload.js",
- output: "run/before_unload.js.out",
- });
-
- // tests the serialization of webstorage (both localStorage and sessionStorage)
- itest!(webstorage_serialization {
- args: "run run/webstorage/serialization.ts",
- output: "run/webstorage/serialization.ts.out",
- });
-
- // tests to ensure that when `--location` is set, all code shares the same
- // localStorage cache based on the origin of the location URL.
- #[test]
- fn webstorage_location_shares_origin() {
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--location")
- .arg("https://example.com/a.ts")
- .arg("run/webstorage/fixture.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 0 }\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--location")
- .arg("https://example.com/b.ts")
- .arg("run/webstorage/logger.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
- }
-
- // test to ensure that when a --config file is set, but no --location, that
- // storage persists against unique configuration files.
- #[test]
- fn webstorage_config_file() {
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--config")
- .arg("run/webstorage/config_a.jsonc")
- .arg("run/webstorage/fixture.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 0 }\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--config")
- .arg("run/webstorage/config_b.jsonc")
- .arg("run/webstorage/logger.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 0 }\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--config")
- .arg("run/webstorage/config_a.jsonc")
- .arg("run/webstorage/logger.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
- }
-
- // tests to ensure `--config` does not effect persisted storage when a
- // `--location` is provided.
- #[test]
- fn webstorage_location_precedes_config() {
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--location")
- .arg("https://example.com/a.ts")
- .arg("--config")
- .arg("run/webstorage/config_a.jsonc")
- .arg("run/webstorage/fixture.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 0 }\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--location")
- .arg("https://example.com/b.ts")
- .arg("--config")
- .arg("run/webstorage/config_b.jsonc")
- .arg("run/webstorage/logger.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
- }
-
- // test to ensure that when there isn't a configuration or location, that the
- // main module is used to determine how to persist storage data.
- #[test]
- fn webstorage_main_module() {
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("run/webstorage/fixture.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 0 }\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("run/webstorage/logger.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 0 }\n");
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("run/webstorage/fixture.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, b"Storage { length: 1, hello: \"deno\" }\n");
- }
-
- itest!(_075_import_local_query_hash {
- args: "run run/075_import_local_query_hash.ts",
- output: "run/075_import_local_query_hash.ts.out",
- });
-
- itest!(_077_fetch_empty {
- args: "run -A run/077_fetch_empty.ts",
- output: "run/077_fetch_empty.ts.out",
- exit_code: 1,
- });
-
- itest!(_078_unload_on_exit {
- args: "run run/078_unload_on_exit.ts",
- output: "run/078_unload_on_exit.ts.out",
- exit_code: 1,
- });
-
- itest!(_079_location_authentication {
- args:
- "run --location https://foo:bar@baz/qux run/079_location_authentication.ts",
- output: "run/079_location_authentication.ts.out",
-});
-
- itest!(_081_location_relative_fetch_redirect {
- args: "run --location http://127.0.0.1:4546/ --allow-net run/081_location_relative_fetch_redirect.ts",
- output: "run/081_location_relative_fetch_redirect.ts.out",
- http_server: true,
- });
-
- itest!(_082_prepare_stack_trace_throw {
- args: "run run/082_prepare_stack_trace_throw.js",
- output: "run/082_prepare_stack_trace_throw.js.out",
- exit_code: 1,
- });
-
- #[test]
- fn _083_legacy_external_source_map() {
- let _g = util::http_server();
- let deno_dir = TempDir::new();
- let module_url = url::Url::parse(
- "http://localhost:4545/run/083_legacy_external_source_map.ts",
- )
- .unwrap();
- // Write a faulty old external source map.
- let faulty_map_path = deno_dir.path().join("gen/http/localhost_PORT4545/9576bd5febd0587c5c4d88d57cb3ac8ebf2600c529142abe3baa9a751d20c334.js.map");
- std::fs::create_dir_all(faulty_map_path.parent().unwrap()).unwrap();
- std::fs::write(faulty_map_path, "{\"version\":3,\"file\":\"\",\"sourceRoot\":\"\",\"sources\":[\"http://localhost:4545/083_legacy_external_source_map.ts\"],\"names\":[],\"mappings\":\";AAAA,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC\"}").unwrap();
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(module_url.to_string())
- .output()
- .unwrap();
- // Before https://github.com/denoland/deno/issues/6965 was fixed, the faulty
- // old external source map would cause a panic while formatting the error
- // and the exit code would be 101. The external source map should be ignored
- // in favor of the inline one.
- assert_eq!(output.status.code(), Some(1));
- let out = std::str::from_utf8(&output.stdout).unwrap();
- assert_eq!(out, "");
- }
-
- itest!(dynamic_import_async_error {
- args: "run --allow-read run/dynamic_import_async_error/main.ts",
- output: "run/dynamic_import_async_error/main.out",
- });
-
- itest!(dynamic_import_already_rejected {
- args: "run --allow-read run/dynamic_import_already_rejected/main.ts",
- output: "run/dynamic_import_already_rejected/main.out",
- });
-
- itest!(no_check_imports_not_used_as_values {
- args: "run --config run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json --no-check run/no_check_imports_not_used_as_values/main.ts",
- output: "run/no_check_imports_not_used_as_values/main.out",
- });
-
- itest!(_088_dynamic_import_already_evaluating {
- args: "run --allow-read run/088_dynamic_import_already_evaluating.ts",
- output: "run/088_dynamic_import_already_evaluating.ts.out",
- });
-
- // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
- itest!(_089_run_allow_list {
- args: "run --unstable --allow-run=curl run/089_run_allow_list.ts",
- output: "run/089_run_allow_list.ts.out",
- });
-
- #[test]
- fn _090_run_permissions_request() {
- let args = "run --quiet run/090_run_permissions_request.ts";
- use util::PtyData::*;
- util::test_pty2(args, vec![
- Output("⚠️ ️Deno requests run access to \"ls\". Run again with --allow-run to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
- Input("y\n"),
- Output("⚠️ ️Deno requests run access to \"cat\". Run again with --allow-run to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
- Input("n\n"),
- Output("granted\r\n"),
- Output("prompt\r\n"),
- Output("denied\r\n"),
- ]);
- }
-
- itest!(_091_use_define_for_class_fields {
- args: "run --check run/091_use_define_for_class_fields.ts",
- output: "run/091_use_define_for_class_fields.ts.out",
- exit_code: 1,
- });
-
- itest!(_092_import_map_unmapped_bare_specifier {
- args: "run --import-map import_maps/import_map.json run/092_import_map_unmapped_bare_specifier.ts",
- output: "run/092_import_map_unmapped_bare_specifier.ts.out",
- exit_code: 1,
-});
-
- itest!(js_import_detect {
- args: "run --quiet --reload run/js_import_detect.ts",
- output: "run/js_import_detect.ts.out",
- exit_code: 0,
- });
-
- itest!(blob_gc_finalization {
- args: "run run/blob_gc_finalization.js",
- output: "run/blob_gc_finalization.js.out",
- exit_code: 0,
- });
-
- itest!(fetch_response_finalization {
- args:
- "run --v8-flags=--expose-gc --allow-net run/fetch_response_finalization.js",
- output: "run/fetch_response_finalization.js.out",
- http_server: true,
- exit_code: 0,
-});
-
- itest!(import_type {
- args: "run --reload run/import_type.ts",
- output: "run/import_type.ts.out",
- });
-
- itest!(import_type_no_check {
- args: "run --reload --no-check run/import_type.ts",
- output: "run/import_type.ts.out",
- });
-
- itest!(private_field_presence {
- args: "run --reload run/private_field_presence.ts",
- output: "run/private_field_presence.ts.out",
- });
-
- itest!(private_field_presence_no_check {
- args: "run --reload --no-check run/private_field_presence.ts",
- output: "run/private_field_presence.ts.out",
- });
-
- // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
- itest!(lock_write_fetch {
- args:
- "run --quiet --allow-read --allow-write --allow-env --allow-run --unstable run/lock_write_fetch/main.ts",
- output: "run/lock_write_fetch/main.out",
- http_server: true,
- exit_code: 0,
-});
-
- itest!(lock_check_ok {
- args:
- "run --lock=run/lock_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
- output: "run/003_relative_import.ts.out",
- http_server: true,
-});
-
- itest!(lock_check_ok2 {
- args: "run --lock=run/lock_check_ok2.json run/019_media_types.ts",
- output: "run/019_media_types.ts.out",
- http_server: true,
- });
-
- itest!(lock_dynamic_imports {
- args: "run --lock=run/lock_dynamic_imports.json --allow-read --allow-net http://127.0.0.1:4545/run/013_dynamic_import.ts",
- output: "run/lock_dynamic_imports.out",
- exit_code: 10,
- http_server: true,
-});
-
- itest!(lock_check_err {
- args: "run --lock=run/lock_check_err.json http://127.0.0.1:4545/run/003_relative_import.ts",
- output: "run/lock_check_err.out",
- exit_code: 10,
- http_server: true,
-});
-
- itest!(lock_check_err2 {
- args: "run --lock=run/lock_check_err2.json run/019_media_types.ts",
- output: "run/lock_check_err2.out",
- exit_code: 10,
- http_server: true,
- });
-
- itest!(config_file_lock_path {
- args: "run --config=run/config_file_lock_path.json run/019_media_types.ts",
- output: "run/config_file_lock_path.out",
- exit_code: 10,
- http_server: true,
- });
-
- itest!(lock_flag_overrides_config_file_lock_path {
- args: "run --lock=run/lock_check_ok2.json --config=run/config_file_lock_path.json run/019_media_types.ts",
- output: "run/019_media_types.ts.out",
- http_server: true,
- });
-
- itest!(lock_v2_check_ok {
- args:
- "run --lock=run/lock_v2_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
- output: "run/003_relative_import.ts.out",
- http_server: true,
-});
-
- itest!(lock_v2_check_ok2 {
- args: "run --lock=run/lock_v2_check_ok2.json run/019_media_types.ts",
- output: "run/019_media_types.ts.out",
- http_server: true,
- });
-
- itest!(lock_v2_dynamic_imports {
- args: "run --lock=run/lock_v2_dynamic_imports.json --allow-read --allow-net http://127.0.0.1:4545/run/013_dynamic_import.ts",
- output: "run/lock_v2_dynamic_imports.out",
- exit_code: 10,
- http_server: true,
-});
-
- itest!(lock_v2_check_err {
- args: "run --lock=run/lock_v2_check_err.json http://127.0.0.1:4545/run/003_relative_import.ts",
- output: "run/lock_v2_check_err.out",
- exit_code: 10,
- http_server: true,
-});
-
- itest!(lock_v2_check_err2 {
- args: "run --lock=run/lock_v2_check_err2.json run/019_media_types.ts",
- output: "run/lock_v2_check_err2.out",
- exit_code: 10,
- http_server: true,
- });
-
- itest!(lock_only_http_and_https {
- args: "run --lock=run/lock_only_http_and_https/deno.lock run/lock_only_http_and_https/main.ts",
- output: "run/lock_only_http_and_https/main.out",
- http_server: true,
-});
-
- itest!(mts_dmts_mjs {
- args: "run subdir/import.mts",
- output: "run/mts_dmts_mjs.out",
- });
-
- itest!(mts_dmts_mjs_no_check {
- args: "run --no-check subdir/import.mts",
- output: "run/mts_dmts_mjs.out",
- });
-
- itest!(async_error {
- exit_code: 1,
- args: "run --reload run/async_error.ts",
- output: "run/async_error.ts.out",
- });
-
- itest!(config {
- args:
- "run --reload --config run/config/tsconfig.json --check run/config/main.ts",
- output: "run/config/main.out",
-});
-
- itest!(config_types {
- args:
- "run --reload --quiet --config run/config_types/tsconfig.json run/config_types/main.ts",
- output: "run/config_types/main.out",
-});
-
- itest!(config_types_remote {
- http_server: true,
- args: "run --reload --quiet --config run/config_types/remote.tsconfig.json run/config_types/main.ts",
- output: "run/config_types/main.out",
- });
-
- itest!(empty_typescript {
- args: "run --reload --check run/empty.ts",
- output_str: Some("Check file:[WILDCARD]/run/empty.ts\n"),
- });
-
- itest!(error_001 {
- args: "run --reload run/error_001.ts",
- exit_code: 1,
- output: "run/error_001.ts.out",
- });
-
- itest!(error_002 {
- args: "run --reload run/error_002.ts",
- exit_code: 1,
- output: "run/error_002.ts.out",
- });
-
- itest!(error_003_typescript {
- args: "run --reload --check run/error_003_typescript.ts",
- exit_code: 1,
- output: "run/error_003_typescript.ts.out",
- });
-
- // Supposing that we've already attempted to run error_003_typescript.ts
- // we want to make sure that JS wasn't emitted. Running again without reload flag
- // should result in the same output.
- // https://github.com/denoland/deno/issues/2436
- itest!(error_003_typescript2 {
- args: "run --check run/error_003_typescript.ts",
- exit_code: 1,
- output: "run/error_003_typescript.ts.out",
- });
-
- itest!(error_004_missing_module {
- args: "run --reload run/error_004_missing_module.ts",
- exit_code: 1,
- output: "run/error_004_missing_module.ts.out",
- });
-
- itest!(error_005_missing_dynamic_import {
- args:
- "run --reload --allow-read --quiet run/error_005_missing_dynamic_import.ts",
- exit_code: 1,
- output: "run/error_005_missing_dynamic_import.ts.out",
-});
-
- itest!(error_006_import_ext_failure {
- args: "run --reload run/error_006_import_ext_failure.ts",
- exit_code: 1,
- output: "run/error_006_import_ext_failure.ts.out",
- });
-
- itest!(error_007_any {
- args: "run --reload run/error_007_any.ts",
- exit_code: 1,
- output: "run/error_007_any.ts.out",
- });
-
- itest!(error_008_checkjs {
- args: "run --reload run/error_008_checkjs.js",
- exit_code: 1,
- output: "run/error_008_checkjs.js.out",
- });
-
- itest!(error_009_extensions_error {
- args: "run run/error_009_extensions_error.js",
- output: "run/error_009_extensions_error.js.out",
- exit_code: 1,
- });
-
- itest!(error_011_bad_module_specifier {
- args: "run --reload run/error_011_bad_module_specifier.ts",
- exit_code: 1,
- output: "run/error_011_bad_module_specifier.ts.out",
- });
-
- itest!(error_012_bad_dynamic_import_specifier {
- args: "run --reload --check run/error_012_bad_dynamic_import_specifier.ts",
- exit_code: 1,
- output: "run/error_012_bad_dynamic_import_specifier.ts.out",
- });
-
- itest!(error_013_missing_script {
- args: "run --reload missing_file_name",
- exit_code: 1,
- output: "run/error_013_missing_script.out",
- });
-
- itest!(error_014_catch_dynamic_import_error {
- args:
- "run --reload --allow-read run/error_014_catch_dynamic_import_error.js",
- output: "run/error_014_catch_dynamic_import_error.js.out",
- });
-
- itest!(error_015_dynamic_import_permissions {
- args: "run --reload --quiet run/error_015_dynamic_import_permissions.js",
- output: "run/error_015_dynamic_import_permissions.out",
- exit_code: 1,
- http_server: true,
- });
-
- // We have an allow-net flag but not allow-read, it should still result in error.
- itest!(error_016_dynamic_import_permissions2 {
- args:
- "run --reload --allow-net run/error_016_dynamic_import_permissions2.js",
- output: "run/error_016_dynamic_import_permissions2.out",
- exit_code: 1,
- http_server: true,
- });
-
- itest!(error_017_hide_long_source_ts {
- args: "run --reload --check run/error_017_hide_long_source_ts.ts",
- output: "run/error_017_hide_long_source_ts.ts.out",
- exit_code: 1,
- });
-
- itest!(error_018_hide_long_source_js {
- args: "run run/error_018_hide_long_source_js.js",
- output: "run/error_018_hide_long_source_js.js.out",
- exit_code: 1,
- });
-
- itest!(error_019_stack_function {
- args: "run run/error_019_stack_function.ts",
- output: "run/error_019_stack_function.ts.out",
- exit_code: 1,
- });
-
- itest!(error_020_stack_constructor {
- args: "run run/error_020_stack_constructor.ts",
- output: "run/error_020_stack_constructor.ts.out",
- exit_code: 1,
- });
-
- itest!(error_021_stack_method {
- args: "run run/error_021_stack_method.ts",
- output: "run/error_021_stack_method.ts.out",
- exit_code: 1,
- });
-
- itest!(error_022_stack_custom_error {
- args: "run run/error_022_stack_custom_error.ts",
- output: "run/error_022_stack_custom_error.ts.out",
- exit_code: 1,
- });
-
- itest!(error_023_stack_async {
- args: "run run/error_023_stack_async.ts",
- output: "run/error_023_stack_async.ts.out",
- exit_code: 1,
- });
-
- itest!(error_024_stack_promise_all {
- args: "run run/error_024_stack_promise_all.ts",
- output: "run/error_024_stack_promise_all.ts.out",
- exit_code: 1,
- });
-
- itest!(error_025_tab_indent {
- args: "run run/error_025_tab_indent",
- output: "run/error_025_tab_indent.out",
- exit_code: 1,
- });
-
- itest!(error_026_remote_import_error {
- args: "run run/error_026_remote_import_error.ts",
- output: "run/error_026_remote_import_error.ts.out",
- exit_code: 1,
- http_server: true,
- });
-
- itest!(error_for_await {
- args: "run --reload --check run/error_for_await.ts",
- output: "run/error_for_await.ts.out",
- exit_code: 1,
- });
-
- itest!(error_missing_module_named_import {
- args: "run --reload run/error_missing_module_named_import.ts",
- output: "run/error_missing_module_named_import.ts.out",
- exit_code: 1,
- });
-
- itest!(error_no_check {
- args: "run --reload --no-check run/error_no_check.ts",
- output: "run/error_no_check.ts.out",
- exit_code: 1,
- });
-
- itest!(error_syntax {
- args: "run --reload run/error_syntax.js",
- exit_code: 1,
- output: "run/error_syntax.js.out",
- });
-
- itest!(error_syntax_empty_trailing_line {
- args: "run --reload run/error_syntax_empty_trailing_line.mjs",
- exit_code: 1,
- output: "run/error_syntax_empty_trailing_line.mjs.out",
- });
-
- itest!(error_type_definitions {
- args: "run --reload --check run/error_type_definitions.ts",
- exit_code: 1,
- output: "run/error_type_definitions.ts.out",
- });
-
- itest!(error_local_static_import_from_remote_ts {
- args: "run --reload http://localhost:4545/run/error_local_static_import_from_remote.ts",
- exit_code: 1,
- http_server: true,
- output: "run/error_local_static_import_from_remote.ts.out",
- });
-
- itest!(error_local_static_import_from_remote_js {
- args: "run --reload http://localhost:4545/run/error_local_static_import_from_remote.js",
- exit_code: 1,
- http_server: true,
- output: "run/error_local_static_import_from_remote.js.out",
- });
-
- itest!(exit_error42 {
- exit_code: 42,
- args: "run --quiet --reload run/exit_error42.ts",
- output: "run/exit_error42.ts.out",
- });
-
- itest!(set_exit_code_0 {
- args: "run --no-check --unstable run/set_exit_code_0.ts",
- output_str: Some(""),
- exit_code: 0,
- });
-
- itest!(set_exit_code_1 {
- args: "run --no-check --unstable run/set_exit_code_1.ts",
- output_str: Some(""),
- exit_code: 42,
- });
-
- itest!(set_exit_code_2 {
- args: "run --no-check --unstable run/set_exit_code_2.ts",
- output_str: Some(""),
- exit_code: 42,
- });
-
- itest!(op_exit_op_set_exit_code_in_worker {
- args: "run --no-check --unstable --allow-read run/op_exit_op_set_exit_code_in_worker.ts",
- exit_code: 21,
- output_str: Some(""),
-});
-
- itest!(deno_exit_tampering {
- args: "run --no-check --unstable run/deno_exit_tampering.ts",
- output_str: Some(""),
- exit_code: 42,
- });
-
- itest!(heapstats {
- args: "run --quiet --unstable --v8-flags=--expose-gc run/heapstats.js",
- output: "run/heapstats.js.out",
- });
-
- itest!(finalization_registry {
- args:
- "run --quiet --unstable --v8-flags=--expose-gc run/finalization_registry.js",
- output: "run/finalization_registry.js.out",
-});
-
- itest!(https_import {
- args: "run --quiet --reload --cert tls/RootCA.pem run/https_import.ts",
- output: "run/https_import.ts.out",
- http_server: true,
- });
-
- itest!(if_main {
- args: "run --quiet --reload run/if_main.ts",
- output: "run/if_main.ts.out",
- });
-
- itest!(import_meta {
- args: "run --quiet --reload --import-map=run/import_meta/importmap.json run/import_meta/main.ts",
- output: "run/import_meta/main.out",
-});
-
- itest!(main_module {
- args: "run --quiet --allow-read --reload run/main_module/main.ts",
- output: "run/main_module/main.out",
- });
-
- itest!(no_check {
- args: "run --quiet --reload --no-check run/006_url_imports.ts",
- output: "run/006_url_imports.ts.out",
- http_server: true,
- });
-
- itest!(no_check_decorators {
- args: "run --quiet --reload --no-check run/no_check_decorators.ts",
- output: "run/no_check_decorators.ts.out",
- });
-
- itest!(check_remote {
- args: "run --quiet --reload --check=all run/no_check_remote.ts",
- output: "run/no_check_remote.ts.disabled.out",
- exit_code: 1,
- http_server: true,
- });
-
- itest!(no_check_remote {
- args: "run --quiet --reload --no-check=remote run/no_check_remote.ts",
- output: "run/no_check_remote.ts.enabled.out",
- http_server: true,
- });
-
- itest!(runtime_decorators {
- args: "run --quiet --reload --no-check run/runtime_decorators.ts",
- output: "run/runtime_decorators.ts.out",
- });
-
- itest!(seed_random {
- args: "run --seed=100 run/seed_random.js",
- output: "run/seed_random.js.out",
- });
-
- itest!(type_definitions {
- args: "run --reload run/type_definitions.ts",
- output: "run/type_definitions.ts.out",
- });
-
- itest!(type_definitions_for_export {
- args: "run --reload --check run/type_definitions_for_export.ts",
- output: "run/type_definitions_for_export.ts.out",
- exit_code: 1,
- });
-
- itest!(type_directives_01 {
- args: "run --reload --check=all -L debug run/type_directives_01.ts",
- output: "run/type_directives_01.ts.out",
- http_server: true,
- });
-
- itest!(type_directives_02 {
- args: "run --reload --check=all -L debug run/type_directives_02.ts",
- output: "run/type_directives_02.ts.out",
- });
-
- itest!(type_directives_js_main {
- args: "run --reload -L debug run/type_directives_js_main.js",
- output: "run/type_directives_js_main.js.out",
- exit_code: 0,
- });
-
- itest!(type_directives_redirect {
- args: "run --reload --check run/type_directives_redirect.ts",
- output: "run/type_directives_redirect.ts.out",
- http_server: true,
- });
-
- itest!(type_headers_deno_types {
- args: "run --reload --check run/type_headers_deno_types.ts",
- output: "run/type_headers_deno_types.ts.out",
- http_server: true,
- });
-
- itest!(ts_type_imports {
- args: "run --reload --check run/ts_type_imports.ts",
- output: "run/ts_type_imports.ts.out",
- exit_code: 1,
- });
-
- itest!(ts_decorators {
- args: "run --reload --check run/ts_decorators.ts",
- output: "run/ts_decorators.ts.out",
- });
-
- itest!(ts_type_only_import {
- args: "run --reload --check run/ts_type_only_import.ts",
- output: "run/ts_type_only_import.ts.out",
- });
-
- itest!(swc_syntax_error {
- args: "run --reload --check run/swc_syntax_error.ts",
- output: "run/swc_syntax_error.ts.out",
- exit_code: 1,
- });
-
- itest!(unbuffered_stderr {
- args: "run --reload run/unbuffered_stderr.ts",
- output: "run/unbuffered_stderr.ts.out",
- });
-
- itest!(unbuffered_stdout {
- args: "run --quiet --reload run/unbuffered_stdout.ts",
- output: "run/unbuffered_stdout.ts.out",
- });
-
- itest!(v8_flags_run {
- args: "run --v8-flags=--expose-gc run/v8_flags.js",
- output: "run/v8_flags.js.out",
- });
-
- itest!(v8_flags_unrecognized {
- args: "repl --v8-flags=--foo,bar,--trace-gc,-baz",
- output: "run/v8_flags_unrecognized.out",
- exit_code: 1,
- });
-
- itest!(v8_help {
- args: "repl --v8-flags=--help",
- output: "run/v8_help.out",
- });
-
- itest!(unsupported_dynamic_import_scheme {
- args: "eval import('xxx:')",
- output: "run/unsupported_dynamic_import_scheme.out",
- exit_code: 1,
- });
-
- itest!(wasm {
- args: "run --quiet run/wasm.ts",
- output: "run/wasm.ts.out",
- });
-
- itest!(wasm_shared {
- args: "run --quiet run/wasm_shared.ts",
- output: "run/wasm_shared.out",
- });
-
- itest!(wasm_async {
- args: "run run/wasm_async.js",
- output: "run/wasm_async.out",
- });
-
- itest!(wasm_unreachable {
- args: "run --allow-read run/wasm_unreachable.js",
- output: "run/wasm_unreachable.out",
- exit_code: 1,
- });
-
- itest!(wasm_url {
- args: "run --quiet --allow-net=localhost:4545 run/wasm_url.js",
- output: "run/wasm_url.out",
- exit_code: 1,
- http_server: true,
- });
-
- itest!(weakref {
- args: "run --quiet --reload run/weakref.ts",
- output: "run/weakref.ts.out",
- });
-
- itest!(top_level_await_order {
- args: "run --allow-read run/top_level_await/order.js",
- output: "run/top_level_await/order.out",
- });
-
- itest!(top_level_await_loop {
- args: "run --allow-read run/top_level_await/loop.js",
- output: "run/top_level_await/loop.out",
- });
-
- itest!(top_level_await_circular {
- args: "run --allow-read run/top_level_await/circular.js",
- output: "run/top_level_await/circular.out",
- exit_code: 1,
- });
-
- // Regression test for https://github.com/denoland/deno/issues/11238.
- itest!(top_level_await_nested {
- args: "run --allow-read run/top_level_await/nested/main.js",
- output: "run/top_level_await/nested.out",
- });
-
- itest!(top_level_await_unresolved {
- args: "run run/top_level_await/unresolved.js",
- output: "run/top_level_await/unresolved.out",
- exit_code: 1,
- });
-
- itest!(top_level_await {
- args: "run --allow-read run/top_level_await/top_level_await.js",
- output: "run/top_level_await/top_level_await.out",
- });
-
- itest!(top_level_await_ts {
- args: "run --quiet --allow-read run/top_level_await/top_level_await.ts",
- output: "run/top_level_await/top_level_await.out",
- });
-
- itest!(top_level_for_await {
- args: "run --quiet run/top_level_await/top_level_for_await.js",
- output: "run/top_level_await/top_level_for_await.out",
- });
-
- itest!(top_level_for_await_ts {
- args: "run --quiet run/top_level_await/top_level_for_await.ts",
- output: "run/top_level_await/top_level_for_await.out",
- });
-
- itest!(unstable_disabled {
- args: "run --reload --check run/unstable.ts",
- exit_code: 1,
- output: "run/unstable_disabled.out",
- });
-
- itest!(unstable_enabled {
- args: "run --quiet --reload --unstable run/unstable.ts",
- output: "run/unstable_enabled.out",
- });
-
- itest!(unstable_disabled_js {
- args: "run --reload run/unstable.js",
- output: "run/unstable_disabled_js.out",
- });
-
- itest!(unstable_enabled_js {
- args: "run --quiet --reload --unstable run/unstable.ts",
- output: "run/unstable_enabled_js.out",
- });
-
- itest!(unstable_worker {
- args: "run --reload --unstable --quiet --allow-read run/unstable_worker.ts",
- output: "run/unstable_worker.ts.out",
- });
-
- itest!(import_compression {
- args: "run --quiet --reload --allow-net run/import_compression/main.ts",
- output: "run/import_compression/main.out",
- http_server: true,
- });
-
- itest!(disallow_http_from_https_js {
- args: "run --quiet --reload --cert tls/RootCA.pem https://localhost:5545/run/disallow_http_from_https.js",
- output: "run/disallow_http_from_https_js.out",
- http_server: true,
- exit_code: 1,
-});
-
- itest!(disallow_http_from_https_ts {
- args: "run --quiet --reload --cert tls/RootCA.pem https://localhost:5545/run/disallow_http_from_https.ts",
- output: "run/disallow_http_from_https_ts.out",
- http_server: true,
- exit_code: 1,
-});
-
- itest!(dynamic_import_conditional {
- args: "run --quiet --reload run/dynamic_import_conditional.js",
- output: "run/dynamic_import_conditional.js.out",
- });
-
- itest!(tsx_imports {
- args: "run --reload --check run/tsx_imports/tsx_imports.ts",
- output: "run/tsx_imports/tsx_imports.ts.out",
- });
-
- itest!(fix_dynamic_import_errors {
- args: "run --reload run/fix_dynamic_import_errors.js",
- output: "run/fix_dynamic_import_errors.js.out",
- });
-
- itest!(fix_emittable_skipped {
- args: "run --reload run/fix_emittable_skipped.js",
- output: "run/fix_emittable_skipped.ts.out",
- });
-
- itest!(fix_js_import_js {
- args: "run --quiet --reload run/fix_js_import_js.ts",
- output: "run/fix_js_import_js.ts.out",
- });
-
- itest!(fix_js_imports {
- args: "run --quiet --reload run/fix_js_imports.ts",
- output: "run/fix_js_imports.ts.out",
- });
-
- itest!(fix_tsc_file_exists {
- args: "run --quiet --reload tsc/test.js",
- output: "run/fix_tsc_file_exists.out",
- });
-
- itest!(fix_worker_dispatchevent {
- args: "run --quiet --reload run/fix_worker_dispatchevent.ts",
- output: "run/fix_worker_dispatchevent.ts.out",
- });
-
- itest!(es_private_fields {
- args: "run --quiet --reload run/es_private_fields.js",
- output: "run/es_private_fields.js.out",
- });
-
- itest!(cjs_imports {
- args: "run --quiet --reload run/cjs_imports/main.ts",
- output: "run/cjs_imports/main.out",
- });
-
- itest!(ts_import_from_js {
- args: "run --quiet --reload run/ts_import_from_js/main.js",
- output: "run/ts_import_from_js/main.out",
- http_server: true,
- });
-
- itest!(jsx_import_from_ts {
- args: "run --quiet --reload run/jsx_import_from_ts.ts",
- output: "run/jsx_import_from_ts.ts.out",
- });
-
- itest!(jsx_import_source_pragma {
- args: "run --reload run/jsx_import_source_pragma.tsx",
- output: "run/jsx_import_source.out",
- http_server: true,
- });
-
- itest!(jsx_import_source_pragma_with_config {
- args:
- "run --reload --config jsx/deno-jsx.jsonc --no-lock run/jsx_import_source_pragma.tsx",
- output: "run/jsx_import_source.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_pragma_with_dev_config {
- args:
- "run --reload --config jsx/deno-jsxdev.jsonc --no-lock run/jsx_import_source_pragma.tsx",
- output: "run/jsx_import_source_dev.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_no_pragma {
- args:
- "run --reload --config jsx/deno-jsx.jsonc --no-lock run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_no_pragma_dev {
- args: "run --reload --config jsx/deno-jsxdev.jsonc --no-lock run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_dev.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_pragma_import_map {
- args: "run --reload --import-map jsx/import-map.json run/jsx_import_source_pragma_import_map.tsx",
- output: "run/jsx_import_source_import_map.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_pragma_import_map_dev {
- args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsxdev-import-map.jsonc run/jsx_import_source_pragma_import_map.tsx",
- output: "run/jsx_import_source_import_map_dev.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_import_map {
- args: "run --reload --import-map jsx/import-map.json --no-lock --config jsx/deno-jsx-import-map.jsonc run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_import_map.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_import_map_dev {
- args: "run --reload --import-map jsx/import-map.json --no-lock --config jsx/deno-jsxdev-import-map.jsonc run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_import_map_dev.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_import_map_scoped {
- args: "run --reload --import-map jsx/import-map-scoped.json --no-lock --config jsx/deno-jsx-import-map.jsonc subdir/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_import_map.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_import_map_scoped_dev {
- args: "run --reload --import-map jsx/import-map-scoped.json --no-lock --config jsx/deno-jsxdev-import-map.jsonc subdir/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_import_map_dev.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_pragma_no_check {
- args: "run --reload --no-check run/jsx_import_source_pragma.tsx",
- output: "run/jsx_import_source.out",
- http_server: true,
- });
-
- itest!(jsx_import_source_pragma_with_config_no_check {
- args: "run --reload --config jsx/deno-jsx.jsonc --no-lock --no-check run/jsx_import_source_pragma.tsx",
- output: "run/jsx_import_source.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_no_pragma_no_check {
- args:
- "run --reload --config jsx/deno-jsx.jsonc --no-lock --no-check run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_pragma_import_map_no_check {
- args: "run --reload --import-map jsx/import-map.json --no-check run/jsx_import_source_pragma_import_map.tsx",
- output: "run/jsx_import_source_import_map.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_import_map_no_check {
- args: "run --reload --import-map jsx/import-map.json --no-lock --config jsx/deno-jsx-import-map.jsonc --no-check run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_import_map.out",
- http_server: true,
-});
-
- itest!(jsx_import_source_error {
- args: "run --config jsx/deno-jsx-error.jsonc --check run/jsx_import_source_no_pragma.tsx",
- output: "run/jsx_import_source_error.out",
- exit_code: 1,
-});
-
- // TODO(#11128): Flaky. Re-enable later.
- // itest!(single_compile_with_reload {
- // args: "run --relcert/oad --allow-read run/single_compile_with_reload.ts",
- // output: "run/single_compile_with_reload.ts.out",
- // });
-
- itest!(proto_exploit {
- args: "run run/proto_exploit.js",
- output: "run/proto_exploit.js.out",
- });
-
- itest!(reference_types {
- args: "run --reload --quiet run/reference_types.ts",
- output: "run/reference_types.ts.out",
- });
-
- itest!(references_types_remote {
- http_server: true,
- args: "run --reload --quiet run/reference_types_remote.ts",
- output: "run/reference_types_remote.ts.out",
- });
-
- itest!(reference_types_error {
- args:
- "run --config run/checkjs.tsconfig.json --check run/reference_types_error.js",
- output: "run/reference_types_error.js.out",
- exit_code: 1,
-});
-
- itest!(reference_types_error_no_check {
- args: "run --no-check run/reference_types_error.js",
- output_str: Some(""),
- });
-
- itest!(import_data_url_error_stack {
- args: "run --quiet --reload run/import_data_url_error_stack.ts",
- output: "run/import_data_url_error_stack.ts.out",
- exit_code: 1,
- });
-
- itest!(import_data_url_import_relative {
- args: "run --quiet --reload run/import_data_url_import_relative.ts",
- output: "run/import_data_url_import_relative.ts.out",
- exit_code: 1,
- });
-
- itest!(import_data_url_import_map {
- args: "run --quiet --reload --import-map import_maps/import_map.json run/import_data_url.ts",
- output: "run/import_data_url.ts.out",
- });
-
- itest!(import_data_url_imports {
- args: "run --quiet --reload run/import_data_url_imports.ts",
- output: "run/import_data_url_imports.ts.out",
- http_server: true,
- });
-
- itest!(import_data_url_jsx {
- args: "run --quiet --reload run/import_data_url_jsx.ts",
- output: "run/import_data_url_jsx.ts.out",
- });
-
- itest!(import_data_url {
- args: "run --quiet --reload run/import_data_url.ts",
- output: "run/import_data_url.ts.out",
- });
-
- itest!(import_dynamic_data_url {
- args: "run --quiet --reload run/import_dynamic_data_url.ts",
- output: "run/import_dynamic_data_url.ts.out",
- });
-
- itest!(import_blob_url_error_stack {
- args: "run --quiet --reload run/import_blob_url_error_stack.ts",
- output: "run/import_blob_url_error_stack.ts.out",
- exit_code: 1,
- });
-
- itest!(import_blob_url_import_relative {
- args: "run --quiet --reload run/import_blob_url_import_relative.ts",
- output: "run/import_blob_url_import_relative.ts.out",
- exit_code: 1,
- });
-
- itest!(import_blob_url_imports {
- args:
- "run --quiet --reload --allow-net=localhost:4545 run/import_blob_url_imports.ts",
- output: "run/import_blob_url_imports.ts.out",
- http_server: true,
-});
-
- itest!(import_blob_url_jsx {
- args: "run --quiet --reload run/import_blob_url_jsx.ts",
- output: "run/import_blob_url_jsx.ts.out",
- });
-
- itest!(import_blob_url {
- args: "run --quiet --reload run/import_blob_url.ts",
- output: "run/import_blob_url.ts.out",
- });
-
- itest!(import_file_with_colon {
- args: "run --quiet --reload run/import_file_with_colon.ts",
- output: "run/import_file_with_colon.ts.out",
- http_server: true,
- });
-
- itest!(import_extensionless {
- args: "run --quiet --reload run/import_extensionless.ts",
- output: "run/import_extensionless.ts.out",
- http_server: true,
- });
-
- itest!(classic_workers_event_loop {
- args:
- "run --enable-testing-features-do-not-use run/classic_workers_event_loop.js",
- output: "run/classic_workers_event_loop.js.out",
-});
-
- // FIXME(bartlomieju): disabled, because this test is very flaky on CI
- // itest!(local_sources_not_cached_in_memory {
- // args: "run --allow-read --allow-write run/no_mem_cache.js",
- // output: "run/no_mem_cache.js.out",
- // });
-
- // This test checks that inline source map data is used. It uses a hand crafted
- // source map that maps to a file that exists, but is not loaded into the module
- // graph (inline_js_source_map_2.ts) (because there are no direct dependencies).
- // Source line is not remapped because no inline source contents are included in
- // the sourcemap and the file is not present in the dependency graph.
- itest!(inline_js_source_map_2 {
- args: "run --quiet run/inline_js_source_map_2.js",
- output: "run/inline_js_source_map_2.js.out",
- exit_code: 1,
- });
-
- // This test checks that inline source map data is used. It uses a hand crafted
- // source map that maps to a file that exists, but is not loaded into the module
- // graph (inline_js_source_map_2.ts) (because there are no direct dependencies).
- // Source line remapped using th inline source contents that are included in the
- // inline source map.
- itest!(inline_js_source_map_2_with_inline_contents {
- args: "run --quiet run/inline_js_source_map_2_with_inline_contents.js",
- output: "run/inline_js_source_map_2_with_inline_contents.js.out",
- exit_code: 1,
- });
-
- // This test checks that inline source map data is used. It uses a hand crafted
- // source map that maps to a file that exists, and is loaded into the module
- // graph because of a direct import statement (inline_js_source_map.ts). The
- // source map was generated from an earlier version of this file, where the throw
- // was not commented out. The source line is remapped using source contents that
- // from the module graph.
- itest!(inline_js_source_map_with_contents_from_graph {
- args: "run --quiet run/inline_js_source_map_with_contents_from_graph.js",
- output: "run/inline_js_source_map_with_contents_from_graph.js.out",
- exit_code: 1,
- http_server: true,
- });
-
- // This test ensures that a descriptive error is shown when we're unable to load
- // the import map. Even though this tests only the `run` subcommand, we can be sure
- // that the error message is similar for other subcommands as they all use
- // `program_state.maybe_import_map` to access the import map underneath.
- itest!(error_import_map_unable_to_load {
- args:
- "run --import-map=import_maps/does_not_exist.json import_maps/test.ts",
- output: "run/error_import_map_unable_to_load.out",
- exit_code: 1,
- });
-
- // Test that setting `self` in the main thread to some other value doesn't break
- // the world.
- itest!(replace_self {
- args: "run run/replace_self.js",
- output: "run/replace_self.js.out",
- });
-
- itest!(worker_event_handler_test {
- args: "run --quiet --reload --allow-read run/worker_event_handler_test.js",
- output: "run/worker_event_handler_test.js.out",
- });
-
- itest!(worker_close_race {
- args: "run --quiet --reload --allow-read run/worker_close_race.js",
- output: "run/worker_close_race.js.out",
- });
-
- itest!(worker_drop_handle_race {
- args: "run --quiet --reload --allow-read run/worker_drop_handle_race.js",
- output: "run/worker_drop_handle_race.js.out",
- exit_code: 1,
- });
-
- itest!(worker_drop_handle_race_terminate {
- args: "run --unstable run/worker_drop_handle_race_terminate.js",
- output: "run/worker_drop_handle_race_terminate.js.out",
- });
-
- itest!(worker_close_nested {
- args: "run --quiet --reload --allow-read run/worker_close_nested.js",
- output: "run/worker_close_nested.js.out",
- });
-
- itest!(worker_message_before_close {
- args:
- "run --quiet --reload --allow-read run/worker_message_before_close.js",
- output: "run/worker_message_before_close.js.out",
- });
-
- itest!(worker_close_in_wasm_reactions {
- args:
- "run --quiet --reload --allow-read run/worker_close_in_wasm_reactions.js",
- output: "run/worker_close_in_wasm_reactions.js.out",
- });
-
- itest!(shebang_tsc {
- args: "run --quiet --check run/shebang.ts",
- output: "run/shebang.ts.out",
- });
-
- itest!(shebang_swc {
- args: "run --quiet run/shebang.ts",
- output: "run/shebang.ts.out",
- });
-
- itest!(shebang_with_json_imports_tsc {
- args: "run --quiet import_assertions/json_with_shebang.ts",
- output: "import_assertions/json_with_shebang.ts.out",
- exit_code: 1,
- });
-
- itest!(shebang_with_json_imports_swc {
- args: "run --quiet --no-check import_assertions/json_with_shebang.ts",
- output: "import_assertions/json_with_shebang.ts.out",
- exit_code: 1,
- });
-
- #[test]
- fn no_validate_asm() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("run/no_validate_asm.js")
- .stderr(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert!(output.stderr.is_empty());
- assert!(output.stdout.is_empty());
- }
-
- #[test]
- fn exec_path() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--allow-read")
- .arg("run/exec_path.ts")
- .stdout(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
- let actual =
- std::fs::canonicalize(std::path::Path::new(stdout_str)).unwrap();
- let expected = std::fs::canonicalize(util::deno_exe_path()).unwrap();
- assert_eq!(expected, actual);
- }
-
- #[cfg(windows)]
- // Clippy suggests to remove the `NoStd` prefix from all variants. I disagree.
- #[allow(clippy::enum_variant_names)]
- enum WinProcConstraints {
- NoStdIn,
- NoStdOut,
- NoStdErr,
- }
-
- #[cfg(windows)]
- fn run_deno_script_constrained(
- script_path: std::path::PathBuf,
- constraints: WinProcConstraints,
- ) -> Result<(), i64> {
- let file_path = "assets/DenoWinRunner.ps1";
- let constraints = match constraints {
- WinProcConstraints::NoStdIn => "1",
- WinProcConstraints::NoStdOut => "2",
- WinProcConstraints::NoStdErr => "4",
- };
- let deno_exe_path = util::deno_exe_path()
- .into_os_string()
- .into_string()
- .unwrap();
-
- let deno_script_path = script_path.into_os_string().into_string().unwrap();
-
- let args = vec![&deno_exe_path[..], &deno_script_path[..], constraints];
- util::run_powershell_script_file(file_path, args)
- }
-
- #[cfg(windows)]
- #[test]
- fn should_not_panic_on_no_stdin() {
- let output = run_deno_script_constrained(
- util::testdata_path().join("echo.ts"),
- WinProcConstraints::NoStdIn,
- );
- output.unwrap();
- }
-
- #[cfg(windows)]
- #[test]
- fn should_not_panic_on_no_stdout() {
- let output = run_deno_script_constrained(
- util::testdata_path().join("echo.ts"),
- WinProcConstraints::NoStdOut,
- );
- output.unwrap();
- }
-
- #[cfg(windows)]
- #[test]
- fn should_not_panic_on_no_stderr() {
- let output = run_deno_script_constrained(
- util::testdata_path().join("echo.ts"),
- WinProcConstraints::NoStdErr,
- );
- output.unwrap();
- }
-
- #[cfg(not(windows))]
- #[test]
- fn should_not_panic_on_undefined_home_environment_variable() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("echo.ts")
- .env_remove("HOME")
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[test]
- fn should_not_panic_on_undefined_deno_dir_environment_variable() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("echo.ts")
- .env_remove("DENO_DIR")
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[cfg(not(windows))]
- #[test]
- fn should_not_panic_on_undefined_deno_dir_and_home_environment_variables() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("echo.ts")
- .env_remove("DENO_DIR")
- .env_remove("HOME")
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- #[test]
- fn rust_log() {
- // Without RUST_LOG the stderr is empty.
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("run/001_hello.js")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert!(output.stderr.is_empty());
-
- // With RUST_LOG the stderr is not empty.
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("run/001_hello.js")
- .env("RUST_LOG", "debug")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- assert!(!output.stderr.is_empty());
- }
-
- #[test]
- fn dont_cache_on_check_fail() {
- let deno_dir = util::new_deno_dir();
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check=all")
- .arg("--reload")
- .arg("run/error_003_typescript.ts")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert!(!output.stderr.is_empty());
-
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
- let output = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check=all")
- .arg("run/error_003_typescript.ts")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert!(!output.stderr.is_empty());
- }
-
- mod permissions {
- use super::itest;
- use test_util as util;
-
- // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
- #[test]
- fn with_allow() {
- for permission in &util::PERMISSION_VARIANTS {
- let status = util::deno_cmd()
- .current_dir(&util::testdata_path())
- .arg("run")
- .arg("--unstable")
- .arg(format!("--allow-{0}", permission))
- .arg("run/permission_test.ts")
- .arg(format!("{0}Required", permission))
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
- }
-
- // TODO(bartlomieju): remove --unstable once Deno.spawn is stabilized
- #[test]
- fn without_allow() {
- for permission in &util::PERMISSION_VARIANTS {
- let (_, err) = util::run_and_collect_output(
- false,
- &format!(
- "run --unstable run/permission_test.ts {0}Required",
- permission
- ),
- None,
- None,
- false,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
- }
-
- #[test]
- fn rw_inside_project_dir() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- for permission in &PERMISSION_VARIANTS {
- let status = util::deno_cmd()
- .current_dir(&util::testdata_path())
- .arg("run")
- .arg(format!(
- "--allow-{0}={1}",
- permission,
- util::testdata_path()
- .into_os_string()
- .into_string()
- .unwrap()
- ))
- .arg("run/complex_permissions_test.ts")
- .arg(permission)
- .arg("run/complex_permissions_test.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
- }
-
- #[test]
- fn rw_outside_test_dir() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- for permission in &PERMISSION_VARIANTS {
- let (_, err) = util::run_and_collect_output(
- false,
- &format!(
- "run --allow-{0}={1} run/complex_permissions_test.ts {0} {2}",
- permission,
- util::testdata_path()
- .into_os_string()
- .into_string()
- .unwrap(),
- util::root_path()
- .join("Cargo.toml")
- .into_os_string()
- .into_string()
- .unwrap(),
- ),
- None,
- None,
- false,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
- }
-
- #[test]
- fn rw_inside_test_dir() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- for permission in &PERMISSION_VARIANTS {
- let status = util::deno_cmd()
- .current_dir(&util::testdata_path())
- .arg("run")
- .arg(format!(
- "--allow-{0}={1}",
- permission,
- util::testdata_path()
- .into_os_string()
- .into_string()
- .unwrap()
- ))
- .arg("run/complex_permissions_test.ts")
- .arg(permission)
- .arg("run/complex_permissions_test.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
- }
-
- #[test]
- fn rw_outside_test_and_js_dir() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- let test_dir = util::testdata_path()
- .into_os_string()
- .into_string()
- .unwrap();
- let js_dir = util::root_path()
- .join("js")
- .into_os_string()
- .into_string()
- .unwrap();
- for permission in &PERMISSION_VARIANTS {
- let (_, err) = util::run_and_collect_output(
- false,
- &format!(
- "run --allow-{0}={1},{2} run/complex_permissions_test.ts {0} {3}",
- permission,
- test_dir,
- js_dir,
- util::root_path()
- .join("Cargo.toml")
- .into_os_string()
- .into_string()
- .unwrap(),
- ),
- None,
- None,
- false,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
- }
-
- #[test]
- fn rw_inside_test_and_js_dir() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- let test_dir = util::testdata_path()
- .into_os_string()
- .into_string()
- .unwrap();
- let js_dir = util::root_path()
- .join("js")
- .into_os_string()
- .into_string()
- .unwrap();
- for permission in &PERMISSION_VARIANTS {
- let status = util::deno_cmd()
- .current_dir(&util::testdata_path())
- .arg("run")
- .arg(format!("--allow-{0}={1},{2}", permission, test_dir, js_dir))
- .arg("run/complex_permissions_test.ts")
- .arg(permission)
- .arg("run/complex_permissions_test.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
- }
-
- #[test]
- fn rw_relative() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- for permission in &PERMISSION_VARIANTS {
- let status = util::deno_cmd()
- .current_dir(&util::testdata_path())
- .arg("run")
- .arg(format!("--allow-{0}=.", permission))
- .arg("run/complex_permissions_test.ts")
- .arg(permission)
- .arg("run/complex_permissions_test.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
- }
-
- #[test]
- fn rw_no_prefix() {
- const PERMISSION_VARIANTS: [&str; 2] = ["read", "write"];
- for permission in &PERMISSION_VARIANTS {
- let status = util::deno_cmd()
- .current_dir(&util::testdata_path())
- .arg("run")
- .arg(format!("--allow-{0}=tls/../", permission))
- .arg("run/complex_permissions_test.ts")
- .arg(permission)
- .arg("run/complex_permissions_test.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
- }
-
- #[test]
- fn net_fetch_allow_localhost_4545() {
- let (_, err) = util::run_and_collect_output(
- true,
- "run --allow-net=localhost:4545 run/complex_permissions_test.ts netFetch http://localhost:4545/",
- None,
- None,
- true,
- );
- assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_fetch_allow_deno_land() {
- let (_, err) = util::run_and_collect_output(
- false,
- "run --allow-net=deno.land run/complex_permissions_test.ts netFetch http://localhost:4545/",
- None,
- None,
- true,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_fetch_localhost_4545_fail() {
- let (_, err) = util::run_and_collect_output(
- false,
- "run --allow-net=localhost:4545 run/complex_permissions_test.ts netFetch http://localhost:4546/",
- None,
- None,
- true,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_fetch_localhost() {
- let (_, err) = util::run_and_collect_output(
- true,
- "run --allow-net=localhost run/complex_permissions_test.ts netFetch http://localhost:4545/ http://localhost:4546/ http://localhost:4547/",
- None,
- None,
- true,
- );
- assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_connect_allow_localhost_ip_4555() {
- let (_, err) = util::run_and_collect_output(
- true,
- "run --allow-net=127.0.0.1:4545 run/complex_permissions_test.ts netConnect 127.0.0.1:4545",
- None,
- None,
- true,
- );
- assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_connect_allow_deno_land() {
- let (_, err) = util::run_and_collect_output(
- false,
- "run --allow-net=deno.land run/complex_permissions_test.ts netConnect 127.0.0.1:4546",
- None,
- None,
- true,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_connect_allow_localhost_ip_4545_fail() {
- let (_, err) = util::run_and_collect_output(
- false,
- "run --allow-net=127.0.0.1:4545 run/complex_permissions_test.ts netConnect 127.0.0.1:4546",
- None,
- None,
- true,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_connect_allow_localhost_ip() {
- let (_, err) = util::run_and_collect_output(
- true,
- "run --allow-net=127.0.0.1 run/complex_permissions_test.ts netConnect 127.0.0.1:4545 127.0.0.1:4546 127.0.0.1:4547",
- None,
- None,
- true,
- );
- assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_listen_allow_localhost_4555() {
- let (_, err) = util::run_and_collect_output(
- true,
- "run --allow-net=localhost:4558 run/complex_permissions_test.ts netListen localhost:4558",
- None,
- None,
- false,
- );
- assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_listen_allow_deno_land() {
- let (_, err) = util::run_and_collect_output(
- false,
- "run --allow-net=deno.land run/complex_permissions_test.ts netListen localhost:4545",
- None,
- None,
- false,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_listen_allow_localhost_4555_fail() {
- let (_, err) = util::run_and_collect_output(
- false,
- "run --allow-net=localhost:4555 run/complex_permissions_test.ts netListen localhost:4556",
- None,
- None,
- false,
- );
- assert!(err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn net_listen_allow_localhost() {
- // Port 4600 is chosen to not colide with those used by
- // target/debug/test_server
- let (_, err) = util::run_and_collect_output(
- true,
- "run --allow-net=localhost run/complex_permissions_test.ts netListen localhost:4600",
- None,
- None,
- false,
- );
- assert!(!err.contains(util::PERMISSION_DENIED_PATTERN));
- }
-
- #[test]
- fn _061_permissions_request() {
- let args = "run --quiet run/061_permissions_request.ts";
- use util::PtyData::*;
- util::test_pty2(args, vec![
- Output("⚠️ ️Deno requests read access to \"foo\". Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)] "),
- Input("y\n"),
- Output("⚠️ ️Deno requests read access to \"bar\". Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
- Input("n\n"),
- Output("granted\r\n"),
- Output("prompt\r\n"),
- Output("denied\r\n"),
- ]);
- }
-
- #[test]
- fn _062_permissions_request_global() {
- let args = "run --quiet run/062_permissions_request_global.ts";
- use util::PtyData::*;
- util::test_pty2(args, vec![
- Output("⚠️ ️Deno requests read access. Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)] "),
- Input("y\n"),
- Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
- Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
- Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
- ]);
- }
-
- itest!(_063_permissions_revoke {
- args: "run --allow-read=foo,bar run/063_permissions_revoke.ts",
- output: "run/063_permissions_revoke.ts.out",
- });
-
- itest!(_064_permissions_revoke_global {
- args: "run --allow-read=foo,bar run/064_permissions_revoke_global.ts",
- output: "run/064_permissions_revoke_global.ts.out",
- });
-
- #[test]
- fn _066_prompt() {
- let args = "run --quiet --unstable run/066_prompt.ts";
- use util::PtyData::*;
- util::test_pty2(
- args,
- vec![
- Output("What is your name? [Jane Doe] "),
- Input("John Doe\n"),
- Output("Your name is John Doe.\r\n"),
- Output("What is your name? [Jane Doe] "),
- Input("\n"),
- Output("Your name is Jane Doe.\r\n"),
- Output("Prompt "),
- Input("foo\n"),
- Output("Your input is foo.\r\n"),
- Output("Question 0 [y/N] "),
- Input("Y\n"),
- Output("Your answer is true\r\n"),
- Output("Question 1 [y/N] "),
- Input("N\n"),
- Output("Your answer is false\r\n"),
- Output("Question 2 [y/N] "),
- Input("yes\n"),
- Output("Your answer is false\r\n"),
- Output("Confirm [y/N] "),
- Input("\n"),
- Output("Your answer is false\r\n"),
- Output("What is Windows EOL? "),
- Input("windows\n"),
- Output("Your answer is \"windows\"\r\n"),
- Output("Hi [Enter] "),
- Input("\n"),
- Output("Alert [Enter] "),
- Input("\n"),
- Output("The end of test\r\n"),
- Output("What is EOF? "),
- Input("\n"),
- Output("Your answer is null\r\n"),
- ],
- );
- }
-
- itest!(dynamic_import_permissions_remote_remote {
- args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_remote_remote.ts",
- output: "dynamic_import/permissions_remote_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(dynamic_import_permissions_data_remote {
- args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_data_remote.ts",
- output: "dynamic_import/permissions_data_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(dynamic_import_permissions_blob_remote {
- args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_blob_remote.ts",
- output: "dynamic_import/permissions_blob_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(dynamic_import_permissions_data_local {
- args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_data_local.ts",
- output: "dynamic_import/permissions_data_local.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(dynamic_import_permissions_blob_local {
- args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_blob_local.ts",
- output: "dynamic_import/permissions_blob_local.ts.out",
- http_server: true,
- exit_code: 1,
- });
- }
-
- itest!(tls_starttls {
- args: "run --quiet --reload --allow-net --allow-read --unstable --cert tls/RootCA.pem run/tls_starttls.js",
- output: "run/tls.out",
-});
-
- itest!(tls_connecttls {
- args: "run --quiet --reload --allow-net --allow-read --cert tls/RootCA.pem run/tls_connecttls.js",
- output: "run/tls.out",
-});
-
- itest!(byte_order_mark {
- args: "run --no-check run/byte_order_mark.ts",
- output: "run/byte_order_mark.out",
- });
-
- #[test]
- fn issue9750() {
- use util::PtyData::*;
- util::test_pty2(
- "run --prompt run/issue9750.js",
- vec![
- Output("Enter 'yy':\r\n"),
- Input("yy\n"),
- Output("⚠️ ️Deno requests env access. Run again with --allow-env to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
- Input("n\n"),
- Output("⚠️ ️Deno requests env access to \"SECRET\". Run again with --allow-env to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
- Input("n\n"),
- Output("error: Uncaught (in promise) PermissionDenied: Requires env access to \"SECRET\", run again with the --allow-env flag\r\n"),
- ],
- );
- }
-
- // Regression test for https://github.com/denoland/deno/issues/11451.
- itest!(dom_exception_formatting {
- args: "run run/dom_exception_formatting.ts",
- output: "run/dom_exception_formatting.ts.out",
- exit_code: 1,
- });
-
- itest!(long_data_url_formatting {
- args: "run run/long_data_url_formatting.ts",
- output: "run/long_data_url_formatting.ts.out",
- exit_code: 1,
- });
-
- itest!(eval_context_throw_dom_exception {
- args: "run run/eval_context_throw_dom_exception.js",
- output: "run/eval_context_throw_dom_exception.js.out",
- });
-
- #[test]
- #[cfg(unix)]
- fn navigator_language_unix() {
- let (res, _) = util::run_and_collect_output(
- true,
- "run navigator_language.ts",
- None,
- Some(vec![("LC_ALL".to_owned(), "pl_PL".to_owned())]),
- false,
- );
- assert_eq!(res, "pl-PL\n")
- }
-
- #[test]
- fn navigator_language() {
- let (res, _) = util::run_and_collect_output(
- true,
- "run navigator_language.ts",
- None,
- None,
- false,
- );
- assert!(!res.is_empty())
- }
-
- #[test]
- #[cfg(unix)]
- fn navigator_languages_unix() {
- let (res, _) = util::run_and_collect_output(
- true,
- "run navigator_languages.ts",
- None,
- Some(vec![
- ("LC_ALL".to_owned(), "pl_PL".to_owned()),
- ("NO_COLOR".to_owned(), "1".to_owned()),
- ]),
- false,
- );
- assert_eq!(res, "[ \"pl-PL\" ]\n")
- }
-
- #[test]
- fn navigator_languages() {
- let (res, _) = util::run_and_collect_output(
- true,
- "run navigator_languages.ts",
- None,
- None,
- false,
- );
- assert!(!res.is_empty())
- }
-
- /// Regression test for https://github.com/denoland/deno/issues/12740.
- #[test]
- fn issue12740() {
- let mod_dir = TempDir::new();
- let mod1_path = mod_dir.path().join("mod1.ts");
- let mod2_path = mod_dir.path().join("mod2.ts");
- let mut deno_cmd = util::deno_cmd();
- std::fs::write(&mod1_path, "").unwrap();
- let status = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&mod1_path)
- .stderr(Stdio::null())
- .stdout(Stdio::null())
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- std::fs::write(&mod1_path, "export { foo } from \"./mod2.ts\";").unwrap();
- std::fs::write(mod2_path, "(").unwrap();
- let status = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(&mod1_path)
- .stderr(Stdio::null())
- .stdout(Stdio::null())
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(!status.success());
- }
-
- /// Regression test for https://github.com/denoland/deno/issues/12807.
- #[test]
- fn issue12807() {
- let mod_dir = TempDir::new();
- let mod1_path = mod_dir.path().join("mod1.ts");
- let mod2_path = mod_dir.path().join("mod2.ts");
- let mut deno_cmd = util::deno_cmd();
- // With a fresh `DENO_DIR`, run a module with a dependency and a type error.
- std::fs::write(&mod1_path, "import './mod2.ts'; Deno.exit('0');").unwrap();
- std::fs::write(mod2_path, "console.log('Hello, world!');").unwrap();
- let status = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg(&mod1_path)
- .stderr(Stdio::null())
- .stdout(Stdio::null())
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(!status.success());
- // Fix the type error and run again.
- std::fs::write(&mod1_path, "import './mod2.ts'; Deno.exit(0);").unwrap();
- let status = deno_cmd
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg(&mod1_path)
- .stderr(Stdio::null())
- .stdout(Stdio::null())
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- itest!(issue_13562 {
- args: "run run/issue13562.ts",
- output: "run/issue13562.ts.out",
- });
-
- itest!(import_assertions_static_import {
- args: "run --allow-read import_assertions/static_import.ts",
- output: "import_assertions/static_import.out",
- });
-
- itest!(import_assertions_static_export {
- args: "run --allow-read import_assertions/static_export.ts",
- output: "import_assertions/static_export.out",
- });
-
- itest!(import_assertions_static_error {
- args: "run --allow-read import_assertions/static_error.ts",
- output: "import_assertions/static_error.out",
- exit_code: 1,
- });
-
- itest!(import_assertions_dynamic_import {
- args: "run --allow-read import_assertions/dynamic_import.ts",
- output: "import_assertions/dynamic_import.out",
- });
-
- itest!(import_assertions_dynamic_error {
- args: "run --allow-read import_assertions/dynamic_error.ts",
- output: "import_assertions/dynamic_error.out",
- exit_code: 1,
- });
-
- itest!(import_assertions_type_check {
- args: "run --allow-read --check import_assertions/type_check.ts",
- output: "import_assertions/type_check.out",
- exit_code: 1,
- });
-
- itest!(delete_window {
- args: "run run/delete_window.js",
- output_str: Some("true\n"),
- });
-
- itest!(colors_without_global_this {
- args: "run run/colors_without_globalThis.js",
- output_str: Some("true\n"),
- });
-
- itest!(config_auto_discovered_for_local_script {
- args: "run --quiet run/with_config/frontend_work.ts",
- output_str: Some("ok\n"),
- });
-
- itest!(no_config_auto_discovery_for_local_script {
- args: "run --quiet --no-config --check run/with_config/frontend_work.ts",
- output: "run/with_config/no_auto_discovery.out",
- exit_code: 1,
- });
-
- itest!(config_not_auto_discovered_for_remote_script {
- args:
- "run --quiet http://127.0.0.1:4545/run/with_config/server_side_work.ts",
- output_str: Some("ok\n"),
- http_server: true,
- });
-
- itest!(wasm_streaming_panic_test {
- args: "run run/wasm_streaming_panic_test.js",
- output: "run/wasm_streaming_panic_test.js.out",
- exit_code: 1,
- });
-
- // Regression test for https://github.com/denoland/deno/issues/13897.
- itest!(fetch_async_error_stack {
- args: "run --quiet -A run/fetch_async_error_stack.ts",
- output: "run/fetch_async_error_stack.ts.out",
- exit_code: 1,
- });
-
- itest!(unstable_ffi_1 {
- args: "run run/ffi/unstable_ffi_1.js",
- output: "run/ffi/unstable_ffi_1.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_2 {
- args: "run run/ffi/unstable_ffi_2.js",
- output: "run/ffi/unstable_ffi_2.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_3 {
- args: "run run/ffi/unstable_ffi_3.js",
- output: "run/ffi/unstable_ffi_3.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_4 {
- args: "run run/ffi/unstable_ffi_4.js",
- output: "run/ffi/unstable_ffi_4.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_5 {
- args: "run run/ffi/unstable_ffi_5.js",
- output: "run/ffi/unstable_ffi_5.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_6 {
- args: "run run/ffi/unstable_ffi_6.js",
- output: "run/ffi/unstable_ffi_6.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_7 {
- args: "run run/ffi/unstable_ffi_7.js",
- output: "run/ffi/unstable_ffi_7.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_8 {
- args: "run run/ffi/unstable_ffi_8.js",
- output: "run/ffi/unstable_ffi_8.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_9 {
- args: "run run/ffi/unstable_ffi_9.js",
- output: "run/ffi/unstable_ffi_9.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_10 {
- args: "run run/ffi/unstable_ffi_10.js",
- output: "run/ffi/unstable_ffi_10.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_11 {
- args: "run run/ffi/unstable_ffi_11.js",
- output: "run/ffi/unstable_ffi_11.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_12 {
- args: "run run/ffi/unstable_ffi_12.js",
- output: "run/ffi/unstable_ffi_12.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_13 {
- args: "run run/ffi/unstable_ffi_13.js",
- output: "run/ffi/unstable_ffi_13.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_14 {
- args: "run run/ffi/unstable_ffi_14.js",
- output: "run/ffi/unstable_ffi_14.js.out",
- exit_code: 70,
- });
-
- itest!(unstable_ffi_15 {
- args: "run run/ffi/unstable_ffi_15.js",
- output: "run/ffi/unstable_ffi_15.js.out",
- exit_code: 70,
- });
-
- itest!(future_check2 {
- args: "run --check run/future_check.ts",
- output: "run/future_check2.out",
- });
-
- itest!(event_listener_error {
- args: "run --quiet run/event_listener_error.ts",
- output: "run/event_listener_error.ts.out",
- exit_code: 1,
- });
-
- itest!(event_listener_error_handled {
- args: "run --quiet run/event_listener_error_handled.ts",
- output: "run/event_listener_error_handled.ts.out",
- });
-
- // https://github.com/denoland/deno/pull/14159#issuecomment-1092285446
- itest!(event_listener_error_immediate_exit {
- args: "run --quiet run/event_listener_error_immediate_exit.ts",
- output: "run/event_listener_error_immediate_exit.ts.out",
- exit_code: 1,
- });
-
- // https://github.com/denoland/deno/pull/14159#issuecomment-1092285446
- itest!(event_listener_error_immediate_exit_worker {
- args:
- "run --quiet --unstable -A run/event_listener_error_immediate_exit_worker.ts",
- output: "run/event_listener_error_immediate_exit_worker.ts.out",
- exit_code: 1,
-});
-
- itest!(set_timeout_error {
- args: "run --quiet run/set_timeout_error.ts",
- output: "run/set_timeout_error.ts.out",
- exit_code: 1,
- });
-
- itest!(set_timeout_error_handled {
- args: "run --quiet run/set_timeout_error_handled.ts",
- output: "run/set_timeout_error_handled.ts.out",
- });
-
- itest!(aggregate_error {
- args: "run --quiet run/aggregate_error.ts",
- output: "run/aggregate_error.out",
- exit_code: 1,
- });
-
- itest!(complex_error {
- args: "run --quiet run/complex_error.ts",
- output: "run/complex_error.ts.out",
- exit_code: 1,
- });
-
- // Regression test for https://github.com/denoland/deno/issues/16340.
- itest!(error_with_errors_prop {
- args: "run --quiet run/error_with_errors_prop.js",
- output: "run/error_with_errors_prop.js.out",
- exit_code: 1,
- });
-
- // Regression test for https://github.com/denoland/deno/issues/12143.
- itest!(js_root_with_ts_check {
- args: "run --quiet --check run/js_root_with_ts_check.js",
- output: "run/js_root_with_ts_check.js.out",
- exit_code: 1,
- });
-
- #[test]
- fn check_local_then_remote() {
- let _http_guard = util::http_server();
- let deno_dir = util::new_deno_dir();
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check")
- .arg("run/remote_type_error/main.ts")
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--check=all")
- .arg("run/remote_type_error/main.ts")
- .env("NO_COLOR", "1")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- let stderr = std::str::from_utf8(&output.stderr).unwrap();
- assert_contains!(
- stderr,
- "Type 'string' is not assignable to type 'number'."
- );
- }
-
- // Regression test for https://github.com/denoland/deno/issues/15163
- itest!(check_js_points_to_ts {
- args: "run --quiet --check --config run/checkjs.tsconfig.json run/check_js_points_to_ts/test.js",
- output: "run/check_js_points_to_ts/test.js.out",
- exit_code: 1,
-});
-
- itest!(no_prompt_flag {
- args: "run --quiet --unstable --no-prompt run/no_prompt.ts",
- output_str: Some(""),
- });
-
- #[test]
- fn deno_no_prompt_environment_variable() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--unstable")
- .arg("run/no_prompt.ts")
- .env("DENO_NO_PROMPT", "1")
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
-
- itest!(report_error {
- args: "run --quiet run/report_error.ts",
- output: "run/report_error.ts.out",
- exit_code: 1,
- });
-
- itest!(report_error_handled {
- args: "run --quiet run/report_error_handled.ts",
- output: "run/report_error_handled.ts.out",
- });
-
- // Regression test for https://github.com/denoland/deno/issues/15513.
- itest!(report_error_end_of_program {
- args: "run --quiet run/report_error_end_of_program.ts",
- output: "run/report_error_end_of_program.ts.out",
- exit_code: 1,
- });
-
- itest!(queue_microtask_error {
- args: "run --quiet run/queue_microtask_error.ts",
- output: "run/queue_microtask_error.ts.out",
- exit_code: 1,
- });
-
- itest!(queue_microtask_error_handled {
- args: "run --quiet run/queue_microtask_error_handled.ts",
- output: "run/queue_microtask_error_handled.ts.out",
- });
-
- itest!(spawn_stdout_inherit {
- args: "run --quiet --unstable -A run/spawn_stdout_inherit.ts",
- output: "run/spawn_stdout_inherit.ts.out",
- });
-
- itest!(error_name_non_string {
- args: "run --quiet run/error_name_non_string.js",
- output: "run/error_name_non_string.js.out",
- exit_code: 1,
- });
-
- itest!(custom_inspect_url {
- args: "run run/custom_inspect_url.js",
- output: "run/custom_inspect_url.js.out",
- });
-
- itest!(config_json_import {
- args: "run --quiet -c jsx/deno-jsx.json run/config_json_import.ts",
- output: "run/config_json_import.ts.out",
- http_server: true,
- });
-
- #[test]
- fn running_declaration_files() {
- let temp_dir = TempDir::new();
- let files = vec!["file.d.ts", "file.d.cts", "file.d.mts"];
-
- for file in files {
- temp_dir.write(file, "");
- let mut deno_cmd = util::deno_cmd_with_deno_dir(&temp_dir);
- let output = deno_cmd
- .current_dir(temp_dir.path())
- .args(["run", file])
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(output.status.success());
- }
- }
-
- itest!(test_and_bench_are_noops_in_run {
- args: "run run/test_and_bench_in_run.js",
- output_str: Some(""),
- });
-
- itest!(followup_dyn_import_resolved {
- args:
- "run --unstable --allow-read run/followup_dyn_import_resolves/main.ts",
- output: "run/followup_dyn_import_resolves/main.ts.out",
- });
-
- itest!(unhandled_rejection {
- args: "run --check run/unhandled_rejection.ts",
- output: "run/unhandled_rejection.ts.out",
- });
-
- itest!(unhandled_rejection_sync_error {
- args: "run --check run/unhandled_rejection_sync_error.ts",
- output: "run/unhandled_rejection_sync_error.ts.out",
- });
-
- // Regression test for https://github.com/denoland/deno/issues/15661
- itest!(unhandled_rejection_dynamic_import {
- args: "run --allow-read run/unhandled_rejection_dynamic_import/main.ts",
- output: "run/unhandled_rejection_dynamic_import/main.ts.out",
- exit_code: 1,
- });
-
- // Regression test for https://github.com/denoland/deno/issues/16909
- itest!(unhandled_rejection_dynamic_import2 {
- args: "run --allow-read run/unhandled_rejection_dynamic_import2/main.ts",
- output: "run/unhandled_rejection_dynamic_import2/main.ts.out",
- });
-
- itest!(nested_error {
- args: "run run/nested_error.ts",
- output: "run/nested_error.ts.out",
- exit_code: 1,
- });
-
- itest!(node_env_var_allowlist {
- args: "run --unstable --no-prompt run/node_env_var_allowlist.ts",
- output: "run/node_env_var_allowlist.ts.out",
- exit_code: 1,
- });
-
- #[test]
- fn cache_test() {
- let _g = util::http_server();
- let deno_dir = TempDir::new();
- let module_url =
- url::Url::parse("http://localhost:4545/run/006_url_imports.ts").unwrap();
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("cache")
- .arg("--check=all")
- .arg("-L")
- .arg("debug")
- .arg(module_url.to_string())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
-
- let prg = util::deno_exe_path();
- let output = Command::new(prg)
- .env("DENO_DIR", deno_dir.path())
- .env("HTTP_PROXY", "http://nil")
- .env("NO_COLOR", "1")
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(module_url.to_string())
- .output()
- .expect("Failed to spawn script");
-
- let str_output = std::str::from_utf8(&output.stdout).unwrap();
-
- let module_output_path =
- util::testdata_path().join("run/006_url_imports.ts.out");
- let mut module_output = String::new();
- let mut module_output_file =
- std::fs::File::open(module_output_path).unwrap();
- module_output_file
- .read_to_string(&mut module_output)
- .unwrap();
-
- assert_eq!(module_output, str_output);
- }
-
- #[test]
- fn cache_invalidation_test() {
- let deno_dir = TempDir::new();
- let fixture_path = deno_dir.path().join("fixture.ts");
- {
- let mut file = std::fs::File::create(fixture_path.clone())
- .expect("could not create fixture");
- file
- .write_all(b"console.log(\"42\");")
- .expect("could not write fixture");
- }
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(fixture_path.to_str().unwrap())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
- let actual = std::str::from_utf8(&output.stdout).unwrap();
- assert_eq!(actual, "42\n");
- {
- let mut file = std::fs::File::create(fixture_path.clone())
- .expect("could not create fixture");
- file
- .write_all(b"console.log(\"43\");")
- .expect("could not write fixture");
- }
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(fixture_path.to_str().unwrap())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
- let actual = std::str::from_utf8(&output.stdout).unwrap();
- assert_eq!(actual, "43\n");
- }
-
- #[test]
- fn cache_invalidation_test_no_check() {
- let deno_dir = TempDir::new();
- let fixture_path = deno_dir.path().join("fixture.ts");
- {
- let mut file = std::fs::File::create(fixture_path.clone())
- .expect("could not create fixture");
- file
- .write_all(b"console.log(\"42\");")
- .expect("could not write fixture");
- }
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--no-check")
- .arg(fixture_path.to_str().unwrap())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
- let actual = std::str::from_utf8(&output.stdout).unwrap();
- assert_eq!(actual, "42\n");
- {
- let mut file = std::fs::File::create(fixture_path.clone())
- .expect("could not create fixture");
- file
- .write_all(b"console.log(\"43\");")
- .expect("could not write fixture");
- }
- let output = Command::new(util::deno_exe_path())
- .env("DENO_DIR", deno_dir.path())
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--no-check")
- .arg(fixture_path.to_str().unwrap())
- .output()
- .expect("Failed to spawn script");
- assert!(output.status.success());
- let actual = std::str::from_utf8(&output.stdout).unwrap();
- assert_eq!(actual, "43\n");
- }
-
- #[test]
- fn ts_dependency_recompilation() {
- let t = TempDir::new();
- let ats = t.path().join("a.ts");
-
- std::fs::write(
- &ats,
- "
- import { foo } from \"./b.ts\";
-
- function print(str: string): void {
- console.log(str);
- }
-
- print(foo);",
- )
- .unwrap();
-
- let bts = t.path().join("b.ts");
- std::fs::write(
- &bts,
- "
- export const foo = \"foo\";",
- )
- .unwrap();
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--check")
- .arg(&ats)
- .output()
- .expect("failed to spawn script");
-
- let stdout_output = std::str::from_utf8(&output.stdout).unwrap().trim();
- let stderr_output = std::str::from_utf8(&output.stderr).unwrap().trim();
-
- assert!(stdout_output.ends_with("foo"));
- assert!(stderr_output.starts_with("Check"));
-
- // Overwrite contents of b.ts and run again
- std::fs::write(
- &bts,
- "
- export const foo = 5;",
- )
- .expect("error writing file");
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--check")
- .arg(&ats)
- .output()
- .expect("failed to spawn script");
-
- let stdout_output = std::str::from_utf8(&output.stdout).unwrap().trim();
- let stderr_output = std::str::from_utf8(&output.stderr).unwrap().trim();
-
- // error: TS2345 [ERROR]: Argument of type '5' is not assignable to parameter of type 'string'.
- assert!(stderr_output.contains("TS2345"));
- assert!(!output.status.success());
- assert!(stdout_output.is_empty());
- }
-
- #[test]
- fn basic_auth_tokens() {
- let _g = util::http_server();
-
- let output = util::deno_cmd()
- .current_dir(util::root_path())
- .arg("run")
- .arg("http://127.0.0.1:4554/run/001_hello.js")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
-
- assert!(!output.status.success());
-
- let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
- assert!(stdout_str.is_empty());
-
- let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim();
- eprintln!("{}", stderr_str);
-
- assert!(stderr_str.contains(
- "Module not found \"http://127.0.0.1:4554/run/001_hello.js\"."
- ));
-
- let output = util::deno_cmd()
- .current_dir(util::root_path())
- .arg("run")
- .arg("http://127.0.0.1:4554/run/001_hello.js")
- .env("DENO_AUTH_TOKENS", "testuser123:testpassabc@127.0.0.1:4554")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
-
- let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim();
- eprintln!("{}", stderr_str);
-
- assert!(output.status.success());
-
- let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
- assert_eq!(util::strip_ansi_codes(stdout_str), "Hello World");
- }
-
- #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
- async fn test_resolve_dns() {
- use std::net::SocketAddr;
- use std::str::FromStr;
- use std::sync::Arc;
- use std::time::Duration;
- use tokio::net::TcpListener;
- use tokio::net::UdpSocket;
- use tokio::sync::oneshot;
- use trust_dns_server::authority::Catalog;
- use trust_dns_server::authority::ZoneType;
- use trust_dns_server::proto::rr::Name;
- use trust_dns_server::store::in_memory::InMemoryAuthority;
- use trust_dns_server::ServerFuture;
-
- const DNS_PORT: u16 = 4553;
-
- // Setup DNS server for testing
- async fn run_dns_server(tx: oneshot::Sender<()>) {
- let zone_file = std::fs::read_to_string(
- util::testdata_path().join("run/resolve_dns.zone.in"),
- )
- .unwrap();
- let lexer = Lexer::new(&zone_file);
- let records = Parser::new().parse(
- lexer,
- Some(Name::from_str("example.com").unwrap()),
- None,
- );
- if records.is_err() {
- panic!("failed to parse: {:?}", records.err())
- }
- let (origin, records) = records.unwrap();
- let authority = Box::new(Arc::new(
- InMemoryAuthority::new(origin, records, ZoneType::Primary, false)
- .unwrap(),
- ));
- let mut catalog: Catalog = Catalog::new();
- catalog.upsert(Name::root().into(), authority);
-
- let mut server_fut = ServerFuture::new(catalog);
- let socket_addr = SocketAddr::from(([127, 0, 0, 1], DNS_PORT));
- let tcp_listener = TcpListener::bind(socket_addr).await.unwrap();
- let udp_socket = UdpSocket::bind(socket_addr).await.unwrap();
- server_fut.register_socket(udp_socket);
- server_fut.register_listener(tcp_listener, Duration::from_secs(2));
-
- // Notifies that the DNS server is ready
- tx.send(()).unwrap();
-
- server_fut.block_until_done().await.unwrap();
- }
-
- let (ready_tx, ready_rx) = oneshot::channel();
- let dns_server_fut = run_dns_server(ready_tx);
- let handle = tokio::spawn(dns_server_fut);
-
- // Waits for the DNS server to be ready
- ready_rx.await.unwrap();
-
- // Pass: `--allow-net`
- {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--check")
- .arg("--allow-net")
- .arg("run/resolve_dns.ts")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- let err = String::from_utf8_lossy(&output.stderr);
- let out = String::from_utf8_lossy(&output.stdout);
- println!("{}", err);
- assert!(output.status.success());
- assert!(err.starts_with("Check file"));
-
- let expected = std::fs::read_to_string(
- util::testdata_path().join("run/resolve_dns.ts.out"),
- )
- .unwrap();
- assert_eq!(expected, out);
- }
-
- // Pass: `--allow-net=127.0.0.1:4553`
- {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--check")
- .arg("--allow-net=127.0.0.1:4553")
- .arg("run/resolve_dns.ts")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- let err = String::from_utf8_lossy(&output.stderr);
- let out = String::from_utf8_lossy(&output.stdout);
- assert!(output.status.success());
- assert!(err.starts_with("Check file"));
-
- let expected = std::fs::read_to_string(
- util::testdata_path().join("run/resolve_dns.ts.out"),
- )
- .unwrap();
- assert_eq!(expected, out);
- }
-
- // Permission error: `--allow-net=deno.land`
- {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--check")
- .arg("--allow-net=deno.land")
- .arg("run/resolve_dns.ts")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- let err = String::from_utf8_lossy(&output.stderr);
- let out = String::from_utf8_lossy(&output.stdout);
- assert!(!output.status.success());
- assert!(err.starts_with("Check file"));
- assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
- assert!(out.is_empty());
- }
-
- // Permission error: no permission specified
- {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--check")
- .arg("run/resolve_dns.ts")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- let err = String::from_utf8_lossy(&output.stderr);
- let out = String::from_utf8_lossy(&output.stdout);
- assert!(!output.status.success());
- assert!(err.starts_with("Check file"));
- assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
- assert!(out.is_empty());
- }
-
- handle.abort();
- }
-
- #[tokio::test]
- async fn http2_request_url() {
- // TLS streams require the presence of an ambient local task set to gracefully
- // close dropped connections in the background.
- LocalSet::new()
- .run_until(async {
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--unstable")
- .arg("--quiet")
- .arg("--allow-net")
- .arg("--allow-read")
- .arg("./run/http2_request_url.ts")
- .arg("4506")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let stdout = child.stdout.as_mut().unwrap();
- let mut buffer = [0; 5];
- let read = stdout.read(&mut buffer).unwrap();
- assert_eq!(read, 5);
- let msg = std::str::from_utf8(&buffer).unwrap();
- assert_eq!(msg, "READY");
-
- let cert = reqwest::Certificate::from_pem(include_bytes!(
- "./testdata/tls/RootCA.crt"
- ))
- .unwrap();
-
- let client = reqwest::Client::builder()
- .add_root_certificate(cert)
- .http2_prior_knowledge()
- .build()
- .unwrap();
-
- let res = client.get("http://127.0.0.1:4506").send().await.unwrap();
- assert_eq!(200, res.status());
-
- let body = res.text().await.unwrap();
- assert_eq!(body, "http://127.0.0.1:4506/");
-
- child.kill().unwrap();
- child.wait().unwrap();
- })
- .await;
- }
-
- #[cfg(not(windows))]
- #[test]
- fn set_raw_should_not_panic_on_no_tty() {
- let output = util::deno_cmd()
- .arg("eval")
- .arg("Deno.stdin.setRaw(true)")
- // stdin set to piped so it certainly does not refer to TTY
- .stdin(std::process::Stdio::piped())
- // stderr is piped so we can capture output.
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- let stderr = std::str::from_utf8(&output.stderr).unwrap().trim();
- assert!(stderr.contains("BadResource"));
- }
-
- #[test]
- fn timeout_clear() {
- // https://github.com/denoland/deno/issues/7599
-
- use std::time::Duration;
- use std::time::Instant;
-
- let source_code = r#"
-const handle = setTimeout(() => {
- console.log("timeout finish");
-}, 10000);
-clearTimeout(handle);
-console.log("finish");
-"#;
-
- let mut p = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("-")
- .stdin(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let stdin = p.stdin.as_mut().unwrap();
- stdin.write_all(source_code.as_bytes()).unwrap();
- let start = Instant::now();
- let status = p.wait().unwrap();
- let end = Instant::now();
- assert!(status.success());
- // check that program did not run for 10 seconds
- // for timeout to clear
- assert!(end - start < Duration::new(10, 0));
- }
-
- #[test]
- fn broken_stdout() {
- let (reader, writer) = os_pipe::pipe().unwrap();
- // drop the reader to create a broken pipe
- drop(reader);
-
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("eval")
- .arg("console.log(3.14)")
- .stdout(writer)
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
-
- assert!(!output.status.success());
- let stderr = std::str::from_utf8(output.stderr.as_ref()).unwrap().trim();
- assert!(stderr.contains("Uncaught BrokenPipe"));
- assert!(!stderr.contains("panic"));
- }
-
- itest!(error_cause {
- args: "run run/error_cause.ts",
- output: "run/error_cause.ts.out",
- exit_code: 1,
- });
-
- itest!(error_cause_recursive_aggregate {
- args: "run error_cause_recursive_aggregate.ts",
- output: "error_cause_recursive_aggregate.ts.out",
- exit_code: 1,
- });
-
- itest!(error_cause_recursive_tail {
- args: "run error_cause_recursive_tail.ts",
- output: "error_cause_recursive_tail.ts.out",
- exit_code: 1,
- });
-
- itest!(error_cause_recursive {
- args: "run run/error_cause_recursive.ts",
- output: "run/error_cause_recursive.ts.out",
- exit_code: 1,
- });
-
- #[test]
- fn websocket() {
- let _g = util::http_server();
-
- let script = util::testdata_path().join("run/websocket_test.ts");
- let root_ca = util::testdata_path().join("tls/RootCA.pem");
- let status = util::deno_cmd()
- .arg("test")
- .arg("--unstable")
- .arg("--allow-net")
- .arg("--cert")
- .arg(root_ca)
- .arg(script)
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-
- assert!(status.success());
- }
-
- #[ignore]
- #[test]
- fn websocketstream() {
- let _g = util::http_server();
-
- let script = util::testdata_path().join("run/websocketstream_test.ts");
- let root_ca = util::testdata_path().join("tls/RootCA.pem");
- let status = util::deno_cmd()
- .arg("test")
- .arg("--unstable")
- .arg("--allow-net")
- .arg("--cert")
- .arg(root_ca)
- .arg(script)
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-
- assert!(status.success());
- }
-
- #[test]
- fn websocketstream_ping() {
- use deno_runtime::deno_websocket::tokio_tungstenite::tungstenite;
- let _g = util::http_server();
-
- let script = util::testdata_path().join("run/websocketstream_ping_test.ts");
- let root_ca = util::testdata_path().join("tls/RootCA.pem");
- let mut child = util::deno_cmd()
- .arg("test")
- .arg("--unstable")
- .arg("--allow-net")
- .arg("--cert")
- .arg(root_ca)
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let server = std::net::TcpListener::bind("127.0.0.1:4513").unwrap();
- let (stream, _) = server.accept().unwrap();
- let mut socket = tungstenite::accept(stream).unwrap();
- socket
- .write_message(tungstenite::Message::Text(String::from("A")))
- .unwrap();
- socket
- .write_message(tungstenite::Message::Ping(vec![]))
- .unwrap();
- socket
- .write_message(tungstenite::Message::Text(String::from("B")))
- .unwrap();
- let message = socket.read_message().unwrap();
- assert_eq!(message, tungstenite::Message::Pong(vec![]));
- socket
- .write_message(tungstenite::Message::Text(String::from("C")))
- .unwrap();
- socket.close(None).unwrap();
-
- assert!(child.wait().unwrap().success());
- }
-
- #[test]
- fn websocket_server_multi_field_connection_header() {
- let script = util::testdata_path()
- .join("run/websocket_server_multi_field_connection_header_test.ts");
- let root_ca = util::testdata_path().join("tls/RootCA.pem");
- let mut child = util::deno_cmd()
- .arg("run")
- .arg("--unstable")
- .arg("--allow-net")
- .arg("--cert")
- .arg(root_ca)
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stdout = child.stdout.as_mut().unwrap();
- let mut buffer = [0; 5];
- let read = stdout.read(&mut buffer).unwrap();
- assert_eq!(read, 5);
- let msg = std::str::from_utf8(&buffer).unwrap();
- assert_eq!(msg, "READY");
-
- let req = http::request::Builder::new()
- .header(http::header::CONNECTION, "keep-alive, Upgrade")
- .uri("ws://localhost:4319")
- .body(())
- .unwrap();
- let (mut socket, _) =
- deno_runtime::deno_websocket::tokio_tungstenite::tungstenite::connect(
- req,
- )
- .unwrap();
- let message = socket.read_message().unwrap();
- assert_eq!(message, deno_runtime::deno_websocket::tokio_tungstenite::tungstenite::Message::Close(None));
- socket.close(None).unwrap();
- assert!(child.wait().unwrap().success());
- }
-
- // TODO(bartlomieju): this should use `deno run`, not `deno test`; but the
- // test hangs then. https://github.com/denoland/deno/issues/14283
- #[test]
- #[ignore]
- fn websocket_server_idletimeout() {
- let script =
- util::testdata_path().join("run/websocket_server_idletimeout.ts");
- let root_ca = util::testdata_path().join("tls/RootCA.pem");
- let mut child = util::deno_cmd()
- .arg("test")
- .arg("--unstable")
- .arg("--allow-net")
- .arg("--cert")
- .arg(root_ca)
- .arg(script)
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let stdout = child.stdout.as_mut().unwrap();
- let mut buffer = [0; 5];
- let read = stdout.read(&mut buffer).unwrap();
- assert_eq!(read, 5);
- let msg = std::str::from_utf8(&buffer).unwrap();
- assert_eq!(msg, "READY");
-
- let req = http::request::Builder::new()
- .uri("ws://localhost:4509")
- .body(())
- .unwrap();
- let (_ws, _request) =
- deno_runtime::deno_websocket::tokio_tungstenite::tungstenite::connect(
- req,
- )
- .unwrap();
-
- assert!(child.wait().unwrap().success());
- }
-
- itest!(auto_discover_lockfile {
- args: "run run/auto_discover_lockfile/main.ts",
- output: "run/auto_discover_lockfile/main.out",
- http_server: true,
- exit_code: 10,
- });
-
- itest!(no_lock_flag {
- args: "run --no-lock run/no_lock_flag/main.ts",
- output: "run/no_lock_flag/main.out",
- http_server: true,
- exit_code: 0,
- });
-
- itest!(config_file_lock_false {
- args: "run --config=run/config_file_lock_boolean/false.json run/config_file_lock_boolean/main.ts",
- output: "run/config_file_lock_boolean/false.main.out",
- http_server: true,
- exit_code: 0,
-});
-
- itest!(config_file_lock_true {
- args: "run --config=run/config_file_lock_boolean/true.json run/config_file_lock_boolean/main.ts",
- output: "run/config_file_lock_boolean/true.main.out",
- http_server: true,
- exit_code: 10,
-});
-
- // Check https://github.com/denoland/deno_std/issues/2882
- itest!(flash_shutdown {
- args: "run --unstable --allow-net run/flash_shutdown/main.ts",
- exit_code: 0,
- });
-
- itest!(permission_args {
- args: "run run/001_hello.js --allow-net",
- output: "run/permission_args.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-
- itest!(permission_args_quiet {
- args: "run --quiet run/001_hello.js --allow-net",
- output: "run/001_hello.js.out",
- });
-
- // Regression test for https://github.com/denoland/deno/issues/16772
- #[test]
- fn file_fetcher_preserves_permissions() {
- let _guard = util::http_server();
- util::with_pty(&["repl"], |mut console| {
- console.write_text(
- "const a = import('http://127.0.0.1:4545/run/019_media_types.ts');",
- );
- console.write_text("y");
- console.write_line("");
- console.write_line("close();");
- let output = console.read_all_output();
- assert_contains!(output, "success");
- assert_contains!(output, "true");
- });
- }
-}
diff --git a/cli/tests/task_tests.rs b/cli/tests/task_tests.rs
deleted file mode 100644
index b403c4a5a..000000000
--- a/cli/tests/task_tests.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-// Most of the tests for this are in deno_task_shell.
-// These tests are intended to only test integration.
-
-mod task {
- use super::*;
-
- itest!(task_no_args {
- args: "task -q --config task/deno.json",
- output: "task/task_no_args.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 1,
- });
-
- itest!(task_cwd {
- args: "task -q --config task/deno.json --cwd .. echo_cwd",
- output: "task/task_cwd.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 0,
- });
-
- itest!(task_init_cwd {
- args: "task -q --config task/deno.json --cwd .. echo_init_cwd",
- output: "task/task_init_cwd.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 0,
- });
-
- itest!(task_init_cwd_already_set {
- args: "task -q --config task/deno.json echo_init_cwd",
- output: "task/task_init_cwd_already_set.out",
- envs: vec![
- ("NO_COLOR".to_string(), "1".to_string()),
- ("INIT_CWD".to_string(), "HELLO".to_string())
- ],
- exit_code: 0,
- });
-
- itest!(task_cwd_resolves_config_from_specified_dir {
- args: "task -q --cwd task",
- output: "task/task_no_args.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 1,
- });
-
- itest!(task_non_existent {
- args: "task --config task/deno.json non_existent",
- output: "task/task_non_existent.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 1,
- });
-
- #[test]
- fn task_emoji() {
- // this bug only appears when using a pty/tty
- let args = "task --config task/deno.json echo_emoji";
- use test_util::PtyData::*;
- test_util::test_pty2(args, vec![Output("Task echo_emoji echo 🔥\r\n🔥")]);
- }
-
- itest!(task_boolean_logic {
- args: "task -q --config task/deno.json boolean_logic",
- output: "task/task_boolean_logic.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-
- itest!(task_exit_code_5 {
- args: "task --config task/deno.json exit_code_5",
- output: "task/task_exit_code_5.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- exit_code: 5,
- });
-
- itest!(task_additional_args {
- args: "task -q --config task/deno.json echo 2",
- output: "task/task_additional_args.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-
- itest!(task_additional_args_no_shell_expansion {
- args_vec: vec![
- "task",
- "-q",
- "--config",
- "task/deno.json",
- "echo",
- "$(echo 5)"
- ],
- output: "task/task_additional_args_no_shell_expansion.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-
- itest!(task_additional_args_nested_strings {
- args_vec: vec![
- "task",
- "-q",
- "--config",
- "task/deno.json",
- "echo",
- "string \"quoted string\""
- ],
- output: "task/task_additional_args_nested_strings.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-
- itest!(task_additional_args_no_logic {
- args_vec: vec![
- "task",
- "-q",
- "--config",
- "task/deno.json",
- "echo",
- "||",
- "echo",
- "5"
- ],
- output: "task/task_additional_args_no_logic.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-
- itest!(task_deno_exe_no_env {
- args_vec: vec!["task", "-q", "--config", "task/deno.json", "deno_echo"],
- output: "task/task_deno_exe_no_env.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- env_clear: true,
- });
-
- itest!(task_piped_stdin {
- args_vec: vec!["task", "-q", "--config", "task/deno.json", "piped"],
- output: "task/task_piped_stdin.out",
- envs: vec![("NO_COLOR".to_string(), "1".to_string())],
- });
-}
diff --git a/cli/tests/test_tests.rs b/cli/tests/test_tests.rs
deleted file mode 100644
index 83b119241..000000000
--- a/cli/tests/test_tests.rs
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-use deno_core::url::Url;
-use test_util as util;
-
-mod test {
- use super::*;
-
- #[test]
- fn no_color() {
- let (out, _) = util::run_and_collect_output(
- false,
- "test test/no_color.ts",
- None,
- Some(vec![("NO_COLOR".to_owned(), "true".to_owned())]),
- false,
- );
- // ANSI escape codes should be stripped.
- assert!(out.contains("success ... ok"));
- assert!(out.contains("fail ... FAILED"));
- assert!(out.contains("ignored ... ignored"));
- assert!(out.contains("FAILED | 1 passed | 1 failed | 1 ignored"));
- }
-
- itest!(overloads {
- args: "test test/overloads.ts",
- exit_code: 0,
- output: "test/overloads.out",
- });
-
- itest!(meta {
- args: "test test/meta.ts",
- exit_code: 0,
- output: "test/meta.out",
- });
-
- itest!(pass {
- args: "test test/pass.ts",
- exit_code: 0,
- output: "test/pass.out",
- });
-
- itest!(ignore {
- args: "test test/ignore.ts",
- exit_code: 0,
- output: "test/ignore.out",
- });
-
- itest!(ignore_permissions {
- args: "test --unstable test/ignore_permissions.ts",
- exit_code: 0,
- output: "test/ignore_permissions.out",
- });
-
- itest!(fail {
- args: "test test/fail.ts",
- exit_code: 1,
- output: "test/fail.out",
- });
-
- itest!(collect {
- args: "test --ignore=test/collect/ignore test/collect",
- exit_code: 0,
- output: "test/collect.out",
- });
-
- itest!(test_with_config {
- args: "test --config test/collect/deno.jsonc test/collect",
- exit_code: 0,
- output: "test/collect.out",
- });
-
- itest!(test_with_config2 {
- args: "test --config test/collect/deno2.jsonc test/collect",
- exit_code: 0,
- output: "test/collect2.out",
- });
-
- itest!(test_with_malformed_config {
- args: "test --config test/collect/deno.malformed.jsonc",
- exit_code: 1,
- output: "test/collect_with_malformed_config.out",
- });
-
- itest!(parallel_flag {
- args: "test test/short-pass.ts --parallel",
- exit_code: 0,
- output: "test/short-pass.out",
- });
-
- itest!(parallel_flag_with_env_variable {
- args: "test test/short-pass.ts --parallel",
- envs: vec![("DENO_JOBS".to_owned(), "2".to_owned())],
- exit_code: 0,
- output: "test/short-pass.out",
- });
-
- itest!(jobs_flag {
- args: "test test/short-pass.ts --jobs",
- exit_code: 0,
- output: "test/short-pass-jobs-flag-warning.out",
- });
-
- itest!(jobs_flag_with_numeric_value {
- args: "test test/short-pass.ts --jobs=2",
- exit_code: 0,
- output: "test/short-pass-jobs-flag-warning.out",
- });
-
- itest!(load_unload {
- args: "test test/load_unload.ts",
- exit_code: 0,
- output: "test/load_unload.out",
- });
-
- itest!(interval {
- args: "test test/interval.ts",
- exit_code: 0,
- output: "test/interval.out",
- });
-
- itest!(doc {
- args: "test --doc --allow-all test/doc.ts",
- exit_code: 1,
- output: "test/doc.out",
- });
-
- itest!(doc_only {
- args: "test --doc --allow-all test/doc_only",
- exit_code: 0,
- output: "test/doc_only.out",
- });
-
- itest!(markdown {
- args: "test --doc --allow-all test/markdown.md",
- exit_code: 1,
- output: "test/markdown.out",
- });
-
- itest!(markdown_windows {
- args: "test --doc --allow-all test/markdown_windows.md",
- exit_code: 1,
- output: "test/markdown_windows.out",
- });
-
- itest!(markdown_full_block_names {
- args: "test --doc --allow-all test/markdown_full_block_names.md",
- exit_code: 1,
- output: "test/markdown_full_block_names.out",
- });
-
- itest!(markdown_ignore_html_comment {
- args: "test --doc --allow-all test/markdown_with_comment.md",
- exit_code: 1,
- output: "test/markdown_with_comment.out",
- });
-
- itest!(text {
- args: "test --doc --allow-all test/text.md",
- exit_code: 0,
- output: "test/text.out",
- });
-
- itest!(quiet {
- args: "test --quiet test/quiet.ts",
- exit_code: 0,
- output: "test/quiet.out",
- });
-
- itest!(fail_fast {
- args: "test --fail-fast test/fail_fast.ts",
- exit_code: 1,
- output: "test/fail_fast.out",
- });
-
- itest!(only {
- args: "test test/only.ts",
- exit_code: 1,
- output: "test/only.out",
- });
-
- itest!(no_check {
- args: "test --no-check test/no_check.ts",
- exit_code: 1,
- output: "test/no_check.out",
- });
-
- itest!(no_run {
- args: "test --unstable --no-run test/no_run.ts",
- output: "test/no_run.out",
- exit_code: 1,
- });
-
- itest!(allow_all {
- args: "test --unstable --allow-all test/allow_all.ts",
- exit_code: 0,
- output: "test/allow_all.out",
- });
-
- itest!(allow_none {
- args: "test --unstable test/allow_none.ts",
- exit_code: 1,
- output: "test/allow_none.out",
- });
-
- itest!(ops_sanitizer_unstable {
- args: "test --unstable --trace-ops test/ops_sanitizer_unstable.ts",
- exit_code: 1,
- output: "test/ops_sanitizer_unstable.out",
- });
-
- itest!(ops_sanitizer_timeout_failure {
- args: "test test/ops_sanitizer_timeout_failure.ts",
- output: "test/ops_sanitizer_timeout_failure.out",
- });
-
- itest!(ops_sanitizer_multiple_timeout_tests {
- args: "test --trace-ops test/ops_sanitizer_multiple_timeout_tests.ts",
- exit_code: 1,
- output: "test/ops_sanitizer_multiple_timeout_tests.out",
- });
-
- itest!(ops_sanitizer_multiple_timeout_tests_no_trace {
- args: "test test/ops_sanitizer_multiple_timeout_tests.ts",
- exit_code: 1,
- output: "test/ops_sanitizer_multiple_timeout_tests_no_trace.out",
- });
-
- itest!(trace_ops_catch_error {
- args: "test -A --trace-ops test/trace_ops_caught_error/main.ts",
- exit_code: 0,
- output: "test/trace_ops_caught_error/main.out",
- });
-
- // TODO(@littledivy): re-enable this test, recent optimizations made output non deterministic.
- // https://github.com/denoland/deno/issues/14268
- //
- // itest!(ops_sanitizer_missing_details {
- // args: "test --allow-write --allow-read test/ops_sanitizer_missing_details.ts",
- // exit_code: 1,
- // output: "test/ops_sanitizer_missing_details.out",
- // });
-
- itest!(ops_sanitizer_nexttick {
- args: "test test/ops_sanitizer_nexttick.ts",
- output: "test/ops_sanitizer_nexttick.out",
- });
-
- itest!(resource_sanitizer {
- args: "test --allow-read test/resource_sanitizer.ts",
- exit_code: 1,
- output: "test/resource_sanitizer.out",
- });
-
- itest!(exit_sanitizer {
- args: "test test/exit_sanitizer.ts",
- output: "test/exit_sanitizer.out",
- exit_code: 1,
- });
-
- itest!(clear_timeout {
- args: "test test/clear_timeout.ts",
- exit_code: 0,
- output: "test/clear_timeout.out",
- });
-
- itest!(finally_timeout {
- args: "test test/finally_timeout.ts",
- exit_code: 1,
- output: "test/finally_timeout.out",
- });
-
- itest!(unresolved_promise {
- args: "test test/unresolved_promise.ts",
- exit_code: 1,
- output: "test/unresolved_promise.out",
- });
-
- itest!(unhandled_rejection {
- args: "test test/unhandled_rejection.ts",
- exit_code: 1,
- output: "test/unhandled_rejection.out",
- });
-
- itest!(filter {
- args: "test --filter=foo test/filter",
- exit_code: 0,
- output: "test/filter.out",
- });
-
- itest!(shuffle {
- args: "test --shuffle test/shuffle",
- exit_code: 0,
- output_str: Some("[WILDCARD]"),
- });
-
- itest!(shuffle_with_seed {
- args: "test --shuffle=42 test/shuffle",
- exit_code: 0,
- output: "test/shuffle.out",
- });
-
- itest!(aggregate_error {
- args: "test --quiet test/aggregate_error.ts",
- exit_code: 1,
- output: "test/aggregate_error.out",
- });
-
- itest!(steps_passing_steps {
- args: "test test/steps/passing_steps.ts",
- exit_code: 0,
- output: "test/steps/passing_steps.out",
- });
-
- itest!(steps_failing_steps {
- args: "test test/steps/failing_steps.ts",
- exit_code: 1,
- output: "test/steps/failing_steps.out",
- });
-
- itest!(steps_ignored_steps {
- args: "test test/steps/ignored_steps.ts",
- exit_code: 0,
- output: "test/steps/ignored_steps.out",
- });
-
- itest!(steps_invalid_usage {
- args: "test test/steps/invalid_usage.ts",
- exit_code: 1,
- output: "test/steps/invalid_usage.out",
- });
-
- itest!(steps_output_within {
- args: "test test/steps/output_within.ts",
- exit_code: 0,
- output: "test/steps/output_within.out",
- });
-
- itest!(no_prompt_by_default {
- args: "test --quiet test/no_prompt_by_default.ts",
- exit_code: 1,
- output: "test/no_prompt_by_default.out",
- });
-
- itest!(no_prompt_with_denied_perms {
- args: "test --quiet --allow-read test/no_prompt_with_denied_perms.ts",
- exit_code: 1,
- output: "test/no_prompt_with_denied_perms.out",
- });
-
- itest!(test_with_custom_jsx {
- args: "test --quiet --allow-read test/hello_world.ts --config=test/deno_custom_jsx.json",
- exit_code: 0,
- output: "test/hello_world.out",
-});
-
- #[test]
- fn captured_output() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("--allow-run")
- .arg("--allow-read")
- .arg("--unstable")
- .arg("test/captured_output.ts")
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
-
- let output_start = "------- output -------";
- let output_end = "----- output end -----";
- assert!(output.status.success());
- let output_text = String::from_utf8(output.stdout).unwrap();
- let start = output_text.find(output_start).unwrap() + output_start.len();
- let end = output_text.find(output_end).unwrap();
- // replace zero width space that may appear in test output due
- // to test runner output flusher
- let output_text = output_text[start..end]
- .replace('\u{200B}', "")
- .trim()
- .to_string();
- let mut lines = output_text.lines().collect::<Vec<_>>();
- // the output is racy on either stdout or stderr being flushed
- // from the runtime into the rust code, so sort it... the main
- // thing here to ensure is that we're capturing the output in
- // this block on stdout
- lines.sort_unstable();
- assert_eq!(lines.join(" "), "0 1 2 3 4 5 6 7 8 9");
- }
-
- #[test]
- fn recursive_permissions_pledge() {
- let output = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("test/recursive_permissions_pledge.js")
- .stderr(std::process::Stdio::piped())
- .stdout(std::process::Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert!(String::from_utf8(output.stderr).unwrap().contains(
- "pledge test permissions called before restoring previous pledge"
- ));
- }
-
- #[test]
- fn file_protocol() {
- let file_url =
- Url::from_file_path(util::testdata_path().join("test/file_protocol.ts"))
- .unwrap()
- .to_string();
-
- (util::CheckOutputIntegrationTest {
- args_vec: vec!["test", &file_url],
- exit_code: 0,
- output: "test/file_protocol.out",
- ..Default::default()
- })
- .run();
- }
-
- itest!(uncaught_errors {
- args: "test --quiet test/uncaught_errors_1.ts test/uncaught_errors_2.ts test/uncaught_errors_3.ts",
- output: "test/uncaught_errors.out",
- exit_code: 1,
-});
-
- itest!(check_local_by_default {
- args: "test --quiet test/check_local_by_default.ts",
- output: "test/check_local_by_default.out",
- http_server: true,
- });
-
- itest!(check_local_by_default2 {
- args: "test --quiet test/check_local_by_default2.ts",
- output: "test/check_local_by_default2.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(non_error_thrown {
- args: "test --quiet test/non_error_thrown.ts",
- output: "test/non_error_thrown.out",
- exit_code: 1,
- });
-
- itest!(parallel_output {
- args: "test --parallel --reload test/parallel_output.ts",
- output: "test/parallel_output.out",
- exit_code: 1,
- });
-}
diff --git a/cli/tests/upgrade_tests.rs b/cli/tests/upgrade_tests.rs
deleted file mode 100644
index 76a072126..000000000
--- a/cli/tests/upgrade_tests.rs
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::process::{Command, Stdio};
-use test_util as util;
-use test_util::TempDir;
-
-mod upgrade {
- use super::*;
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_in_tmpdir() {
- let temp_dir = TempDir::new();
- let exe_path = temp_dir.path().join("deno");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- let status = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--force")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let _mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- // TODO(ry) assert!(mtime1 < mtime2);
- }
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_with_space_in_path() {
- let temp_dir = TempDir::new_with_prefix("directory with spaces");
- let exe_path = temp_dir.path().join("deno");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let status = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--force")
- .env("TMP", temp_dir.path())
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_with_version_in_tmpdir() {
- let temp_dir = TempDir::new();
- let exe_path = temp_dir.path().join("deno");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- let status = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--force")
- .arg("--version")
- .arg("1.11.5")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let upgraded_deno_version = String::from_utf8(
- Command::new(&exe_path).arg("-V").output().unwrap().stdout,
- )
- .unwrap();
- assert!(upgraded_deno_version.contains("1.11.5"));
- let _mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- // TODO(ry) assert!(mtime1 < mtime2);
- }
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_with_canary_in_tmpdir() {
- let temp_dir = TempDir::new();
- let exe_path = temp_dir.path().join("deno");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- let status = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--canary")
- .arg("--version")
- .arg("e6685f0f01b8a11a5eaff020f5babcfde76b3038")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let upgraded_deno_version = String::from_utf8(
- Command::new(&exe_path).arg("-V").output().unwrap().stdout,
- )
- .unwrap();
- assert!(upgraded_deno_version.contains("e6685f0"));
- let _mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- // TODO(ry) assert!(mtime1 < mtime2);
- }
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_with_out_in_tmpdir() {
- let temp_dir = TempDir::new();
- let exe_path = temp_dir.path().join("deno");
- let new_exe_path = temp_dir.path().join("foo");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- let status = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--version")
- .arg("1.11.5")
- .arg("--output")
- .arg(new_exe_path.to_str().unwrap())
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- assert!(new_exe_path.exists());
- let mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap();
- assert_eq!(mtime1, mtime2); // Original exe_path was not changed.
-
- let v = String::from_utf8(
- Command::new(&new_exe_path)
- .arg("-V")
- .output()
- .unwrap()
- .stdout,
- )
- .unwrap();
- assert!(v.contains("1.11.5"));
- }
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_invalid_stable_version() {
- let temp_dir = TempDir::new();
- let exe_path = temp_dir.path().join("deno");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let output = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--version")
- .arg("foobar")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(
- "error: Invalid semver passed\n",
- util::strip_ansi_codes(&String::from_utf8(output.stderr).unwrap())
- );
- }
-
- // Warning: this test requires internet access.
- // TODO(#7412): reenable. test is flaky
- #[test]
- #[ignore]
- fn upgrade_invalid_canary_version() {
- let temp_dir = TempDir::new();
- let exe_path = temp_dir.path().join("deno");
- let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap();
- assert!(exe_path.exists());
- let output = Command::new(&exe_path)
- .arg("upgrade")
- .arg("--canary")
- .arg("--version")
- .arg("foobar")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap()
- .wait_with_output()
- .unwrap();
- assert!(!output.status.success());
- assert_eq!(
- "error: Invalid commit hash passed\n",
- util::strip_ansi_codes(&String::from_utf8(output.stderr).unwrap())
- );
- }
-}
diff --git a/cli/tests/vendor_tests.rs b/cli/tests/vendor_tests.rs
deleted file mode 100644
index 55b8913db..000000000
--- a/cli/tests/vendor_tests.rs
+++ /dev/null
@@ -1,583 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod vendor {
- use deno_core::serde_json;
- use deno_core::serde_json::json;
- use pretty_assertions::assert_eq;
- use std::fmt::Write as _;
- use std::path::PathBuf;
- use std::process::Stdio;
- use test_util as util;
- use test_util::TempDir;
- use util::http_server;
- use util::new_deno_dir;
-
- #[test]
- fn output_dir_exists() {
- let t = TempDir::new();
- t.write("mod.ts", "");
- t.create_dir_all("vendor");
- t.write("vendor/mod.ts", "");
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("mod.ts")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- concat!(
- "error: Output directory was not empty. Please specify an empty ",
- "directory or use --force to ignore this error and potentially ",
- "overwrite its contents.",
- ),
- );
- assert!(!output.status.success());
-
- // ensure it errors when using the `--output` arg too
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("--output")
- .arg("vendor")
- .arg("mod.ts")
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- concat!(
- "error: Output directory was not empty. Please specify an empty ",
- "directory or use --force to ignore this error and potentially ",
- "overwrite its contents.",
- ),
- );
- assert!(!output.status.success());
-
- // now use `--force`
- let status = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("mod.ts")
- .arg("--force")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn standard_test() {
- let _server = http_server();
- let t = TempDir::new();
- let vendor_dir = t.path().join("vendor2");
- t.write(
- "my_app.ts",
- "import {Logger} from 'http://localhost:4545/vendor/query_reexport.ts?testing'; new Logger().log('outputted');",
- );
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .arg("vendor")
- .arg("my_app.ts")
- .arg("--output")
- .arg("vendor2")
- .env("NO_COLOR", "1")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- format!(
- concat!(
- "Download http://localhost:4545/vendor/query_reexport.ts?testing\n",
- "Download http://localhost:4545/vendor/logger.ts?test\n",
- "{}",
- ),
- success_text("2 modules", "vendor2", true),
- )
- );
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
- assert!(output.status.success());
-
- assert!(vendor_dir.exists());
- assert!(!t.path().join("vendor").exists());
- let import_map: serde_json::Value =
- serde_json::from_str(&t.read_to_string("vendor2/import_map.json"))
- .unwrap();
- assert_eq!(
- import_map,
- json!({
- "imports": {
- "http://localhost:4545/vendor/query_reexport.ts?testing": "./localhost_4545/vendor/query_reexport.ts",
- "http://localhost:4545/": "./localhost_4545/",
- },
- "scopes": {
- "./localhost_4545/": {
- "./localhost_4545/vendor/logger.ts?test": "./localhost_4545/vendor/logger.ts"
- }
- }
- }),
- );
-
- // try running the output with `--no-remote`
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--no-remote")
- .arg("--check")
- .arg("--quiet")
- .arg("--import-map")
- .arg("vendor2/import_map.json")
- .arg("my_app.ts")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8_lossy(&output.stderr).trim(), "");
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "outputted");
- assert!(output.status.success());
- }
-
- #[test]
- fn import_map_output_dir() {
- let _server = http_server();
- let t = TempDir::new();
- t.write("mod.ts", "");
- t.create_dir_all("vendor");
- t.write(
- "vendor/import_map.json",
- // will be ignored
- "{ \"imports\": { \"https://localhost:4545/\": \"./localhost/\" }}",
- );
- t.write(
- "deno.json",
- "{ \"import_map\": \"./vendor/import_map.json\" }",
- );
- t.write(
- "my_app.ts",
- "import {Logger} from 'http://localhost:4545/vendor/logger.ts'; new Logger().log('outputted');",
- );
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("--force")
- .arg("--import-map")
- .arg("vendor/import_map.json")
- .arg("my_app.ts")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- format!(
- concat!(
- "Ignoring import map. Specifying an import map file ({}) in the deno ",
- "vendor output directory is not supported. If you wish to use an ",
- "import map while vendoring, please specify one located outside this ",
- "directory.\n",
- "Download http://localhost:4545/vendor/logger.ts\n",
- "{}",
- ),
- PathBuf::from("vendor").join("import_map.json").display(),
- success_text_updated_deno_json("1 module", "vendor/"),
- )
- );
- assert!(output.status.success());
- }
-
- #[test]
- fn remote_module_test() {
- let _server = http_server();
- let t = TempDir::new();
- let vendor_dir = t.path().join("vendor");
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("http://localhost:4545/vendor/query_reexport.ts")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- format!(
- concat!(
- "Download http://localhost:4545/vendor/query_reexport.ts\n",
- "Download http://localhost:4545/vendor/logger.ts?test\n",
- "{}",
- ),
- success_text("2 modules", "vendor/", true),
- )
- );
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
- assert!(output.status.success());
- assert!(vendor_dir.exists());
- assert!(vendor_dir
- .join("localhost_4545/vendor/query_reexport.ts")
- .exists());
- assert!(vendor_dir.join("localhost_4545/vendor/logger.ts").exists());
- let import_map: serde_json::Value =
- serde_json::from_str(&t.read_to_string("vendor/import_map.json"))
- .unwrap();
- assert_eq!(
- import_map,
- json!({
- "imports": {
- "http://localhost:4545/": "./localhost_4545/",
- },
- "scopes": {
- "./localhost_4545/": {
- "./localhost_4545/vendor/logger.ts?test": "./localhost_4545/vendor/logger.ts",
- }
- }
- }),
- );
- }
-
- #[test]
- fn existing_import_map_no_remote() {
- let _server = http_server();
- let t = TempDir::new();
- t.write(
- "mod.ts",
- "import {Logger} from 'http://localhost:4545/vendor/logger.ts';",
- );
- let import_map_filename = "imports2.json";
- let import_map_text =
- r#"{ "imports": { "http://localhost:4545/vendor/": "./logger/" } }"#;
- t.write(import_map_filename, import_map_text);
- t.create_dir_all("logger");
- t.write("logger/logger.ts", "export class Logger {}");
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("mod.ts")
- .arg("--import-map")
- .arg(import_map_filename)
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- success_text("0 modules", "vendor/", false)
- );
- assert!(output.status.success());
- // it should not have found any remote dependencies because
- // the provided import map mapped it to a local directory
- assert_eq!(t.read_to_string(import_map_filename), import_map_text);
- }
-
- #[test]
- fn existing_import_map_mixed_with_remote() {
- let _server = http_server();
- let deno_dir = new_deno_dir();
- let t = TempDir::new();
- t.write(
- "mod.ts",
- "import {Logger} from 'http://localhost:4545/vendor/logger.ts';",
- );
-
- let status = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(t.path())
- .arg("vendor")
- .arg("mod.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
-
- assert_eq!(
- t.read_to_string("vendor/import_map.json"),
- r#"{
- "imports": {
- "http://localhost:4545/": "./localhost_4545/"
- }
-}
-"#,
- );
-
- // make the import map specific to support vendoring mod.ts in the next step
- t.write(
- "vendor/import_map.json",
- r#"{
- "imports": {
- "http://localhost:4545/vendor/logger.ts": "./localhost_4545/vendor/logger.ts"
- }
-}
-"#,
- );
-
- t.write(
- "mod.ts",
- concat!(
- "import {Logger} from 'http://localhost:4545/vendor/logger.ts';\n",
- "import {Logger as OtherLogger} from 'http://localhost:4545/vendor/mod.ts';\n",
- ),
- );
-
- // now vendor with the existing import map in a separate vendor directory
- let deno = util::deno_cmd_with_deno_dir(&deno_dir)
- .env("NO_COLOR", "1")
- .current_dir(t.path())
- .arg("vendor")
- .arg("mod.ts")
- .arg("--import-map")
- .arg("vendor/import_map.json")
- .arg("--output")
- .arg("vendor2")
- .stderr(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- format!(
- concat!("Download http://localhost:4545/vendor/mod.ts\n", "{}",),
- success_text("1 module", "vendor2", true),
- )
- );
- assert!(output.status.success());
-
- // tricky scenario here where the output directory now contains a mapping
- // back to the previous vendor location
- assert_eq!(
- t.read_to_string("vendor2/import_map.json"),
- r#"{
- "imports": {
- "http://localhost:4545/vendor/logger.ts": "../vendor/localhost_4545/vendor/logger.ts",
- "http://localhost:4545/": "./localhost_4545/"
- },
- "scopes": {
- "./localhost_4545/": {
- "./localhost_4545/vendor/logger.ts": "../vendor/localhost_4545/vendor/logger.ts"
- }
- }
-}
-"#,
- );
-
- // ensure it runs
- let status = util::deno_cmd()
- .current_dir(t.path())
- .arg("run")
- .arg("--check")
- .arg("--no-remote")
- .arg("--import-map")
- .arg("vendor2/import_map.json")
- .arg("mod.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- }
-
- #[test]
- fn dynamic_import() {
- let _server = http_server();
- let t = TempDir::new();
- t.write(
- "mod.ts",
- "import {Logger} from 'http://localhost:4545/vendor/dynamic.ts'; new Logger().log('outputted');",
- );
-
- let status = util::deno_cmd()
- .current_dir(t.path())
- .arg("vendor")
- .arg("mod.ts")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- assert!(status.success());
- let import_map: serde_json::Value =
- serde_json::from_str(&t.read_to_string("vendor/import_map.json"))
- .unwrap();
- assert_eq!(
- import_map,
- json!({
- "imports": {
- "http://localhost:4545/": "./localhost_4545/",
- }
- }),
- );
-
- // try running the output with `--no-remote`
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--allow-read=.")
- .arg("--no-remote")
- .arg("--check")
- .arg("--quiet")
- .arg("--import-map")
- .arg("vendor/import_map.json")
- .arg("mod.ts")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8_lossy(&output.stderr).trim(), "");
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "outputted");
- assert!(output.status.success());
- }
-
- #[test]
- fn dynamic_non_analyzable_import() {
- let _server = http_server();
- let t = TempDir::new();
- t.write(
- "mod.ts",
- "import {Logger} from 'http://localhost:4545/vendor/dynamic_non_analyzable.ts'; new Logger().log('outputted');",
- );
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("vendor")
- .arg("--reload")
- .arg("mod.ts")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- // todo(https://github.com/denoland/deno_graph/issues/138): it should warn about
- // how it couldn't analyze the dynamic import
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- format!(
- "Download http://localhost:4545/vendor/dynamic_non_analyzable.ts\n{}",
- success_text("1 module", "vendor/", true),
- )
- );
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
- assert!(output.status.success());
- }
-
- #[test]
- fn update_existing_config_test() {
- let _server = http_server();
- let t = TempDir::new();
- t.write(
- "my_app.ts",
- "import {Logger} from 'http://localhost:4545/vendor/logger.ts'; new Logger().log('outputted');",
- );
- t.write("deno.json", "{\n}");
-
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .arg("vendor")
- .arg("my_app.ts")
- .arg("--output")
- .arg("vendor2")
- .env("NO_COLOR", "1")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(
- String::from_utf8_lossy(&output.stderr).trim(),
- format!(
- "Download http://localhost:4545/vendor/logger.ts\n{}",
- success_text_updated_deno_json("1 module", "vendor2",)
- )
- );
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "");
- assert!(output.status.success());
-
- // try running the output with `--no-remote` and not specifying a `--vendor`
- let deno = util::deno_cmd()
- .current_dir(t.path())
- .env("NO_COLOR", "1")
- .arg("run")
- .arg("--no-remote")
- .arg("--check")
- .arg("--quiet")
- .arg("my_app.ts")
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert_eq!(String::from_utf8_lossy(&output.stderr).trim(), "");
- assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "outputted");
- assert!(output.status.success());
- }
-
- fn success_text(
- module_count: &str,
- dir: &str,
- has_import_map: bool,
- ) -> String {
- let mut text = format!("Vendored {} into {} directory.", module_count, dir);
- if has_import_map {
- let f = format!(
- concat!(
- "\n\nTo use vendored modules, specify the `--import-map {}import_map.json` flag when ",
- r#"invoking Deno subcommands or add an `"importMap": "<path_to_vendored_import_map>"` "#,
- "entry to a deno.json file.",
- ),
- if dir != "vendor/" {
- format!("{}{}", dir.trim_end_matches('/'), if cfg!(windows) { '\\' } else {'/'})
- } else {
- dir.to_string()
- }
- );
- write!(text, "{}", f).unwrap();
- }
- text
- }
-
- fn success_text_updated_deno_json(module_count: &str, dir: &str) -> String {
- format!(
- concat!(
- "Vendored {} into {} directory.\n\n",
- "Updated your local Deno configuration file with a reference to the ",
- "new vendored import map at {}import_map.json. Invoking Deno subcommands will ",
- "now automatically resolve using the vendored modules. You may override ",
- "this by providing the `--import-map <other-import-map>` flag or by ",
- "manually editing your Deno configuration file.",
- ),
- module_count,
- dir,
- if dir != "vendor/" {
- format!(
- "{}{}",
- dir.trim_end_matches('/'),
- if cfg!(windows) { '\\' } else { '/' }
- )
- } else {
- dir.to_string()
- }
- )
- }
-}
diff --git a/cli/tests/watcher_tests.rs b/cli/tests/watcher_tests.rs
deleted file mode 100644
index a300f3953..000000000
--- a/cli/tests/watcher_tests.rs
+++ /dev/null
@@ -1,1230 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use flaky_test::flaky_test;
-use std::fs::write;
-use std::io::BufRead;
-use test_util as util;
-use test_util::assert_contains;
-use test_util::TempDir;
-
-mod watcher {
- use util::assert_not_contains;
-
- use super::*;
-
- const CLEAR_SCREEN: &str = r#"[2J"#;
-
- // Helper function to skip watcher output that contains "Restarting"
- // phrase.
- fn skip_restarting_line(
- stderr_lines: &mut impl Iterator<Item = String>,
- ) -> String {
- loop {
- let msg = stderr_lines.next().unwrap();
- if !msg.contains("Restarting") {
- return msg;
- }
- }
- }
-
- fn read_all_lints(stderr_lines: &mut impl Iterator<Item = String>) -> String {
- let mut str = String::new();
- for t in stderr_lines {
- let t = util::strip_ansi_codes(&t);
- if t.starts_with("Watcher File change detected") {
- continue;
- }
- if t.starts_with("Watcher") {
- break;
- }
- if t.starts_with('(') {
- str.push_str(&t);
- str.push('\n');
- }
- }
- str
- }
-
- fn wait_for(
- condition: impl Fn(&str) -> bool,
- lines: &mut impl Iterator<Item = String>,
- ) {
- loop {
- let msg = lines.next().unwrap();
- if condition(&msg) {
- break;
- }
- }
- }
-
- fn wait_contains(s: &str, lines: &mut impl Iterator<Item = String>) {
- wait_for(|msg| msg.contains(s), lines)
- }
-
- fn read_line(s: &str, lines: &mut impl Iterator<Item = String>) -> String {
- lines.find(|m| m.contains(s)).unwrap()
- }
-
- fn check_alive_then_kill(mut child: std::process::Child) {
- assert!(child.try_wait().unwrap().is_none());
- child.kill().unwrap();
- }
-
- fn child_lines(
- child: &mut std::process::Child,
- ) -> (impl Iterator<Item = String>, impl Iterator<Item = String>) {
- let stdout_lines = std::io::BufReader::new(child.stdout.take().unwrap())
- .lines()
- .map(|r| {
- let line = r.unwrap();
- eprintln!("STDOUT: {}", line);
- line
- });
- let stderr_lines = std::io::BufReader::new(child.stderr.take().unwrap())
- .lines()
- .map(|r| {
- let line = r.unwrap();
- eprintln!("STDERR: {}", line);
- line
- });
- (stdout_lines, stderr_lines)
- }
-
- #[test]
- fn lint_watch_test() {
- let t = TempDir::new();
- let badly_linted_original =
- util::testdata_path().join("lint/watch/badly_linted.js");
- let badly_linted_output =
- util::testdata_path().join("lint/watch/badly_linted.js.out");
- let badly_linted_fixed1 =
- util::testdata_path().join("lint/watch/badly_linted_fixed1.js");
- let badly_linted_fixed1_output =
- util::testdata_path().join("lint/watch/badly_linted_fixed1.js.out");
- let badly_linted_fixed2 =
- util::testdata_path().join("lint/watch/badly_linted_fixed2.js");
- let badly_linted_fixed2_output =
- util::testdata_path().join("lint/watch/badly_linted_fixed2.js.out");
- let badly_linted = t.path().join("badly_linted.js");
-
- std::fs::copy(badly_linted_original, &badly_linted).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("lint")
- .arg(&badly_linted)
- .arg("--watch")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Lint started");
- let mut output = read_all_lints(&mut stderr_lines);
- let expected = std::fs::read_to_string(badly_linted_output).unwrap();
- assert_eq!(output, expected);
-
- // Change content of the file again to be badly-linted
- std::fs::copy(badly_linted_fixed1, &badly_linted).unwrap();
- std::thread::sleep(std::time::Duration::from_secs(1));
-
- output = read_all_lints(&mut stderr_lines);
- let expected = std::fs::read_to_string(badly_linted_fixed1_output).unwrap();
- assert_eq!(output, expected);
-
- // Change content of the file again to be badly-linted
- std::fs::copy(badly_linted_fixed2, &badly_linted).unwrap();
-
- output = read_all_lints(&mut stderr_lines);
- let expected = std::fs::read_to_string(badly_linted_fixed2_output).unwrap();
- assert_eq!(output, expected);
-
- // the watcher process is still alive
- assert!(child.try_wait().unwrap().is_none());
-
- child.kill().unwrap();
- drop(t);
- }
-
- #[test]
- fn lint_watch_without_args_test() {
- let t = TempDir::new();
- let badly_linted_original =
- util::testdata_path().join("lint/watch/badly_linted.js");
- let badly_linted_output =
- util::testdata_path().join("lint/watch/badly_linted.js.out");
- let badly_linted_fixed1 =
- util::testdata_path().join("lint/watch/badly_linted_fixed1.js");
- let badly_linted_fixed1_output =
- util::testdata_path().join("lint/watch/badly_linted_fixed1.js.out");
- let badly_linted_fixed2 =
- util::testdata_path().join("lint/watch/badly_linted_fixed2.js");
- let badly_linted_fixed2_output =
- util::testdata_path().join("lint/watch/badly_linted_fixed2.js.out");
- let badly_linted = t.path().join("badly_linted.js");
-
- std::fs::copy(badly_linted_original, &badly_linted).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(t.path())
- .arg("lint")
- .arg("--watch")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Lint started");
- let mut output = read_all_lints(&mut stderr_lines);
- let expected = std::fs::read_to_string(badly_linted_output).unwrap();
- assert_eq!(output, expected);
-
- // Change content of the file again to be badly-linted
- std::fs::copy(badly_linted_fixed1, &badly_linted).unwrap();
-
- output = read_all_lints(&mut stderr_lines);
- let expected = std::fs::read_to_string(badly_linted_fixed1_output).unwrap();
- assert_eq!(output, expected);
-
- // Change content of the file again to be badly-linted
- std::fs::copy(badly_linted_fixed2, &badly_linted).unwrap();
- std::thread::sleep(std::time::Duration::from_secs(1));
-
- output = read_all_lints(&mut stderr_lines);
- let expected = std::fs::read_to_string(badly_linted_fixed2_output).unwrap();
- assert_eq!(output, expected);
-
- // the watcher process is still alive
- assert!(child.try_wait().unwrap().is_none());
-
- child.kill().unwrap();
- drop(t);
- }
-
- #[test]
- fn lint_all_files_on_each_change_test() {
- let t = TempDir::new();
- let badly_linted_fixed0 =
- util::testdata_path().join("lint/watch/badly_linted.js");
- let badly_linted_fixed1 =
- util::testdata_path().join("lint/watch/badly_linted_fixed1.js");
- let badly_linted_fixed2 =
- util::testdata_path().join("lint/watch/badly_linted_fixed2.js");
-
- let badly_linted_1 = t.path().join("badly_linted_1.js");
- let badly_linted_2 = t.path().join("badly_linted_2.js");
- std::fs::copy(badly_linted_fixed0, badly_linted_1).unwrap();
- std::fs::copy(badly_linted_fixed1, &badly_linted_2).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("lint")
- .arg(t.path())
- .arg("--watch")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- assert_contains!(
- read_line("Checked", &mut stderr_lines),
- "Checked 2 files"
- );
-
- std::fs::copy(badly_linted_fixed2, badly_linted_2).unwrap();
-
- assert_contains!(
- read_line("Checked", &mut stderr_lines),
- "Checked 2 files"
- );
-
- assert!(child.try_wait().unwrap().is_none());
-
- child.kill().unwrap();
- drop(t);
- }
-
- #[test]
- fn fmt_watch_test() {
- let fmt_testdata_path = util::testdata_path().join("fmt");
- let t = TempDir::new();
- let fixed = fmt_testdata_path.join("badly_formatted_fixed.js");
- let badly_formatted_original =
- fmt_testdata_path.join("badly_formatted.mjs");
- let badly_formatted = t.path().join("badly_formatted.js");
- std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(&fmt_testdata_path)
- .arg("fmt")
- .arg(&badly_formatted)
- .arg("--watch")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Fmt started");
- assert_contains!(
- skip_restarting_line(&mut stderr_lines),
- "badly_formatted.js"
- );
- assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
-
- let expected = std::fs::read_to_string(fixed.clone()).unwrap();
- let actual = std::fs::read_to_string(badly_formatted.clone()).unwrap();
- assert_eq!(actual, expected);
-
- // Change content of the file again to be badly formatted
- std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
-
- assert_contains!(
- skip_restarting_line(&mut stderr_lines),
- "badly_formatted.js"
- );
- assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
-
- // Check if file has been automatically formatted by watcher
- let expected = std::fs::read_to_string(fixed).unwrap();
- let actual = std::fs::read_to_string(badly_formatted).unwrap();
- assert_eq!(actual, expected);
- check_alive_then_kill(child);
- }
-
- #[test]
- fn fmt_watch_without_args_test() {
- let fmt_testdata_path = util::testdata_path().join("fmt");
- let t = TempDir::new();
- let fixed = fmt_testdata_path.join("badly_formatted_fixed.js");
- let badly_formatted_original =
- fmt_testdata_path.join("badly_formatted.mjs");
- let badly_formatted = t.path().join("badly_formatted.js");
- std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(t.path())
- .arg("fmt")
- .arg("--watch")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Fmt started");
- assert_contains!(
- skip_restarting_line(&mut stderr_lines),
- "badly_formatted.js"
- );
- assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
-
- let expected = std::fs::read_to_string(fixed.clone()).unwrap();
- let actual = std::fs::read_to_string(badly_formatted.clone()).unwrap();
- assert_eq!(actual, expected);
-
- // Change content of the file again to be badly formatted
- std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
- assert_contains!(
- skip_restarting_line(&mut stderr_lines),
- "badly_formatted.js"
- );
- assert_contains!(read_line("Checked", &mut stderr_lines), "Checked 1 file");
-
- // Check if file has been automatically formatted by watcher
- let expected = std::fs::read_to_string(fixed).unwrap();
- let actual = std::fs::read_to_string(badly_formatted).unwrap();
- assert_eq!(actual, expected);
- check_alive_then_kill(child);
- }
-
- #[test]
- fn fmt_check_all_files_on_each_change_test() {
- let t = TempDir::new();
- let fmt_testdata_path = util::testdata_path().join("fmt");
- let badly_formatted_original =
- fmt_testdata_path.join("badly_formatted.mjs");
- let badly_formatted_1 = t.path().join("badly_formatted_1.js");
- let badly_formatted_2 = t.path().join("badly_formatted_2.js");
- std::fs::copy(&badly_formatted_original, &badly_formatted_1).unwrap();
- std::fs::copy(&badly_formatted_original, badly_formatted_2).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(&fmt_testdata_path)
- .arg("fmt")
- .arg(t.path())
- .arg("--watch")
- .arg("--check")
- .arg("--unstable")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- assert_contains!(
- read_line("error", &mut stderr_lines),
- "Found 2 not formatted files in 2 files"
- );
-
- // Change content of the file again to be badly formatted
- std::fs::copy(&badly_formatted_original, &badly_formatted_1).unwrap();
-
- assert_contains!(
- read_line("error", &mut stderr_lines),
- "Found 2 not formatted files in 2 files"
- );
-
- check_alive_then_kill(child);
- }
-
- #[test]
- fn bundle_js_watch() {
- use std::path::PathBuf;
- // Test strategy extends this of test bundle_js by adding watcher
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.ts");
- write(&file_to_watch, "console.log('Hello world');").unwrap();
- assert!(file_to_watch.is_file());
- let t = TempDir::new();
- let bundle = t.path().join("mod6.bundle.js");
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(&file_to_watch)
- .arg(&bundle)
- .arg("--watch")
- .arg("--unstable")
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
-
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
-
- assert_contains!(stderr_lines.next().unwrap(), "Check");
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Bundle started");
- assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
- assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
- let file = PathBuf::from(&bundle);
- assert!(file.is_file());
- wait_contains("Bundle finished", &mut stderr_lines);
-
- write(&file_to_watch, "console.log('Hello world2');").unwrap();
-
- assert_contains!(stderr_lines.next().unwrap(), "Check");
- let next_line = stderr_lines.next().unwrap();
- // Should not clear screen, as we are in non-TTY environment
- assert_not_contains!(&next_line, CLEAR_SCREEN);
- assert_contains!(&next_line, "File change detected!");
- assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
- assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
- let file = PathBuf::from(&bundle);
- assert!(file.is_file());
- wait_contains("Bundle finished", &mut stderr_lines);
-
- // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
- write(&file_to_watch, "syntax error ^^").unwrap();
-
- assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
- assert_contains!(stderr_lines.next().unwrap(), "error: ");
- wait_contains("Bundle failed", &mut stderr_lines);
- check_alive_then_kill(deno);
- }
-
- /// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt
- #[test]
- fn bundle_watch_not_exit() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.ts");
- write(&file_to_watch, "syntax error ^^").unwrap();
- let target_file = t.path().join("target.js");
-
- let mut deno = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("bundle")
- .arg(&file_to_watch)
- .arg(&target_file)
- .arg("--watch")
- .arg("--unstable")
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
-
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Bundle started");
- assert_contains!(stderr_lines.next().unwrap(), "error:");
- assert_eq!(stderr_lines.next().unwrap(), "");
- assert_eq!(stderr_lines.next().unwrap(), " syntax error ^^");
- assert_eq!(stderr_lines.next().unwrap(), " ~~~~~");
- assert_contains!(stderr_lines.next().unwrap(), "Bundle failed");
- // the target file hasn't been created yet
- assert!(!target_file.is_file());
-
- // Make sure the watcher actually restarts and works fine with the proper syntax
- write(&file_to_watch, "console.log(42);").unwrap();
-
- assert_contains!(stderr_lines.next().unwrap(), "Check");
- let next_line = stderr_lines.next().unwrap();
- // Should not clear screen, as we are in non-TTY environment
- assert_not_contains!(&next_line, CLEAR_SCREEN);
- assert_contains!(&next_line, "File change detected!");
- assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.ts");
- assert_contains!(stderr_lines.next().unwrap(), "target.js");
-
- wait_contains("Bundle finished", &mut stderr_lines);
-
- // bundled file is created
- assert!(target_file.is_file());
- check_alive_then_kill(deno);
- }
-
- #[test]
- fn run_watch_no_dynamic() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(&file_to_watch, "console.log('Hello world');").unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg("--unstable")
- .arg("-L")
- .arg("debug")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- wait_contains("Hello world", &mut stdout_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
- &mut stderr_lines,
- );
-
- // Change content of the file
- write(&file_to_watch, "console.log('Hello world2');").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("Hello world2", &mut stdout_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
- &mut stderr_lines,
- );
-
- // Add dependency
- let another_file = t.path().join("another_file.js");
- write(&another_file, "export const foo = 0;").unwrap();
- write(
- &file_to_watch,
- "import { foo } from './another_file.js'; console.log(foo);",
- )
- .unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("0", &mut stdout_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("another_file.js"),
- &mut stderr_lines,
- );
-
- // Confirm that restarting occurs when a new file is updated
- write(&another_file, "export const foo = 42;").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("42", &mut stdout_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
- &mut stderr_lines,
- );
-
- // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
- write(&file_to_watch, "syntax error ^^").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("error:", &mut stderr_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
- &mut stderr_lines,
- );
-
- // Then restore the file
- write(
- &file_to_watch,
- "import { foo } from './another_file.js'; console.log(foo);",
- )
- .unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("42", &mut stdout_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("another_file.js"),
- &mut stderr_lines,
- );
-
- // Update the content of the imported file with invalid syntax
- write(&another_file, "syntax error ^^").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("error:", &mut stderr_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("another_file.js"),
- &mut stderr_lines,
- );
-
- // Modify the imported file and make sure that restarting occurs
- write(&another_file, "export const foo = 'modified!';").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("modified!", &mut stdout_lines);
- wait_contains("Watching paths", &mut stderr_lines);
- check_alive_then_kill(child);
- }
-
- // TODO(bartlomieju): this test became flaky on macOS runner; it is unclear
- // if that's because of a bug in code or the runner itself. We should reenable
- // it once we upgrade to XL runners for macOS.
- #[cfg(not(target_os = "macos"))]
- #[test]
- fn run_watch_external_watch_files() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(&file_to_watch, "console.log('Hello world');").unwrap();
-
- let external_file_to_watch = t.path().join("external_file_to_watch.txt");
- write(&external_file_to_watch, "Hello world").unwrap();
-
- let mut watch_arg = "--watch=".to_owned();
- let external_file_to_watch_str = external_file_to_watch
- .clone()
- .into_os_string()
- .into_string()
- .unwrap();
- watch_arg.push_str(&external_file_to_watch_str);
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg(watch_arg)
- .arg("-L")
- .arg("debug")
- .arg("--unstable")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
- wait_contains("Process started", &mut stderr_lines);
- wait_contains("Hello world", &mut stdout_lines);
- wait_for(
- |m| {
- m.contains("Watching paths") && m.contains("external_file_to_watch.txt")
- },
- &mut stderr_lines,
- );
-
- // Change content of the external file
- write(&external_file_to_watch, "Hello world2").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("Process finished", &mut stderr_lines);
- check_alive_then_kill(child);
- }
-
- #[test]
- fn run_watch_load_unload_events() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(
- &file_to_watch,
- r#"
- setInterval(() => {}, 0);
- window.addEventListener("load", () => {
- console.log("load");
- });
-
- window.addEventListener("unload", () => {
- console.log("unload");
- });
- "#,
- )
- .unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg("--unstable")
- .arg("-L")
- .arg("debug")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- // Wait for the first load event to fire
- wait_contains("load", &mut stdout_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
- &mut stderr_lines,
- );
-
- // Change content of the file, this time without an interval to keep it alive.
- write(
- &file_to_watch,
- r#"
- window.addEventListener("load", () => {
- console.log("load");
- });
-
- window.addEventListener("unload", () => {
- console.log("unload");
- });
- "#,
- )
- .unwrap();
-
- // Wait for the restart
- wait_contains("Restarting", &mut stderr_lines);
-
- // Confirm that the unload event was dispatched from the first run
- wait_contains("unload", &mut stdout_lines);
-
- // Followed by the load event of the second run
- wait_contains("load", &mut stdout_lines);
-
- // Which is then unloaded as there is nothing keeping it alive.
- wait_contains("unload", &mut stdout_lines);
- check_alive_then_kill(child);
- }
-
- /// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt
- #[test]
- fn run_watch_not_exit() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(&file_to_watch, "syntax error ^^").unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg("--unstable")
- .arg("-L")
- .arg("debug")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- wait_contains("Process started", &mut stderr_lines);
- wait_contains("error:", &mut stderr_lines);
- wait_for(
- |m| m.contains("Watching paths") && m.contains("file_to_watch.js"),
- &mut stderr_lines,
- );
-
- // Make sure the watcher actually restarts and works fine with the proper syntax
- write(&file_to_watch, "console.log(42);").unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains("42", &mut stdout_lines);
- wait_contains("Process finished", &mut stderr_lines);
- check_alive_then_kill(child);
- }
-
- #[test]
- fn run_watch_with_import_map_and_relative_paths() {
- fn create_relative_tmp_file(
- directory: &TempDir,
- filename: &'static str,
- filecontent: &'static str,
- ) -> std::path::PathBuf {
- let absolute_path = directory.path().join(filename);
- write(&absolute_path, filecontent).unwrap();
- let relative_path = absolute_path
- .strip_prefix(util::testdata_path())
- .unwrap()
- .to_owned();
- assert!(relative_path.is_relative());
- relative_path
- }
- let temp_directory = TempDir::new_in(&util::testdata_path());
- let file_to_watch = create_relative_tmp_file(
- &temp_directory,
- "file_to_watch.js",
- "console.log('Hello world');",
- );
- let import_map_path = create_relative_tmp_file(
- &temp_directory,
- "import_map.json",
- "{\"imports\": {}}",
- );
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--unstable")
- .arg("--watch")
- .arg("--import-map")
- .arg(&import_map_path)
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
- let next_line = stderr_lines.next().unwrap();
- assert_contains!(&next_line, "Process started");
- assert_contains!(stderr_lines.next().unwrap(), "Process finished");
- assert_contains!(stdout_lines.next().unwrap(), "Hello world");
-
- check_alive_then_kill(child);
- }
-
- #[test]
- fn run_watch_error_messages() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(
- &file_to_watch,
- "throw SyntaxError(`outer`, {cause: TypeError(`inner`)})",
- )
- .unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_, mut stderr_lines) = child_lines(&mut child);
-
- wait_contains("Process started", &mut stderr_lines);
- wait_contains("error: Uncaught SyntaxError: outer", &mut stderr_lines);
- wait_contains("Caused by: TypeError: inner", &mut stderr_lines);
- wait_contains("Process finished", &mut stderr_lines);
-
- check_alive_then_kill(child);
- }
-
- #[test]
- fn test_watch() {
- let t = TempDir::new();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("--watch")
- .arg("--unstable")
- .arg("--no-check")
- .arg(t.path())
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- assert_eq!(stdout_lines.next().unwrap(), "");
- assert_contains!(stdout_lines.next().unwrap(), "0 passed | 0 failed");
- wait_contains("Test finished", &mut stderr_lines);
-
- let foo_file = t.path().join("foo.js");
- let bar_file = t.path().join("bar.js");
- let foo_test = t.path().join("foo_test.js");
- let bar_test = t.path().join("bar_test.js");
- write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
- write(&bar_file, "export default function bar() { 2 + 2 }").unwrap();
- write(
- &foo_test,
- "import foo from './foo.js'; Deno.test('foo', foo);",
- )
- .unwrap();
- write(
- bar_test,
- "import bar from './bar.js'; Deno.test('bar', bar);",
- )
- .unwrap();
-
- assert_eq!(stdout_lines.next().unwrap(), "");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "foo", "bar");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "foo", "bar");
- stdout_lines.next();
- stdout_lines.next();
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Change content of the file
- write(
- &foo_test,
- "import foo from './foo.js'; Deno.test('foobar', foo);",
- )
- .unwrap();
-
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "foobar");
- stdout_lines.next();
- stdout_lines.next();
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Add test
- let another_test = t.path().join("new_test.js");
- write(&another_test, "Deno.test('another one', () => 3 + 3)").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "another one");
- stdout_lines.next();
- stdout_lines.next();
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Confirm that restarting occurs when a new file is updated
- write(&another_test, "Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)")
- .unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "running 2 tests");
- assert_contains!(stdout_lines.next().unwrap(), "another one");
- assert_contains!(stdout_lines.next().unwrap(), "another another one");
- stdout_lines.next();
- stdout_lines.next();
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
- write(&another_test, "syntax error ^^").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stderr_lines.next().unwrap(), "error:");
- assert_eq!(stderr_lines.next().unwrap(), "");
- assert_eq!(stderr_lines.next().unwrap(), " syntax error ^^");
- assert_eq!(stderr_lines.next().unwrap(), " ~~~~~");
- assert_contains!(stderr_lines.next().unwrap(), "Test failed");
-
- // Then restore the file
- write(&another_test, "Deno.test('another one', () => 3 + 3)").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "another one");
- stdout_lines.next();
- stdout_lines.next();
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Confirm that the watcher keeps on working even if the file is updated and the test fails
- // This also confirms that it restarts when dependencies change
- write(
- &foo_file,
- "export default function foo() { throw new Error('Whoops!'); }",
- )
- .unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "FAILED");
- wait_for(|m| m.contains("FAILED"), &mut stdout_lines);
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Then restore the file
- write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
- assert_contains!(stderr_lines.next().unwrap(), "Restarting");
- assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
- assert_contains!(stdout_lines.next().unwrap(), "foo");
- stdout_lines.next();
- stdout_lines.next();
- stdout_lines.next();
- wait_contains("Test finished", &mut stderr_lines);
-
- // Test that circular dependencies work fine
- write(
- &foo_file,
- "import './bar.js'; export default function foo() { 1 + 1 }",
- )
- .unwrap();
- write(
- &bar_file,
- "import './foo.js'; export default function bar() { 2 + 2 }",
- )
- .unwrap();
- check_alive_then_kill(child);
- }
-
- #[flaky_test]
- fn test_watch_doc() {
- let t = TempDir::new();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("test")
- .arg("--watch")
- .arg("--doc")
- .arg("--unstable")
- .arg(t.path())
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- assert_eq!(stdout_lines.next().unwrap(), "");
- assert_contains!(stdout_lines.next().unwrap(), "0 passed | 0 failed");
- wait_contains("Test finished", &mut stderr_lines);
-
- let foo_file = t.path().join("foo.ts");
- write(
- &foo_file,
- r#"
- export default function foo() {}
- "#,
- )
- .unwrap();
-
- write(
- &foo_file,
- r#"
- /**
- * ```ts
- * import foo from "./foo.ts";
- * ```
- */
- export default function foo() {}
- "#,
- )
- .unwrap();
-
- // We only need to scan for a Check file://.../foo.ts$3-6 line that
- // corresponds to the documentation block being type-checked.
- assert_contains!(skip_restarting_line(&mut stderr_lines), "foo.ts$3-6");
- check_alive_then_kill(child);
- }
-
- #[test]
- fn test_watch_module_graph_error_referrer() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(&file_to_watch, "import './nonexistent.js';").unwrap();
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg("--unstable")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_, mut stderr_lines) = child_lines(&mut child);
- let line1 = stderr_lines.next().unwrap();
- assert_contains!(&line1, "Process started");
- let line2 = stderr_lines.next().unwrap();
- assert_contains!(&line2, "error: Module not found");
- assert_contains!(&line2, "nonexistent.js");
- let line3 = stderr_lines.next().unwrap();
- assert_contains!(&line3, " at ");
- assert_contains!(&line3, "file_to_watch.js");
- wait_contains("Process finished", &mut stderr_lines);
- check_alive_then_kill(child);
- }
-
- // Regression test for https://github.com/denoland/deno/issues/15428.
- #[test]
- fn test_watch_unload_handler_error_on_drop() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(
- &file_to_watch,
- r#"
- addEventListener("unload", () => {
- throw new Error("foo");
- });
- setTimeout(() => {
- throw new Error("bar");
- });
- "#,
- )
- .unwrap();
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (_, mut stderr_lines) = child_lines(&mut child);
- wait_contains("Process started", &mut stderr_lines);
- wait_contains("Uncaught Error: bar", &mut stderr_lines);
- wait_contains("Process finished", &mut stderr_lines);
- check_alive_then_kill(child);
- }
-
- // Regression test for https://github.com/denoland/deno/issues/15465.
- #[test]
- fn run_watch_reload_once() {
- let _g = util::http_server();
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- let file_content = r#"
- import { time } from "http://localhost:4545/dynamic_module.ts";
- console.log(time);
- "#;
- write(&file_to_watch, file_content).unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg("--reload")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- wait_contains("finished", &mut stderr_lines);
- let first_output = stdout_lines.next().unwrap();
-
- write(&file_to_watch, file_content).unwrap();
- // The remote dynamic module should not have been reloaded again.
-
- wait_contains("finished", &mut stderr_lines);
- let second_output = stdout_lines.next().unwrap();
- assert_eq!(second_output, first_output);
-
- check_alive_then_kill(child);
- }
-
- #[test]
- fn run_watch_dynamic_imports() {
- let t = TempDir::new();
- let file_to_watch = t.path().join("file_to_watch.js");
- write(
- &file_to_watch,
- r#"
- console.log("Hopefully dynamic import will be watched...");
- await import("./imported.js");
- "#,
- )
- .unwrap();
- let file_to_watch2 = t.path().join("imported.js");
- write(
- file_to_watch2,
- r#"
- import "./imported2.js";
- console.log("I'm dynamically imported and I cause restarts!");
- "#,
- )
- .unwrap();
- let file_to_watch3 = t.path().join("imported2.js");
- write(
- &file_to_watch3,
- r#"
- console.log("I'm statically imported from the dynamic import");
- "#,
- )
- .unwrap();
-
- let mut child = util::deno_cmd()
- .current_dir(util::testdata_path())
- .arg("run")
- .arg("--watch")
- .arg("--unstable")
- .arg("--allow-read")
- .arg("-L")
- .arg("debug")
- .arg(&file_to_watch)
- .env("NO_COLOR", "1")
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::piped())
- .spawn()
- .unwrap();
- let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
-
- assert_contains!(stderr_lines.next().unwrap(), "Process started");
-
- wait_contains(
- "Hopefully dynamic import will be watched...",
- &mut stdout_lines,
- );
- wait_contains(
- "I'm statically imported from the dynamic import",
- &mut stdout_lines,
- );
- wait_contains(
- "I'm dynamically imported and I cause restarts!",
- &mut stdout_lines,
- );
-
- wait_for(
- |m| m.contains("Watching paths") && m.contains("imported2.js"),
- &mut stderr_lines,
- );
- wait_contains("finished", &mut stderr_lines);
-
- write(
- &file_to_watch3,
- r#"
- console.log("I'm statically imported from the dynamic import and I've changed");
- "#,
- )
- .unwrap();
-
- wait_contains("Restarting", &mut stderr_lines);
- wait_contains(
- "Hopefully dynamic import will be watched...",
- &mut stdout_lines,
- );
- wait_contains(
- "I'm statically imported from the dynamic import and I've changed",
- &mut stdout_lines,
- );
- wait_contains(
- "I'm dynamically imported and I cause restarts!",
- &mut stdout_lines,
- );
-
- check_alive_then_kill(child);
- }
-}
diff --git a/cli/tests/worker_tests.rs b/cli/tests/worker_tests.rs
deleted file mode 100644
index fa60e53c4..000000000
--- a/cli/tests/worker_tests.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod integration;
-
-mod worker {
- use super::*;
-
- itest!(workers {
- args: "test --reload --location http://127.0.0.1:4545/ -A --unstable workers/test.ts",
- output: "workers/test.ts.out",
- http_server: true,
- });
-
- itest!(worker_error {
- args: "run -A workers/worker_error.ts",
- output: "workers/worker_error.ts.out",
- exit_code: 1,
- });
-
- itest!(worker_nested_error {
- args: "run -A workers/worker_nested_error.ts",
- output: "workers/worker_nested_error.ts.out",
- exit_code: 1,
- });
-
- itest!(worker_async_error {
- args: "run -A --quiet --reload workers/worker_async_error.ts",
- output: "workers/worker_async_error.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_message_handler_error {
- args: "run -A --quiet --reload workers/worker_message_handler_error.ts",
- output: "workers/worker_message_handler_error.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(nonexistent_worker {
- args: "run --allow-read workers/nonexistent_worker.ts",
- output: "workers/nonexistent_worker.out",
- exit_code: 1,
- });
-
- itest!(_084_worker_custom_inspect {
- args: "run --allow-read workers/custom_inspect/main.ts",
- output: "workers/custom_inspect/main.out",
- });
-
- itest!(error_worker_permissions_local {
- args: "run --reload workers/error_worker_permissions_local.ts",
- output: "workers/error_worker_permissions_local.ts.out",
- exit_code: 1,
- });
-
- itest!(error_worker_permissions_remote {
- args: "run --reload workers/error_worker_permissions_remote.ts",
- http_server: true,
- output: "workers/error_worker_permissions_remote.ts.out",
- exit_code: 1,
- });
-
- itest!(worker_permissions_remote_remote {
- args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_remote_remote.ts",
- output: "workers/permissions_remote_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_permissions_dynamic_remote {
- args: "run --quiet --reload --allow-net --unstable workers/permissions_dynamic_remote.ts",
- output: "workers/permissions_dynamic_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_permissions_data_remote {
- args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_data_remote.ts",
- output: "workers/permissions_data_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_permissions_blob_remote {
- args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_blob_remote.ts",
- output: "workers/permissions_blob_remote.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_permissions_data_local {
- args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_data_local.ts",
- output: "workers/permissions_data_local.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_permissions_blob_local {
- args: "run --quiet --reload --allow-net=localhost:4545 workers/permissions_blob_local.ts",
- output: "workers/permissions_blob_local.ts.out",
- http_server: true,
- exit_code: 1,
- });
-
- itest!(worker_terminate_tla_crash {
- args: "run --quiet --reload workers/terminate_tla_crash.js",
- output: "workers/terminate_tla_crash.js.out",
- });
-
- itest!(worker_error_event {
- args: "run --quiet -A workers/error_event.ts",
- output: "workers/error_event.ts.out",
- exit_code: 1,
- });
-}